Skip to content

Commit 3e78c30

Browse files
committed
last push
1 parent e73263e commit 3e78c30

File tree

8 files changed

+619
-294
lines changed

8 files changed

+619
-294
lines changed

README.md

+92-16
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,106 @@
1-
try:
2-
- finish readme
3-
- run setup.py
4-
- try sphinx
1+
# Battleships Project
2+
Coursework
53

64
## Introduction
7-
brief summary of the purpose of the project
5+
This project is my rendition of the popular battleships game using Flask API and provided templates to deliver the game via web interface. In particular, someone can play against an "AI" opponent to win the game by sinking all the enemy ships, as the player you are able to provide custom ship placements whereas the "AI" makes random ship placements before the game begins. You are automatically redirected through the game so this project is intended to be accessible for all users, no knowledge of web server, command line or otherwise technical knowledge is necessary to run and enjoy the game.
6+
87

98
## Prerequisites
10-
any dependencies not in the installation
11-
Flask>=3.0.0
9+
Dependencies not in the installation:
10+
- Python>=3.6
11+
- Flask>=3.0.0
12+
1213

1314
## Installation
14-
any module dependencies
15-
usually pip install statement or python setup.py
15+
Other dependencies provided in Python Standard Library:
16+
- json>=2.0.9
17+
- random
18+
- logging
19+
These do not require pip install commands.
20+
1621

1722
## Getting Started Tutorial
18-
step-by-step example use case
23+
1. Set working directory to /source folder
24+
2. Run main.py python file
25+
- if using some python IDE like VSCode, click run python file
26+
- if using CMD, run command python main.py
27+
3. Go to url http://127.0.0.1:5000/placement to start the game
28+
4. You will automatically be prompted and redirected through the game, enjoy!
29+
5. When the game is finished (a win statement is presented) exit the program from the terminal typically by pressing CTRL+C
30+
31+
To run command-line interface version only:
32+
1. Set working directory to /source folder
33+
2. Run mp_game_engine.py python file
34+
- if using some python IDE like VSCode, click run python file
35+
- if using CMD, run command python mp_game_engine.py
36+
3. this version has the same functionality making use of AI opponent and "prints" the player board after every turn, enjoy!
37+
4. the interface will automatically exit when the game is finished (a win statement is presented)
38+
1939

2040
## Testing
21-
how to run and test the code
41+
This project can be run from a python IDE or the terminal (eg. CMD), in both cases the "console view" should provide the following message:
42+
> Serving Flask app 'main'
43+
>
44+
> Debug mode: off
45+
46+
This allows all debug and otherwise verbose content to be shown in log.txt inside /source folder - via logging library - this is for the benefit of testing as well simplicity for the average user. You do not need to interact with this file unless you want to inspect previous runs, several logging statements have been commented out but left in the project to show its use during development and to give any user the option to debug in case of errors.
47+
48+
When ending the program and closing the server there will be no further messages to the "console view" however reloading the starting url given above should return:
49+
> can't reach this page
50+
>
51+
> 127.0.0.1 refused to connect
52+
53+
This means the server has successfully been shut down, ensure you do not leave the server running as this has negative consequences of resource and memory management on your device.
54+
55+
56+
## Features and Self-Review
57+
The original functionality involves:
58+
- firstly a command line game of battleships with simple AI opponent including:
59+
- randomised AI actions using random library
60+
- custom player ship placement via json file "placement.json"
61+
62+
- AI attacks resulting in, contextually, ships "sinking"
63+
- winning statement and program end
64+
- and to expand on this, a web interface using given templates and building on Flask API, adding:
65+
- Flask API infrastructure
66+
- html template connections
67+
- asyncronous programming using url requests and mapping functions to these triggers
68+
69+
Additional feaatures implemented:
70+
- Within the logic:
71+
- ensured that the coordinate input system converts between everyday coordinates (from bottom left) to programming "coordinates" (from top left)
72+
- introduced checks for ensuring that if AI (randomly) doesnt picks the same board position multiple times which would otherwise slow down the game infinitely and result in immature and unrealistic game experience
73+
- enforced repeating hit outcome in case the player repeatedly picks the same board position multiple times, which would otherwise result in a previously sunken ship (red) to turn into an empty space (blue) which resulted in inconsistent gameplay and confusing visuals
74+
- Within the program:
75+
- error handling/ checks specifically in command-line interface as this takes direct user input
76+
- full use of docstrings for modules and methods, providing cohesive documentation on all sections of the program
77+
- use of logging both during testing process to compare runs but also for simplicity of "console view" when running program
78+
- Beyond the program:
79+
- config file allowing well formatted, easy to edit method to customise the app for further use/ specific needs
80+
- documentation using sphinx to pull docstrings and type hinting information from all modules
81+
- distribution files built using setup.py file providing source and build distribution files
82+
83+
Self-Review:
84+
85+
During the course of this project, I was able to make effective use of my resources, both digital and in-person, to develop the logic and test the functionality of the program. Unfortunately, lack of time management lead me to miss out on large portion of recording the testing/ unit testing, instead I devoted time to robust error-handling to balance this.
86+
87+
I chose to add certain features to the game to ensure consistency and clarity in the web interface, which I considered highly important for a GUI of a game, one of the key aspects of that being clear visuals.
88+
89+
I also followed good practices, refactoring and reusing my code to save time and run more efficiently such as with implementing the use of "placement.json". It was originally from the command-line interface as custom ship placements, and by overwriting it each time in web interface (in main.py module) repurposed the "place_battleships" function (in components.py module) without introducing a new subroutine to handle conversion between different data formats between my work and the html templates.
90+
2291

2392
## Developer Documentation
24-
detailed menual on how to use the code
93+
Please see /docs/buiild/html/index.html.
94+
This is a sphinx quick-build using the docstrings written within the python files, please be aware due to errors in the process it was not able to recognise modules which imported the components module therefore this documentation page is limited to that of the components module itself, unfortunately I was unable to resolve this.
95+
96+
To view specific details please see module/ function specific docstrings and type-hinting which have been provided for everything used in the project.
97+
98+
All modules and main code can be found in the /source folder along with the battleships.txt used to define ships and their sizes for the game and placement.json used to provide custom ship placement on the players board. These are default files defined in the config.json file which can easily be changed by the user to point to some equivalent file in the same /source directory. Please ensure you keep the same formatting style and file type for each if you choose to provide your own files. Additionally this is where log.txt can be found as discussed previously.
99+
100+
Sphinx documentation setup and html files can be found in /docs folder as described above, and similarly packaged distribution files can be found in /dist folder which provides compressed wheels and compressed source code, this was done using setup.py python file which can be found in root directory of this project.
25101

26102
## Details
27-
- author
28-
- license
29-
- link to source
30-
- acknowledgements
103+
- Author - Tamanna Kar
104+
- License - MIT License
105+
- Link to source - https://github.com/TKar9901/UoE-Project---Battleships (private repo)
106+
- Acknowledgements - University of Exeter resources and staff.

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,3 @@
2020
],
2121
python_requires=">=3.6",
2222
)
23-

source/components.py

+101-96
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,130 @@
1-
### components.py
1+
"""
2+
All basic functionality across both web and command-line interfaces are developed
3+
as functions dealing with initialisation and setup of the game as well as interacting
4+
with external files for ships and their placements on the player's board.
5+
6+
Imports random and json are from the Python Standard Library so no installs are needed
7+
and no constants were required for the basic functionality, so simplistic testing was
8+
used to ensure functionality and reliability.
9+
"""
10+
11+
## components.py
212
# imports
313
import random
414
import json
515

616
def print_board(board: list) -> None:
7-
"""to print game board with clarity and consistecy (in testing)
8-
9-
keyword arguments:
10-
board -- 2d list of ship placements to print to console
11-
"""
17+
"""to print game board with clarity and consistency (in testing)
18+
19+
keyword arguments:
20+
21+
board -- 2d list of ship placements to print to console
22+
"""
1223

13-
for row in board:
14-
print(*row, sep="\t")
24+
for row in board:
25+
print(*row, sep="\t")
1526

1627

1728
def initialise_board() -> list:
18-
"""returns 2d array of size*size containing None values
19-
"""
29+
"""returns 2d array of size*size containing None values
30+
"""
2031

21-
with open("config.json", "r") as f:
22-
size = json.load(f)["board_size"]
32+
with open("config.json", mode="r", encoding="utf-8") as config_file:
33+
size = json.load(config_file)["board_size"]
2334

24-
board = [[None for i in range(size)] for j in range(size)]
25-
return board
35+
board = [[None for i in range(size)] for j in range(size)]
36+
return board
2637

2738
# test:
2839
# print_board(initialise_board())
2940

3041

3142
def create_battleships() -> dict:
32-
"""returns dict of ships:size from given file
33-
"""
43+
"""returns dict of {ships:size} from given file
44+
"""
3445

35-
with open("config.json", "r") as f:
36-
filename = json.load(f)["battleship_file"]
46+
with open("config.json", mode="r", encoding="utf-8") as config_file:
47+
filename = json.load(config_file)["battleship_file"]
3748

38-
with open(filename ,"r") as f:
39-
ships = {}
40-
for line in f:
41-
line = line.split(":")
42-
ships[line[0]] = int(line[1])
49+
with open(filename , mode="r", encoding="utf-8") as file_name:
50+
ships = {}
51+
for line in file_name:
52+
line = line.split(":")
53+
ships[line[0]] = int(line[1])
4354

44-
return(ships)
55+
return ships
4556

4657
# test:
4758
# print(create_battleships())
4859

4960

5061
def place_battleships(board: list, ships: dict, method: str="simple") -> list:
51-
"""returns board with placed ships based on given chosen method
52-
53-
keyword arguments:
54-
board -- empty 2d list to add ship placements as given by function initialise_board,
55-
ships -- dictionary of ship names and sizes as given by function create_battleships,
56-
method -- algorithm for assigning board positions to ships, default='simple'
57-
"""
58-
59-
if method=="simple":
60-
i = 0
61-
for ship, size in ships.items():
62-
for n in range(size):
63-
board[i][n] = ship
64-
i += 1
65-
66-
elif method=="random":
67-
for ship, size in ships.items():
68-
placed = False
69-
while not placed:
70-
orien = random.choice("hv")
71-
if orien == "h":
72-
try:
73-
posx = random.randint(0, len(board)-1)
74-
posy = random.randint(0, len(board)-1)
75-
possible = True
76-
for n in range(size):
77-
if board[posy][posx+n]:
78-
possible = False
79-
except:
80-
pass
81-
else:
82-
if possible:
83-
for n in range(size):
84-
board[posy][posx+n] = ship
85-
placed = True
86-
87-
else:
88-
try:
89-
posx = random.randint(0, len(board)-1)
90-
posy = random.randint(0, len(board)-1)
91-
possible = True
92-
for n in range(size):
93-
if board[posy+n][posx]:
94-
possible = False
95-
except:
96-
pass
97-
else:
98-
if possible:
99-
for n in range(size):
100-
board[posy+n][posx] = ship
101-
placed = True
102-
103-
elif method=="custom":
104-
with open("config.json", "r") as f:
105-
filename = json.load(f)["ship_placement_file"]
106-
107-
with open(filename, "r") as f:
108-
placements = json.load(f)
109-
110-
for ship, size in ships.items():
111-
orien = placements[ship][2]
112-
posx, posy = int(placements[ship][0]), int(placements[ship][1])
113-
114-
if orien == "h":
115-
for n in range(size):
116-
board[posy][posx+n] = ship
117-
else:
118-
for n in range(size):
119-
board[posy+n][posx] = ship
120-
121-
122-
return board
62+
"""returns board with placed ships based on given chosen method
63+
64+
keyword arguments:
65+
66+
board -- empty 2d list to add ship placements as given by function initialise_board,
67+
68+
ships -- dictionary of ship names and sizes as given by function create_battleships,
69+
70+
method -- algorithm for assigning board positions to ships, default='simple'
71+
"""
72+
73+
if method=="simple":
74+
i = 0
75+
for ship, size in ships.items():
76+
for size_n in range(size):
77+
board[i][size_n] = ship
78+
i += 1
79+
80+
elif method=="random":
81+
for ship, size in ships.items():
82+
placed = False
83+
while not placed:
84+
orien = random.choice("hv")
85+
posx = random.randint(0, len(board)-1)
86+
posy = random.randint(0, len(board)-1)
87+
possible = True
88+
89+
try:
90+
for size_n in range(size):
91+
if orien == "h":
92+
if board[posy][posx+size_n]:
93+
possible = False
94+
else:
95+
if board[posy+size_n][posx]:
96+
possible = False
97+
except IndexError:
98+
pass
99+
else:
100+
if possible:
101+
for size_n in range(size):
102+
if orien == "h":
103+
board[posy][posx+size_n] = ship
104+
else:
105+
board[posy+size_n][posx] = ship
106+
placed = True
107+
108+
elif method=="custom":
109+
with open("config.json", mode="r", encoding="utf-8") as config_file:
110+
filename = json.load(config_file)["ship_placement_file"]
111+
112+
with open(filename, mode="r", encoding="utf-8") as file_name:
113+
placements = json.load(file_name)
114+
115+
for ship, size in ships.items():
116+
orien = placements[ship][2]
117+
posx, posy = int(placements[ship][0]), int(placements[ship][1])
118+
119+
if orien == "h":
120+
for size_n in range(size):
121+
if orien == "h":
122+
board[posy][posx+size_n] = ship
123+
else:
124+
board[posy+size_n][posx] = ship
125+
126+
127+
return board
123128

124129
# test:
125130
# print_board(place_battleships(initialise_board(), create_battleships(), method="custom"))

0 commit comments

Comments
 (0)