diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..32c297c Binary files /dev/null and b/.DS_Store differ diff --git a/Battleships/.DS_Store b/Battleships/.DS_Store new file mode 100644 index 0000000..465823b Binary files /dev/null and b/Battleships/.DS_Store differ diff --git a/Battleships/Lesson1.html b/Battleships/Lesson1.html new file mode 100644 index 0000000..cb25f34 --- /dev/null +++ b/Battleships/Lesson1.html @@ -0,0 +1,121 @@ + + + + + + Lesson 1: Battleships in Python! + + + +

๐ŸŽฎ Lesson 1: Find the Hidden Ship!

+ +
+

โœ… Learning Outcomes

+ +
+ +
+

๐Ÿ’ก Why It Matters

+

Every video game has hidden logic โ€” secret levels, random enemies, or treasure locations. Learning to hide and reveal things with code is the first step to making your own games!

+

These same skills are used in apps, quizzes, and even smart home devices!

+
+ +

โฑ๏ธ Lesson Plan (40 minutes)

+ +
+ 0โ€“5 min โ†’ Demo & Explain
+ Show the game: โ€œThereโ€™s a secret ship! Can you find it?โ€
+ Explain: Weโ€™ll write code that hides a ship and checks your guess. +
+ +
+ 5โ€“20 min โ†’ Code Together
+ Type this starter code (or use your template): +
+import random
+
+ship_row = random.randint(0, 4)
+ship_col = random.randint(0, 4)
+
+print("Guess the ship!")
+guess_row = int(input("Row (0-4): "))
+guess_col = int(input("Col (0-4): "))
+
+if guess_row == ship_row and guess_col == ship_col:
+    print("๐ŸŽฏ HIT! You sank the ship!")
+else:
+    print("๐Ÿ’ฆ MISS!")
+
+print("The ship was at", ship_row, ship_col)
+    
+
+ +
+ 20โ€“35 min โ†’ Test & Improve
+ โ€ข Run the program 3 times
+ โ€ข Try to break it (type a letter instead of number โ€” what happens?)
+ โ€ข ๐ŸŒŸ Challenge: Add a 2nd ship or limit to 3 guesses! +
+ +
+ 35โ€“40 min โ†’ Share & Celebrate
+ Pair up! Can your partner guess the ship in 2 tries? +
+ +

๐Ÿš€ You Just Learned:

+

How to create interactive programs that respond to user choices โ€” the heart of all games!

+ + \ No newline at end of file diff --git a/Battleships/Lesson2.html b/Battleships/Lesson2.html new file mode 100644 index 0000000..13b9103 --- /dev/null +++ b/Battleships/Lesson2.html @@ -0,0 +1,138 @@ + + + + + + Lesson 2: Hunt All the Ships! + + + +

๐Ÿšข Lesson 2: Hunt All the Ships!

+ +
+

โœ… Learning Outcomes

+ +
+ +
+

๐Ÿ’ก Why It Matters

+

Real games donโ€™t end after one guess! They track score, lives, and progress. Learning to manage game state is how you build Pac-Man, Minecraft, or Roblox games!

+

Lists and loops are used in every programming language โ€” from apps to robots.

+
+ +

โฑ๏ธ Lesson Plan (40 minutes)

+ +
+ 0โ€“5 min โ†’ Review & Goal
+ โ€œLast time: 1 ship. Today: 3 ships and 10 turns!โ€
+ Show the enhanced game in action. +
+ +
+ 5โ€“20 min โ†’ Upgrade Your Code
+ Edit your Lesson 1 file to this: +
+import random
+
+ships = []
+while len(ships) < 3:
+    r = random.randint(0, 4)
+    c = random.randint(0, 4)
+    if [r, c] not in ships:
+        ships.append([r, c])
+
+print("3 ships hidden! 10 turns to find them all.")
+hits = 0
+
+for turn in range(10):
+    print("\nTurn", turn + 1)
+    guess_row = int(input("Row (0-4): "))
+    guess_col = int(input("Col (0-4): "))
+    
+    if [guess_row, guess_col] in ships:
+        print("๐ŸŽฏ HIT!")
+        ships.remove([guess_row, guess_col])
+        hits += 1
+        if hits == 3:
+            print("๐Ÿ† You win!")
+            break
+    else:
+        print("๐Ÿ’ฆ MISS!")
+
+if hits < 3:
+    print("Game over! You found", hits, "ships.")
+    
+
+ +
+ 20โ€“35 min โ†’ Level Up!
+ โ€ข Test the game (try to win!)
+ โ€ข ๐ŸŒŸ Challenge 1: Let players use A, B, C for rows!
+ row_letter = input("Row (A-E): ").upper()
+ guess_row = ord(row_letter) - ord('A')
+ โ€ข ๐ŸŒŸ Challenge 2: Show how many turns are left! +
+ +
+ 35โ€“40 min โ†’ Play & Reflect
+ Play your friendโ€™s game! What makes it fun?
+ โ€œNow YOU can make games โ€” not just play them!โ€ +
+ +

๐ŸŒŸ Youโ€™re Now a Game Coder!

+

Youโ€™ve learned the core ideas behind almost every game: hidden objects, player input, feedback, and win/lose conditions.

+ + \ No newline at end of file diff --git a/Battleships/battleships-137-main/.DS_Store b/Battleships/battleships-137-main/.DS_Store new file mode 100644 index 0000000..7f938ab Binary files /dev/null and b/Battleships/battleships-137-main/.DS_Store differ diff --git a/Battleships/battleships-137-main/.gitignore b/Battleships/battleships-137-main/.gitignore new file mode 100644 index 0000000..29c24da --- /dev/null +++ b/Battleships/battleships-137-main/.gitignore @@ -0,0 +1,9 @@ +core.Microsoft* +core.mongo* +core.python* +env.py +__pycache__/ +*.py[cod] +node_modules/ +.github/ +creds.json \ No newline at end of file diff --git a/Battleships/battleships-137-main/.gitpod.dockerfile b/Battleships/battleships-137-main/.gitpod.dockerfile new file mode 100644 index 0000000..cb70866 --- /dev/null +++ b/Battleships/battleships-137-main/.gitpod.dockerfile @@ -0,0 +1,84 @@ +FROM gitpod/workspace-base + +RUN echo "CI version from base" + +### NodeJS ### +USER gitpod +ENV NODE_VERSION=16.13.0 +ENV TRIGGER_REBUILD=1 +RUN curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | PROFILE=/dev/null bash \ + && bash -c ". .nvm/nvm.sh \ + && nvm install $NODE_VERSION \ + && nvm use $NODE_VERSION \ + && nvm alias default $NODE_VERSION \ + && npm install -g typescript yarn node-gyp" \ + && echo ". ~/.nvm/nvm.sh" >> /home/gitpod/.bashrc.d/50-node +ENV PATH=$PATH:/home/gitpod/.nvm/versions/node/v${NODE_VERSION}/bin + +### Python ### +USER gitpod +RUN sudo install-packages python3-pip +ENV PYTHON_VERSION 3.12.2 + +ENV PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH +RUN curl -fsSL https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash \ + && { echo; \ + echo 'eval "$(pyenv init -)"'; \ + echo 'eval "$(pyenv virtualenv-init -)"'; } >> /home/gitpod/.bashrc.d/60-python \ + && pyenv update \ + && pyenv install $PYTHON_VERSION \ + && pyenv global $PYTHON_VERSION \ + && python3 -m pip install --no-cache-dir --upgrade pip \ + && python3 -m pip install --no-cache-dir --upgrade \ + setuptools wheel virtualenv pipenv pylint rope flake8 \ + mypy autopep8 pep8 pylama pydocstyle bandit notebook \ + twine \ + && sudo rm -rf /tmp/*USER gitpod +ENV PYTHONUSERBASE=/workspace/.pip-modules \ + PIP_USER=yes +ENV PATH=$PYTHONUSERBASE/bin:$PATH + +# Setup Heroku CLI +RUN curl https://cli-assets.heroku.com/install.sh | sh + +RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb && sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb && \ + sudo rm -rf /var/cache/apt/* /var/lib/apt/lists/* /tmp/* /home/gitpod/*.deb && \ + sudo chown -R gitpod:gitpod /home/gitpod/.cache/heroku/ + +# Setup PostgreSQL + +RUN sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list' && \ + sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8 && \ + sudo apt-get update -y && \ + sudo apt-get install -y postgresql-12 + +ENV PGDATA="/workspace/.pgsql/data" + +RUN mkdir -p ~/.pg_ctl/bin ~/.pg_ctl/sockets \ + && echo '#!/bin/bash\n[ ! -d $PGDATA ] && mkdir -p $PGDATA && initdb --auth=trust -D $PGDATA\npg_ctl -D $PGDATA -l ~/.pg_ctl/log -o "-k ~/.pg_ctl/sockets" start\n' > ~/.pg_ctl/bin/pg_start \ + && echo '#!/bin/bash\npg_ctl -D $PGDATA -l ~/.pg_ctl/log -o "-k ~/.pg_ctl/sockets" stop\n' > ~/.pg_ctl/bin/pg_stop \ + && chmod +x ~/.pg_ctl/bin/* + +# ENV DATABASE_URL="postgresql://gitpod@localhost" +# ENV PGHOSTADDR="127.0.0.1" +ENV PGDATABASE="postgres" + +ENV PATH="/usr/lib/postgresql/12/bin:/home/gitpod/.nvm/versions/node/v${NODE_VERSION}/bin:$HOME/.pg_ctl/bin:$PATH" + + +# Add aliases + +RUN echo 'alias run="python3 $GITPOD_REPO_ROOT/manage.py runserver 0.0.0.0:8000"' >> ~/.bashrc && \ + echo 'alias heroku_config=". $GITPOD_REPO_ROOT/.vscode/heroku_config.sh"' >> ~/.bashrc && \ + echo 'alias python=python3' >> ~/.bashrc && \ + echo 'alias pip=pip3' >> ~/.bashrc && \ + echo 'alias arctictern="python3 $GITPOD_REPO_ROOT/.vscode/arctictern.py"' >> ~/.bashrc && \ + echo 'alias font_fix="python3 $GITPOD_REPO_ROOT/.vscode/font_fix.py"' >> ~/.bashrc && \ + echo 'alias set_pg="export PGHOSTADDR=127.0.0.1"' >> ~/.bashrc && \ + echo 'alias make_url="python3 $GITPOD_REPO_ROOT/.vscode/make_url.py "' >> ~/.bashrc + +# Local environment variables +ENV PORT="8080" +ENV IP="0.0.0.0" + + diff --git a/Battleships/battleships-137-main/.gitpod.yml b/Battleships/battleships-137-main/.gitpod.yml new file mode 100644 index 0000000..39778f8 --- /dev/null +++ b/Battleships/battleships-137-main/.gitpod.yml @@ -0,0 +1,14 @@ +image: + file: .gitpod.dockerfile +tasks: + - init: . ${GITPOD_REPO_ROOT}/.vscode/init_tasks.sh + command: /home/gitpod/.pg_ctl/bin/pg_start > /dev/null + - command: . ${GITPOD_REPO_ROOT}/.vscode/uptime.sh & +vscode: + extensions: + - ms-python.python + - formulahendry.auto-close-tag + - eventyret.bootstrap-4-cdn-snippet + - hookyqr.beautify + - matt-rudge.auto-open-preview-panel + diff --git a/Battleships/battleships-137-main/.replit b/Battleships/battleships-137-main/.replit new file mode 100644 index 0000000..2bec17a --- /dev/null +++ b/Battleships/battleships-137-main/.replit @@ -0,0 +1,8 @@ +modules = ["web", "nodejs-20", "python-3.12"] +run = "python3 run.py" + +[nix] +channel = "stable-24_05" + +[deployment] +run = ["sh", "-c", "python3 run.py"] diff --git a/Battleships/battleships-137-main/.vscode/arctictern.py b/Battleships/battleships-137-main/.vscode/arctictern.py new file mode 100644 index 0000000..bd164ce --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/arctictern.py @@ -0,0 +1,186 @@ +""" +arctictern.py +A little script that does a big migration +""" + +import json +import os +import requests +import shutil +import subprocess +import sys +from os.path import exists + +COLOURS = {"red": "\033[31m", + "blue": "\033[34m", + "green": "\033[32m", + "reset": "\033[0m", + "bold": "\033[1m"} + +BASE_URL = "https://raw.githubusercontent.com/Code-Institute-Org/gitpod-full-template/main/" +CURRENT_VERSION = 1.0 +THIS_VERSION = 1.0 + + +UPGRADE_FILE_LIST = [{"filename": ".vscode/settings.json", + "url": ".vscode/settings.json" + }, + {"filename": ".vscode/launch.json", + "url": ".vscode/launch.json" + }, + {"filename": ".gitpod.yml", + "url": ".gitpod.yml" + }, + {"filename": ".gitpod.dockerfile", + "url": ".gitpod.dockerfile" + }, + {"filename": ".vscode/heroku_config.sh", + "url": ".vscode/heroku_config.sh" + }, + {"filename": ".vscode/init_tasks.sh", + "url": ".vscode/init_tasks.sh" + }, + {"filename": ".vscode/uptime.sh", + "url": ".vscode/uptime.sh" + }, + {"filename": ".vscode/make_url.py", + "url": ".vscode/make_url.py" + }, + {"filename": ".vscode/arctictern.py", + "url": ".vscode/arctictern.py" + }] + +FINAL_LINES = "\nexport POST_UPGRADE_RUN=1\nsource ~/.bashrc\n" + + +def get_versions(): + + if exists(".vscode/version.txt"): + with open(".vscode/version.txt", "r") as f: + THIS_VERSION = float(f.read().strip()) + else: + with open(".vscode/version.txt", "w") as f: + f.write(str(THIS_VERSION)) + + r = requests.get(BASE_URL + ".vscode/version.txt") + CURRENT_VERSION = float(r.content) + + return {"this_version": THIS_VERSION, + "current_version": CURRENT_VERSION} + +def needs_upgrade(): + """ + Checks the version of the current template against + this version. + Returns True if upgrade is needed, False if not. + """ + + versions = get_versions() + + print(f"Upstream version: {versions['current_version']}") + print(f"Local version: {versions['this_version']}") + + return versions["current_version"] > versions["this_version"] + + +def write_version(): + + versions = get_versions() + + with open(".vscode/version.txt", "w") as f: + f.write(str(versions["current_version"])) + + +def build_post_upgrade(): + + r = requests.get(BASE_URL + ".vscode/upgrades.json") + upgrades = json.loads(r.content.decode("utf-8")) + content = "" + + for k,v in upgrades.items(): + if float(k) > THIS_VERSION: + print(f"Adding version changes for {k} to post_upgrade.sh") + content += v + + if content: + content += FINAL_LINES + with open(".vscode/post_upgrade.sh", "w") as f: + f.writelines(content) + + print("Built post_upgrade.sh. Restart your workspace for it to take effect.") + + +def process(file, suffix): + """ + Replaces and optionally backs up the files that + need to be changed. + Arguments: file - a path and filename + suffix - the suffix to the BASE_URL + """ + + if file == ".gitpod.dockerfile" or file == ".gitpod.yml": + try: + shutil.copyfile(file, f"{file}.tmp") + except FileNotFoundError: + pass + + with open(file, "wb") as f: + r = requests.get(BASE_URL + suffix) + f.write(r.content) + + if exists(f"{file}.tmp"): + result = os.system(f"diff -q {file} {file}.tmp > /dev/null") + if result != 0: + os.remove(f"{file}.tmp") + return True + + return False + + +def start_migration(): + """ + Calls the process function and + renames the directory + """ + push_and_recreate = False + + if not os.path.isdir(".vscode"): + print("Creating .vscode directory") + os.mkdir(".vscode") + + for file in UPGRADE_FILE_LIST: + print(f"Processing: {file['filename']}") + result = process(file["filename"], file["url"]) + if result == True: + push_and_recreate = True + + if push_and_recreate: + write_version() + + if needs_upgrade() and not push_and_recreate: + build_post_upgrade() + + print("Changes saved.") + print("Please add, commit and push to GitHub.") + print("You may need to stop and restart your workspace for") + print("the changes to take effect.\n") + + if push_and_recreate: + print(f"{COLOURS['red']}{COLOURS['bold']}*** IMPORTANT INFORMATION ***{COLOURS['reset']}") + print("The files used to create this workspace have been updated") + print("Please download any files that are in .gitignore and") + print("recreate this workspace by clicking on the Gitpod button") + print("in GitHub. Then, upload your saved files again.\n") + + +if __name__ == "__main__": + + print(f"\n๐Ÿฆ {COLOURS['blue']}{COLOURS['bold']}ArcticTern version 0.3{COLOURS['reset']}") + print("CI Template Migration Utility") + print("-----------------------------") + print("Upgrades the workspace to the latest version.\n") + + if input("Start? Y/N ").lower() == "y": + start_migration() + else: + sys.exit("Migration cancelled by the user") diff --git a/Battleships/battleships-137-main/.vscode/heroku_config.sh b/Battleships/battleships-137-main/.vscode/heroku_config.sh new file mode 100644 index 0000000..5056d45 --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/heroku_config.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Script to allow Heroku API key to be pasted +# exported as an environment variable +# +# Matt Rudge, May 2021 + +echo Heroku authentication configuration script +echo Code Institute, 2021 +echo +echo Get your Heroku API key by going to https://dashboard.heroku.com +echo Go to Account Settings and click on Reveal to view your Heroku API key +echo + +if [[ -z "${HEROKU_API_KEY}" ]]; then + echo Paste your Heroku API key here or press Enter to quit: + read apikey + if [[ -z "${apikey}" ]]; then + return 0 + fi + echo export HEROKU_API_KEY=${apikey} >> ~/.bashrc + echo Added the export. Refreshing the terminal. + . ~/.bashrc > /dev/null + echo Done! +else + echo API key is already set. + echo + echo To reset the API key please input "'reset'": + read reset_trigger + if [[ ${reset_trigger} == reset ]]; then + unset HEROKU_API_KEY + unset reset_trigger + echo + echo API key removed! + else + unset reset_trigger + echo API key unchanged. + fi + echo + echo Exiting +fi diff --git a/Battleships/battleships-137-main/.vscode/init_tasks.sh b/Battleships/battleships-137-main/.vscode/init_tasks.sh new file mode 100644 index 0000000..c532e38 --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/init_tasks.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Gives a personalised greeting +# Adds configuration options for SQLite +# Creates run aliases +# Author: Matt Rudge + +echo "Setting the greeting" +sed -i "s/USER_NAME/$GITPOD_GIT_USER_NAME/g" ${GITPOD_REPO_ROOT}/README.md +echo "Creating .sqliterc file" +echo ".headers on" > ~/.sqliterc +echo ".mode column" >> ~/.sqliterc +echo "Your workspace is ready to use. Happy coding!" diff --git a/Battleships/battleships-137-main/.vscode/make_url.py b/Battleships/battleships-137-main/.vscode/make_url.py new file mode 100644 index 0000000..1a27167 --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/make_url.py @@ -0,0 +1,14 @@ +# Simple utility for creating the Cloudinary URL from a +# cloudinary_python.txt file +# Matt Rudge, November 2021 + +import re + +with open("cloudinary_python.txt") as f: + content = f.readlines() + +cloud_name = re.findall(r"['](.*?)[']",content[15])[0] +api_key = re.findall(r"['](.*?)[']",content[16])[0] +api_secret = re.findall(r"['](.*?)[']",content[17])[0] + +print(f"cloudinary://{api_key}:{api_secret}@{cloud_name}") diff --git a/Battleships/battleships-137-main/.vscode/settings.json b/Battleships/battleships-137-main/.vscode/settings.json new file mode 100644 index 0000000..c683001 --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "python.pythonPath": ".venv/bin/python", + "python.formatting.provider": "black", + "python.formatting.blackArgs": ["--line-length=79" ], + "python.formatting.autopep8Args": ["--max-line-length=79" ], + "python.linting.enabled": true, + "python.linting.flake8Enabled": true, + "python.linting.pylintEnabled": true, + "python.linting.lintOnSave": true, + "python.linting.flake8Args": [ + "--max-line-length=79" + ], + "python.linting.pylintArgs": [ + "--disable=C0111" // Disable missing docstring warnings if needed + ], + "files.autoSave": "onFocusChange" +} \ No newline at end of file diff --git a/Battleships/battleships-137-main/.vscode/uptime.sh b/Battleships/battleships-137-main/.vscode/uptime.sh new file mode 100644 index 0000000..25a37f8 --- /dev/null +++ b/Battleships/battleships-137-main/.vscode/uptime.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Pings the webhook so that we can gather +# basic usage stats. No personally identifiable +# data is captured here, and it is impossible to +# identify an individual user from the captured data. +# Matt Rudge, April 2021 + +UUID=$(cat /proc/sys/kernel/random/uuid) +URL=https://1xthkmzwg3.execute-api.eu-west-1.amazonaws.com/prod/lrsapi/ +API_KEY=jceBCdeGZP9RDeUNCfM4jIQ39Cx0jtG51QgcwDwc +VERB="started" + +clear + +while true; do + + DATA="{\"activity_time\":\"$(date +%Y-%m-%dT%H:%M:%S).000Z\",\"actor\":\"${UUID}\",\"verb\":\"${VERB}\",\"activity_object\":\"Gitpod Workspace\",\"extra_data\":\"{}\"}" + curl -s -X POST -H "x-api-key: ${API_KEY}" -d "${DATA}" ${URL} 1> /dev/null + VERB="running" + sleep 300 + +done diff --git a/Battleships/battleships-137-main/Procfile b/Battleships/battleships-137-main/Procfile new file mode 100644 index 0000000..5ec9cc2 --- /dev/null +++ b/Battleships/battleships-137-main/Procfile @@ -0,0 +1 @@ +web: node index.js \ No newline at end of file diff --git a/Battleships/battleships-137-main/README.md b/Battleships/battleships-137-main/README.md new file mode 100644 index 0000000..5458730 --- /dev/null +++ b/Battleships/battleships-137-main/README.md @@ -0,0 +1,284 @@ +# [Battleship Game](https://battleships-hedgemonkey-131f157c44ae.herokuapp.com/ "Click to see deployed app") + +This is a simple command-line Battleship game written in Python, with a leader board stored in a google docs spreadsheet. + +## Contents + +
+Click here for Table of Contents + +[Screenshots](#screenshots) + +[Flow Chart Screenshots](#flow-chart-screenshots) + +[Features](#features) + +[Testing](#testing) + +[Future](#future) + +[Deployment](#deployment) + +[Credits](#credits) + +
+ +## Screenshots +| Heroku Deployed App | +| :---: | +| ![Screenshot of app on Heroku](readme/screenshot.jpg) | +| This is the app showing the Welcome screenwith Leaderboard with scores sourced through Google Docs API | + +## Flow Chart Screenshots +| Flow Chart Screenshot | +| :---: | +| ![Screenshot of Flowchart](readme/flow_chart.jpg) | +| This is a rough flow chart displaying the program processes made using [Milanote](https://milanote.com/) + +[Back to top](#contents) + +## Features +* **Player vs. Computer:** Play against a challenging AI opponent. +* **Multiple Board Sizes:** Choose a board size between 5x5 and 9x9. +* **High Scores:** Track your best scores and compare them to others. +* **Colorful Interface:** Enjoy a visually enhanced gameplay experience with colored console output. +* **Leaderboard:** Top scores saved to a Google sheets leaderboard using `Google Docs API` and `gspread` + +[Back to top](#contents) + +## Testing + +### Replit +Most of this project was developed using Replit and so testing was done as I was building the project step by step. + +### Manual Testing +The following manual tests were conducted to ensure the application works as expected: + +#### Test 1: Welcome Message +**Steps:** +1. Run the application. +2. Observe the welcome message displayed in the console. + +**Expected Result:** +- The welcome message should be centered and displayed with appropriate colors. + +**Actual Result:** +- The welcome message is displayed correctly. + +**Screenshot:** +![Welcome Message](readme/welcome.jpg) + +#### Test 2: High Scores Display +**Steps:** +1. Run the application. +2. Observe the high scores displayed after the welcome message. + +**Expected Result:** +- The high scores should be displayed in a centered format with appropriate colors. + +**Actual Result:** +- The high scores are displayed correctly. + +**Screenshot:** +![High Scores](readme/welcome.jpg) + +#### Test 3: Board Size Input +**Steps:** +1. Run the application. +2. Enter a valid board size (between 5 and 9). + +**Expected Result:** +- The application should accept the input and proceed to the next step. + +**Actual Result:** +- The application accepts valid inputs and proceeds correctly. + +**Screenshot:** +![Board Size Input](readme/board_size.jpg) + +#### Test 4: Invalid Board Size Input +**Steps:** +1. Run the application. +2. Enter an invalid board size (outside the range of 5 to 9). + +**Expected Result:** +- The application should display an error message and prompt for input again. + +**Actual Result:** +- The application displays an error message and prompts for input again. + +**Screenshot:** +![Invalid Board Size Input](readme/board_size.jpg) + +#### Test 5: Player Turn +**Steps:** +1. Run the application. +2. Enter valid coordinates for the player's turn. + +**Expected Result:** +- The application should update the board and display the result (hit or miss). + +**Actual Result:** +- The application updates the board and displays the result correctly. + +**Screenshot:** +![Player Turn](readme/player_turn.jpg) + +#### Test 6: Computer Turn +**Steps:** +1. Run the application. +2. Observe the computer's turn. + +**Expected Result:** +- The application should update the board and display the result (hit or miss). + +**Actual Result:** +- The application updates the board and displays the result correctly. + +**Screenshot:** +![Computer Turn](readme/player_turn.jpg) + +#### Test 7: Game Over +**Steps:** +1. Run the application. +2. Play the game until either the player or the computer wins. + +**Expected Result:** +- The application should display the game over message and the final scores. + +**Actual Result:** +- The application displays the game over message and the final scores correctly. + +**Screenshot:** +![Game Over](readme/game_over.jpg) + +#### Test 8: Save High Score +**Steps:** +1. Run the application. +2. Win the game and choose to save the high score. +3. Enter a name when prompted. + +**Expected Result:** +The application should save the high score to the Google Sheets and display a success message. + +**Actual Result:** +The application saves the high score and displays the success message correctly. + +**Empty Username:** Players can intentionally leave the username field empty if they prefer not to submit a name. The high score will still be recorded on the leaderboard with a blank entry for the name. + +**Screenshot:** +![Save High Score](readme/save_high_score.jpg) + +[Back to top](#contents) + +### PEP 8 Compliance + +The project's Python code has been refactored to improve adherence to PEP 8 style guidelines. This enhances readability and maintainability. Specific improvements include: + +* **Reduced Branching:** Complex conditional logic, particularly in the `display_boards` function, has been simplified to reduce the number of branches, improving code clarity. +* **Modernized String Formatting:** F-strings (formatted string literals) have been adopted throughout the codebase for more concise and readable string formatting. +* **Removed Redundant Code:** Unnecessary `else` clauses following `return`, `break`, and `continue` statements have been eliminated. +* **Import Order:** Imports have been reorganized to follow PEP 8 recommendations (standard library imports first, followed by third-party and then local imports). + +These changes were guided by feedback from the `pylint` static analysis tool. + +![Pylint Output](readme/pylint_screenshot.jpg) + +[Back to top](#contents) + + +## Future +### Potential Future Features +* **Player vs. Player:** Perhaps add functionality to allow for a 2 player turn-based mode +* **Various Ship Sizes:** It would be nice to add different dized ships more akin to traditional Battleships game, with a choice of orientation +* **Number of Ships:** The added functionality to not only choose the board size but to also choose how many ships on the board would also be an attractive addition to the game +* **Choose Ship Placement:** The option to decide where you would like to place your ships on the board would also be a nice future feature to incorporate + +[Back to top](#contents) + +## Deployment +The site was deployed to Heroku. The steps to deploy are as follows: + +- Set up a [Heroku](https://dashboard.heroku.com) Account and create a new App +- In Settings add the python and nodejs buildpacks +- In `Settings > Config Vars` Add the creds.json contents under the variable `CREDS` +- Link the [GitHub repository](https://github.com/hedgemonkey/battleships) to the Heroku app. + +Heroku git URL +[https://git.heroku.com/battleships-hedgemonkey.git] + +The live link can be found [here](https://battleships-hedgemonkey-131f157c44ae.herokuapp.com/) + +[Back to top](#contents) + +### Google Docs +This project uses a `Google Sheets` spreadsheet hosted on `Google Docs` accessed by `Google Cloud API` this is to keep track of past scores. + +In order to deploy this yourself you would have to get the correct credentials to access this file or specify your own spreadsheet and provide your own Google Cloud API Credentials + +To do this you ned to save your own credentials to `creds.json` and change the line `SHEET = GSPREAD_CLIENT.open('battleship_scores').sheet1` replacing `battleship_scores` with your own spreadsheet filename + +The spreadsheet can be access [HERE](https://docs.google.com/spreadsheets/d/1cUhnYhy8DuxIEW6_BLnj0OFL38dNoRqmyVvkgnQDai8/edit?usp=sharing) + +[Back to top](#contents) + +### Local Deployment + +You can clone or fork this project to make a local copy on your system. + +[Back to top](#contents) + +#### Cloning + +You can clone the repository by following these steps: + +1. Go to the [GitHub repository](https://github.com/Hedgemonkey/battleships). +2. Locate the Code button above the list of files and click it. +3. Select if you prefer to clone using HTTPS, SSH, or GitHub CLI and click the copy button to copy the URL to your clipboard. +4. Open Git Bash or Terminal. +5. Change the current working directory to the one where you want the cloned directory. +6. In your IDE Terminal, type the following command to clone my repository: + - `git clone https://github.com/hedgemonkey/battleships.git` +7. Press Enter to create your local clone. + +[Back to top](#contents) + +#### Forking + +By forking the GitHub Repository, we make a copy of the original repository on our GitHub account to view and/or make changes without affecting the original owner's repository. +You can fork this repository by using the following steps: + +1. Log in to GitHub and locate the [GitHub repository](https://github.com/Hedgemonkey/battleships). +2. At the top of the Repository (not the top of the page) just above the "Settings" Button on the menu, locate the "Fork" Button. +3. Once clicked, you should now have a copy of the original repository in your own GitHub account. + +[Back to top](#contents) + +### Local vs Deployment + +There are no notable differences between my locally developed app and the Heroku deployed site aside from slight variances in the terminal colours. + +[Back to top](#contents) + + +## Credits + +In this section, I will reference the sources of my content and media, full disclosure of any resources used shall be detailed here. + +- Milanote used to create Flow Chart Diagram + +- Gemini Pro AI Used to help guide me when code wasn't doing as I intended + +[Back to top](#contents) + +### Content + +[Back to top](#contents) + +### Acknowledgements + +- I would like to thank the [Code Institute Slack community](https://code-institute-room.slack.com) for the moral support and general information that helps with my studies. +- I would like to also thank Gemini Pro AI for assisting me whenever I needed a bit of quick advice, although it's suggestions often lead to more complications it's explinations and tips were incredibly helpful +- YTMusic for providing me with a soundtrack to work to + +[Back to top](#contents) \ No newline at end of file diff --git a/Battleships/battleships-137-main/controllers/default.js b/Battleships/battleships-137-main/controllers/default.js new file mode 100644 index 0000000..96ffdd4 --- /dev/null +++ b/Battleships/battleships-137-main/controllers/default.js @@ -0,0 +1,60 @@ +const Pty = require('node-pty'); +const fs = require('fs'); + +exports.install = function () { + + ROUTE('/'); + WEBSOCKET('/', socket, ['raw']); + +}; + +function socket() { + + this.encodedecode = false; + this.autodestroy(); + + this.on('open', function (client) { + + // Spawn terminal + client.tty = Pty.spawn('python3', ['run.py'], { + name: 'xterm-color', + cols: 80, + rows: 24, + cwd: process.env.PWD, + env: process.env + }); + + client.tty.on('exit', function (code, signal) { + client.tty = null; + client.close(); + console.log("Process killed"); + }); + + client.tty.on('data', function (data) { + client.send(data); + }); + + }); + + this.on('close', function (client) { + if (client.tty) { + client.tty.kill(9); + client.tty = null; + console.log("Process killed and terminal unloaded"); + } + }); + + this.on('message', function (client, msg) { + client.tty && client.tty.write(msg); + }); +} + +if (process.env.CREDS != null) { + console.log("Creating creds.json file."); + fs.writeFile('creds.json', process.env.CREDS, 'utf8', function (err) { + if (err) { + console.log('Error writing file: ', err); + socket.emit("console_output", "Error saving credentials: " + err); + } + }); +} \ No newline at end of file diff --git a/Battleships/battleships-137-main/index.js b/Battleships/battleships-137-main/index.js new file mode 100644 index 0000000..2598955 --- /dev/null +++ b/Battleships/battleships-137-main/index.js @@ -0,0 +1,30 @@ +// =================================================== +// Total.js start script +// https://www.totaljs.com +// =================================================== + +const options = {}; + +// options.ip = '127.0.0.1'; +options.port = parseInt(process.env.PORT); +// options.unixsocket = require('path').join(require('os').tmpdir(), 'app_name'); +// options.config = { name: 'Total.js' }; +// options.sleep = 3000; +// options.inspector = 9229; +// options.watch = ['private']; +// options.livereload = 'https://yourhostname'; + +// Enables cluster: +// options.cluster = 'auto'; +// options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling) + +// Enables threads: +// options.cluster = 'auto'; +// options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling) +// options.timeout = 5000; +// options.threads = '/api/'; +// options.logs = 'isolated'; + +var type = process.argv.indexOf('--release', 1) !== -1 || process.argv.indexOf('release', 1) !== -1 ? 'release' : 'debug'; +// require('total4/' + type)(options); +require('total4').http('release', options); \ No newline at end of file diff --git a/Battleships/battleships-137-main/package-lock.json b/Battleships/battleships-137-main/package-lock.json new file mode 100644 index 0000000..29dfbf0 --- /dev/null +++ b/Battleships/battleships-137-main/package-lock.json @@ -0,0 +1,106 @@ +{ + "name": "terminal", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "terminal", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "node-pty": "^0.10.1", + "node-static": "^0.7.11", + "total4": "^0.0.45" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "license": "MIT" + }, + "node_modules/node-pty": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.10.1.tgz", + "integrity": "sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "nan": "^2.14.0" + } + }, + "node_modules/node-static": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", + "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", + "license": "MIT", + "dependencies": { + "colors": ">=0.6.0", + "mime": "^1.2.9", + "optimist": ">=0.3.4" + }, + "bin": { + "static": "bin/cli.js" + }, + "engines": { + "node": ">= 0.4.1" + } + }, + "node_modules/optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", + "license": "MIT/X11", + "dependencies": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "node_modules/total4": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/total4/-/total4-0.0.45.tgz", + "integrity": "sha512-96vXqejddbAeNL6zpzbfm8ztiWL4G0fOuoUWniHDlu6oSz+DcxekHkzK0Y9X+ErlHCRTMsP2+ieMTqG6waowiA==", + "license": "MIT", + "bin": { + "total4": "bin/total4" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/Battleships/battleships-137-main/package.json b/Battleships/battleships-137-main/package.json new file mode 100644 index 0000000..56ca191 --- /dev/null +++ b/Battleships/battleships-137-main/package.json @@ -0,0 +1,23 @@ +{ + "name": "terminal", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lechien73/terminal.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/lechien73/terminal/issues" + }, + "homepage": "https://github.com/lechien73/terminal#readme", + "dependencies": { + "node-static": "^0.7.11", + "node-pty": "^0.10.1", + "total4": "^0.0.45" + } +} \ No newline at end of file diff --git a/Battleships/battleships-137-main/readme/board_size.jpg b/Battleships/battleships-137-main/readme/board_size.jpg new file mode 100644 index 0000000..076b19d Binary files /dev/null and b/Battleships/battleships-137-main/readme/board_size.jpg differ diff --git a/Battleships/battleships-137-main/readme/flow_chart.jpg b/Battleships/battleships-137-main/readme/flow_chart.jpg new file mode 100644 index 0000000..61c9d05 Binary files /dev/null and b/Battleships/battleships-137-main/readme/flow_chart.jpg differ diff --git a/Battleships/battleships-137-main/readme/flow_chart.png b/Battleships/battleships-137-main/readme/flow_chart.png new file mode 100644 index 0000000..b548883 Binary files /dev/null and b/Battleships/battleships-137-main/readme/flow_chart.png differ diff --git a/Battleships/battleships-137-main/readme/game_over.jpg b/Battleships/battleships-137-main/readme/game_over.jpg new file mode 100644 index 0000000..1b04466 Binary files /dev/null and b/Battleships/battleships-137-main/readme/game_over.jpg differ diff --git a/Battleships/battleships-137-main/readme/player_turn.jpg b/Battleships/battleships-137-main/readme/player_turn.jpg new file mode 100644 index 0000000..805afc8 Binary files /dev/null and b/Battleships/battleships-137-main/readme/player_turn.jpg differ diff --git a/Battleships/battleships-137-main/readme/pylint_screenshot.jpg b/Battleships/battleships-137-main/readme/pylint_screenshot.jpg new file mode 100644 index 0000000..681e940 Binary files /dev/null and b/Battleships/battleships-137-main/readme/pylint_screenshot.jpg differ diff --git a/Battleships/battleships-137-main/readme/save_high_score.jpg b/Battleships/battleships-137-main/readme/save_high_score.jpg new file mode 100644 index 0000000..6686f44 Binary files /dev/null and b/Battleships/battleships-137-main/readme/save_high_score.jpg differ diff --git a/Battleships/battleships-137-main/readme/screenshot.jpg b/Battleships/battleships-137-main/readme/screenshot.jpg new file mode 100644 index 0000000..936bab8 Binary files /dev/null and b/Battleships/battleships-137-main/readme/screenshot.jpg differ diff --git a/Battleships/battleships-137-main/readme/screenshot.png b/Battleships/battleships-137-main/readme/screenshot.png new file mode 100644 index 0000000..8ee4d02 Binary files /dev/null and b/Battleships/battleships-137-main/readme/screenshot.png differ diff --git a/Battleships/battleships-137-main/readme/welcome.jpg b/Battleships/battleships-137-main/readme/welcome.jpg new file mode 100644 index 0000000..cbe24ca Binary files /dev/null and b/Battleships/battleships-137-main/readme/welcome.jpg differ diff --git a/Battleships/battleships-137-main/requirements.txt b/Battleships/battleships-137-main/requirements.txt new file mode 100644 index 0000000..84f6cf3 --- /dev/null +++ b/Battleships/battleships-137-main/requirements.txt @@ -0,0 +1,20 @@ +cachetools==5.5.0 +certifi==2024.8.30 +cffi==1.17.1 +charset-normalizer==3.3.2 +cryptography==43.0.1 +google-auth==2.34.0 +google-auth-oauthlib==1.2.1 +google-oauth==1.0.1 +gspread==6.1.2 +idna==3.8 +oauthlib==3.2.2 +pyasn1==0.6.0 +pyasn1_modules==0.4.0 +pycparser==2.22 +pyOpenSSL==24.2.1 +requests==2.32.3 +requests-oauthlib==2.0.0 +rsa==4.9 +six==1.16.0 +urllib3==2.2.2 diff --git a/Battleships/battleships-137-main/run.py b/Battleships/battleships-137-main/run.py new file mode 100644 index 0000000..59e29ad --- /dev/null +++ b/Battleships/battleships-137-main/run.py @@ -0,0 +1,371 @@ +"""This module is a battleship game""" + +import os +import re +import string # Corrected import order (see point 14) +import random +import gspread +from google.oauth2.service_account import Credentials + + +SCOPE = [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/drive.file", + "https://www.googleapis.com/auth/drive" + ] + + +CREDS = Credentials.from_service_account_file('creds.json') +SCOPED_CREDS = CREDS.with_scopes(SCOPE) +GSPREAD_CLIENT = gspread.authorize(SCOPED_CREDS) +SHEET = GSPREAD_CLIENT.open('battleship_scores').sheet1 + + +def center_text(text): + """ + Function to center text in the console + """ + console_width = os.get_terminal_size().columns + text_length = len(re.sub(r'\x1b\[[0-9;]*m', '', text)) + padding = (console_width - text_length) // 2 + return " " * padding + text + " " * padding + + +def pause(): + """ + Function to pause and clear the screen + """ + input("\n" + Bcolors.OKCYAN + center_text("Press Enter to continue...") + + Bcolors.ENDC) + os.system('cls' if os.name == 'nt' else 'clear') + + +class Bcolors: + # pylint: disable=too-few-public-methods + """ + Colors for console output + """ + HEADER = '\033[95m' + OKBLUE = '\033[34m' + OKCYAN = '\033[96m' + OKGREEN = '\033[32m' + WARNING = '\033[93m' + RED = '\033[31m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +def welcome_message(): + """ + Function to display the welcome message with colors + """ + print(Bcolors.HEADER + + Bcolors.BOLD + + center_text("Welcome to Battleships!") + + Bcolors.ENDC) + print("\n" + Bcolors.BOLD + + center_text("Welcome to my simple battleships game!") + + Bcolors.ENDC) + print("\n" + Bcolors.BOLD + + center_text("The aim of the game is to hit your opponents ships" + + " in the least trys possible!") + + Bcolors.ENDC) + print("\n" + Bcolors.BOLD + + center_text("Just select a gameboard size between a 5x5 grid and a" + + " 9x9 grid!") + + Bcolors.ENDC) + print("\n" + Bcolors.BOLD + + center_text("Then enter the co-ordinates of where you want to" + + " shoot when prompted!") + + Bcolors.ENDC) + + +def display_high_scores(): + """ + Function to display top 5 high scores + """ + print("\n" + Bcolors.HEADER + center_text("Current High Scores") + + Bcolors.ENDC) + print("\n" + Bcolors.OKCYAN + center_text("Top 5 Lowest Misses:") + + Bcolors.ENDC) + scores = SHEET.get_all_values() + sorted_scores = scores[1:] + sorted_scores.sort(key=lambda x: int(x[1])) + for i in range(1, 6): + if i <= len(sorted_scores): + print(center_text(f"{i}. {sorted_scores[i-1][0]}: { + sorted_scores[i-1][1]}")) + else: + print(f"{i}. N/A") + + +def get_board_size(): + """ + Function to get valid board size input + """ + while True: + try: + size = int(input("Choose board size (5-9): ")) + if 5 <= size <= 9: + return size + print("Invalid size. Please enter a number between 5 and 9") + except ValueError: + print("Invalid input. Please enter a number.") + + +def create_boards(size): + """ + Function to create and initialize boards + """ + player_board = [['O' for _ in range(size)] for _ in range(size)] + computer_board = [['O' for _ in range(size)] for _ in range(size)] + return player_board, computer_board + + +def place_ships(board, num_ships): + """ + Function to place ships randomly + """ + ship_locations = [] + for _ in range(num_ships): + while True: + row = random.randint(0, len(board) - 1) + col = random.randint(0, len(board[0]) - 1) + if board[row][col] == 'O': + board[row][col] = 'S' + ship_locations.append((row, col)) + break + return ship_locations + + +def display_boards(player_board, computer_board): + """ + Function to display both boards side by side with colors and spacing + """ + board_size = len(player_board) + header_spacing = board_size - 3 - max(0, board_size - 6) + print("\n" + center_text(Bcolors.RED + "Player Board " + + " "*header_spacing + + Bcolors.ENDC + " | " + + Bcolors.RED + + " Computer Board" + + Bcolors.ENDC)) + print("\n" + center_text(Bcolors.OKCYAN + " " + + " ".join([str(i) for i in range( + 1, board_size + 1)]) + + " " + Bcolors.ENDC + + " |" + + " " + Bcolors.OKCYAN + + " ".join([str(i) for i in range( + 1, board_size + 1)]) + + " " + + Bcolors.ENDC)) + letters = list(string.ascii_uppercase[:board_size]) + for i in range(board_size): + player_row = [] + computer_row = [] + for j in range(board_size): + if player_board[i][j] == 'X': + player_row.append(Bcolors.RED + + Bcolors.BOLD + + 'X' + + Bcolors.ENDC) + elif player_board[i][j] == 'S': + player_row.append(Bcolors.OKBLUE + + Bcolors.BOLD + + 'S' + + Bcolors.ENDC) + elif player_board[i][j] == 'M': + player_row.append(Bcolors.OKGREEN + + Bcolors.BOLD + + 'M' + + Bcolors.ENDC) + else: + player_row.append(player_board[i][j]) + if computer_board[i][j] == 'X': + computer_row.append(Bcolors.RED + + Bcolors.BOLD + + 'X' + + Bcolors.ENDC) + elif computer_board[i][j] == 'M': + computer_row.append(Bcolors.OKGREEN + + Bcolors.BOLD + + 'M' + + Bcolors.ENDC) + else: + computer_row.append('O') + player_row_str = ' '.join(player_row) + computer_row_str = ' '.join(computer_row) + row_str = f"{Bcolors.OKCYAN}{letters[i]: <2} {Bcolors.ENDC}{ + player_row_str: <{len(player_row_str) + 2}} | { + Bcolors.OKCYAN}{ + letters[i]: <2}{ + Bcolors.ENDC} { + computer_row_str: <{len(computer_row_str) + 2}}" + print(center_text(row_str)) + + +def get_target(board): + """ + Function to get valid target coordinates + """ + while True: + try: + row_letter = input(f"Enter row (A-{chr(ord('A') + len(board) - 1)}). ").upper() + row = string.ascii_uppercase.index(row_letter.upper()) + col = int(input(f"Enter column (1-{len(board)}). ")) - 1 + if 0 <= row < len(board) and 0 <= col < len(board[0]): + return row, col + print("Invalid coordinates. Please enter numbers within the" + + " board size.") + except ValueError: + print("Invalid input. Please enter letters for Rows and numbers" + + " for columns.") + + +def player_turn(player_board, computer_board, computer_ships, misses): + """ + Function to handle player's turn + """ + print(Bcolors.OKBLUE + "\nYour turn:" + Bcolors.ENDC) + display_boards(player_board, computer_board) + while True: + row, col = get_target(computer_board) + if computer_board[row][col] == 'M' or computer_board[row][col] == 'X': + print(Bcolors.WARNING + + "You already tried that target. Choose another one.\n" + + Bcolors.ENDC) + else: + if (row, col) in computer_ships: + computer_board[row][col] = 'X' + computer_ships.remove((row, col)) + print(Bcolors.OKGREEN + + "Hit! Target: " + + chr(ord('A') + row) + + " " + + str(col + 1) + + Bcolors.ENDC) + else: + computer_board[row][col] = 'M' + print(Bcolors.RED + + "Miss! Target: " + + chr(ord('A') + row) + + " " + + str(col + 1) + + Bcolors.ENDC) + misses += 1 + print(Bcolors.OKCYAN + + "\nMisses: " + + Bcolors.ENDC + + str(misses)) + break + return computer_board, computer_ships, misses + + +def computer_turn(player_board, player_ships, misses): + """ + Function to handle computer's turn + """ + print(Bcolors.OKCYAN + "\nComputer's turn:" + Bcolors.ENDC) + while True: + row = random.randint(0, len(player_board) - 1) + col = random.randint(0, len(player_board[0]) - 1) + if player_board[row][col] == 'M' or player_board[row][col] == 'X': + continue + if (row, col) in player_ships: + player_board[row][col] = 'X' + player_ships.remove((row, col)) + print(Bcolors.OKGREEN + + "Computer hit! Target: " + + chr(ord('A') + row) + + " " + + str(col + 1) + + Bcolors.ENDC) + break + player_board[row][col] = 'M' + print(Bcolors.RED + + "Computer missed! Target: " + + chr(ord('A') + row) + + " " + + str(col + 1) + + Bcolors.ENDC) + misses += 1 + break + pause() + return player_board, player_ships, misses + + +def play_game(): + """ + Function to handle the game loop + """ + while True: + os.system('cl' if os.name == 'nt' else 'clear') + welcome_message() + display_high_scores() + board_size = get_board_size() + pause() + player_board, computer_board = create_boards(board_size) + player_ships = place_ships(player_board, 5) + computer_ships = place_ships(computer_board, 5) + player_misses = 0 + computer_misses = 0 + while player_ships and computer_ships: + computer_board, computer_ships, player_misses = player_turn( + player_board, + computer_board, + computer_ships, + player_misses) + if not computer_ships: + print(Bcolors.OKGREEN + "\nCongratulations! You win!" + + Bcolors.ENDC) + save_to_high_scores(player_misses) + break + player_board, player_ships, computer_misses = computer_turn( + player_board, + player_ships, + computer_misses) + if not player_ships: + print(Bcolors.RED + "\nYou lose! Better luck next time." + + Bcolors.ENDC) + break + print("\nFinal Score:") + print(f"Player: {player_misses}") + print(f"Computer: {computer_misses}") + + while True: + play_again = input("\nPlay again? (y/n): ").lower() + if play_again in ('y', 'n'): + os.system('cls' if os.name == 'nt' else 'clear') + break + print("Invalid input. Please enter 'y' or 'n'.") + if play_again == 'n': + print("Goodbye!") + break + + +def save_to_high_scores(misses): + """ + Function to handle saving scores to the spreadsheet + """ + while True: + save_score = input("Do you want to save your score to the high " + + "scores? (y/n): ") + if save_score.lower() == 'y': + name = input("Enter your name: ") + SHEET.append_row([name, misses]) + os.system('cls' if os.name == 'nt' else 'clear') + print("Score saved successfully!") + display_high_scores() + break + if save_score.lower() == 'n': + os.system('cls' if os.name == 'nt' else 'clear') + print("Score not saved.") + display_high_scores() + break + print("Invalid input. Please enter 'y' or 'n'.") + + +if __name__ == "__main__": + play_game() diff --git a/Battleships/battleships-137-main/runtime.txt b/Battleships/battleships-137-main/runtime.txt new file mode 100644 index 0000000..b884b0f --- /dev/null +++ b/Battleships/battleships-137-main/runtime.txt @@ -0,0 +1 @@ +python-3.12.2 \ No newline at end of file diff --git a/Battleships/battleships-137-main/views/index.html b/Battleships/battleships-137-main/views/index.html new file mode 100644 index 0000000..6e6c1ac --- /dev/null +++ b/Battleships/battleships-137-main/views/index.html @@ -0,0 +1,27 @@ + + +
+ + + \ No newline at end of file diff --git a/Battleships/battleships-137-main/views/layout.html b/Battleships/battleships-137-main/views/layout.html new file mode 100644 index 0000000..aaec1c9 --- /dev/null +++ b/Battleships/battleships-137-main/views/layout.html @@ -0,0 +1,173 @@ + + + + + + + + + + + Python Terminal by Code Institute + + + +@{body} + + \ No newline at end of file diff --git a/Battleships/lesson_1.py b/Battleships/lesson_1.py new file mode 100644 index 0000000..0eb0f6f --- /dev/null +++ b/Battleships/lesson_1.py @@ -0,0 +1,41 @@ +# BATTLESHIPS - Grade 7 Python Game +# Lesson 2: Full mini-game with 3 ships and turns! + +import random + +# Step 1: Create 3 hidden ships (as a list of [row, col]) +ships = [] +while len(ships) < 3: + r = random.randint(0, 4) + c = random.randint(0, 4) + if [r, c] not in ships: # avoid duplicates + ships.append([r, c]) + +print("3 ships are hidden on a 5x5 grid!") +print("You have 10 turns to find them all.") + +hits = 0 +turns = 10 + +# Step 2: Game loop +for turn in range(turns): + print("\nTurn", turn + 1) + + # Get guess + guess_row = int(input("Row (0-4): ")) + guess_col = int(input("Col (0-4): ")) + + # Check if guess is a ship + if [guess_row, guess_col] in ships: + print("๐ŸŽฏ HIT!") + ships.remove([guess_row, guess_col]) # remove found ship + hits += 1 + if hits == 3: + print("๐Ÿ† You found all ships! You win!") + break + else: + print("๐Ÿ’ฆ MISS!") + +# Step 3: Game over message +if hits < 3: + print("Game over! You found", hits, "out of 3 ships.") \ No newline at end of file diff --git a/Battleships/lesson_2.py b/Battleships/lesson_2.py new file mode 100644 index 0000000..6b34463 --- /dev/null +++ b/Battleships/lesson_2.py @@ -0,0 +1,22 @@ +# BATTLESHIPS - Grade 7 Python Game +# Lesson 1: Find the hidden ship! + +import random + +# Step 1: Create a secret ship location (row 0-4, col 0-4) +ship_row = random.randint(0, 4) +ship_col = random.randint(0, 4) + +# Step 2: Ask the player for a guess (we'll improve this later!) +print("Guess the ship location!") +guess_row = int(input("Row (0-4): ")) +guess_col = int(input("Col (0-4): ")) + +# Step 3: Check if they hit the ship +if guess_row == ship_row and guess_col == ship_col: + print("๐ŸŽฏ HIT! You sank the ship!") +else: + print("๐Ÿ’ฆ MISS! Try again.") + +# Step 4: (Optional) Show where the ship really was +print("The ship was at row", ship_row, "and col", ship_col) \ No newline at end of file