Use docstrings

This commit is contained in:
Martin Thoma
2022-04-02 07:58:37 +02:00
parent c500424956
commit 4b3b991273
12 changed files with 268 additions and 323 deletions

View File

@@ -1,17 +1,14 @@
import random
import re
from typing import List, Optional, Tuple
###################
#
# static variables
#
###################
BoardType = List[List[Optional[int]]]
CoordinateType = Tuple[int, int]
BOARD_WIDTH = 10
BOARD_HEIGHT = 10
# game ships
#
# data structure keeping track of information
# about the ships in the game. for each ship,
# the following information is provided:
@@ -35,26 +32,21 @@ VALID_MOVES = [
[1, 0], # South
[1, -1], # South West
[0, -1], # West
[-1, -1],
] # North West
[-1, -1], # North West
]
COORD_REGEX = "[ \t]{0,}(-?[0-9]{1,3})[ \t]{0,},[ \t]{0,}(-?[0-9]{1,2})"
####################
#
# global variables
#
####################
# array of BOARD_HEIGHT arrays, BOARD_WIDTH in length,
# representing the human player and computer
player_board = []
computer_board = []
player_board: BoardType = []
computer_board: BoardType = []
# array representing the coordinates
# for each ship for player and computer
# array is in the same order as SHIPS
computer_ship_coords = []
computer_ship_coords: List[List[CoordinateType]] = []
####################################
@@ -88,10 +80,9 @@ num_player_shots = 7
#
####################################
# flag indicating whose turn
# it currently is
COMPUTER = 0
PLAYER = 1
# flag indicating whose turn it currently is
COMPUTER = False
PLAYER = True
active_turn = COMPUTER
####################
@@ -108,26 +99,27 @@ random.seed()
# random_x_y
#
# generate a valid x,y coordinate on the board
# returns: x,y
# x: integer between 1 and BOARD_HEIGHT
# y: integer between 1 and BOARD WIDTH
def random_x_y():
def random_x_y() -> CoordinateType:
"""Generate a valid x,y coordinate on the board"""
x = random.randrange(1, BOARD_WIDTH + 1)
y = random.randrange(1, BOARD_HEIGHT + 1)
return (x, y)
# input_coord
#
# ask user for single (x,y) coordinate
# validate the coordinates are within the bounds
# of the board width and height. mimic the behavior
# of the original program which exited with error
# messages if coordinates where outside of array bounds.
# if input is not numeric, print error out to user and
# let them try again.
def input_coord():
def input_coord() -> CoordinateType:
"""
Ask user for single (x,y) coordinate
validate the coordinates are within the bounds
of the board width and height. mimic the behavior
of the original program which exited with error
messages if coordinates where outside of array bounds.
if input is not numeric, print error out to user and
let them try again.
"""
match = None
while not match:
coords = input("? ")
@@ -148,25 +140,25 @@ def input_coord():
return x, y
# generate_ship_coordinates
#
# given a ship from the SHIPS array, generate
# the coordinates of the ship. the starting point
# of the ship's first coordinate is generated randomly.
# once the starting coordinates are determined, the
# possible directions of the ship, accounting for the
# edges of the board, are determined. once possible
# directions are found, a direction is randomly
# determined and the remaining coordinates are
# generated by adding or substraction from the starting
# coordinates as determined by direction.
#
# arguments:
# ship - index into the SHIPS array
#
# returns:
# array of sets of coordinates (x,y)
def generate_ship_coordinates(ship):
def generate_ship_coordinates(ship: int) -> List[CoordinateType]:
"""
given a ship from the SHIPS array, generate
the coordinates of the ship. the starting point
of the ship's first coordinate is generated randomly.
once the starting coordinates are determined, the
possible directions of the ship, accounting for the
edges of the board, are determined. once possible
directions are found, a direction is randomly
determined and the remaining coordinates are
generated by adding or substraction from the starting
coordinates as determined by direction.
arguments:
ship - index into the SHIPS array
returns:
array of sets of coordinates (x,y)
"""
# randomly generate starting x,y coordinates
start_x, start_y = random_x_y()
@@ -213,20 +205,13 @@ def generate_ship_coordinates(ship):
return coords
# create_blank_board
#
# helper function to create a game board
# that is blank
def create_blank_board():
return [[None for y in range(BOARD_WIDTH)] for x in range(BOARD_HEIGHT)]
def create_blank_board() -> BoardType:
"""Create a blank game board"""
return [[None for _y in range(BOARD_WIDTH)] for _x in range(BOARD_HEIGHT)]
# print_board
#
# print out the game board for testing
# purposes
def print_board(board) -> None:
def print_board(board: BoardType) -> None:
"""Print out the game board for testing purposes"""
# print board header (column numbers)
print(" ", end="")
for z in range(BOARD_WIDTH):
@@ -243,27 +228,31 @@ def print_board(board) -> None:
print()
# place_ship
#
# place a ship on a given board. updates
# the board's row,column value at the given
# coordinates to indicate where a ship is
# on the board.
#
# inputs: board - array of BOARD_HEIGHT by BOARD_WIDTH
# coords - array of sets of (x,y) coordinates of each
# part of the given ship
# ship - integer repreesnting the type of ship (given in SHIPS)
def place_ship(board, coords, ship):
def place_ship(board: BoardType, coords: List[CoordinateType], ship: int) -> None:
"""
Place a ship on a given board.
updates
the board's row,column value at the given
coordinates to indicate where a ship is
on the board.
inputs: board - array of BOARD_HEIGHT by BOARD_WIDTH
coords - array of sets of (x,y) coordinates of each
part of the given ship
ship - integer representing the type of ship (given in SHIPS)
"""
for coord in coords:
board[coord[0] - 1][coord[1] - 1] = ship
# NOTE: A little quirk that exists here and in the orginal
# game: Ships are allowed to cross each other!
# For example: 2 destroyers, length 2, one at
# [(1,1),(2,2)] and other at [(2,1),(1,2)]
def generate_board():
def generate_board() -> Tuple[BoardType, List[List[CoordinateType]]]:
"""
NOTE: A little quirk that exists here and in the orginal
game: Ships are allowed to cross each other!
For example: 2 destroyers, length 2, one at
[(1,1),(2,2)] and other at [(2,1),(1,2)]
"""
board = create_blank_board()
ship_coords = []
@@ -284,7 +273,9 @@ def generate_board():
return board, ship_coords
def execute_shot(turn, board, x, y, current_turn):
def execute_shot(
turn: bool, board: BoardType, x: int, y: int, current_turn: int
) -> int:
"""
given a board and x, y coordinates,
execute a shot. returns True if the shot
@@ -298,12 +289,8 @@ def execute_shot(turn, board, x, y, current_turn):
return ship_hit
# calculate_shots
#
# function to examine each board
# and determine how many shots remaining
def calculate_shots(board):
def calculate_shots(board: BoardType) -> int:
"""Examine each board and determine how many shots remaining"""
ships_found = [0 for x in range(len(SHIPS))]
for x in range(BOARD_HEIGHT):
for y in range(BOARD_WIDTH):
@@ -318,19 +305,12 @@ def calculate_shots(board):
return shots
# initialize
#
# function to initialize global variables used
# during game play.
def initialize_game():
# initialize the global player and computer
# boards
def initialize_game() -> None:
# initialize the global player and computer boards
global player_board
player_board = create_blank_board()
# generate the ships for the computer's
# board
# generate the ships for the computer's board
global computer_board
global computer_ship_coords
computer_board, computer_ship_coords = generate_board()
@@ -355,8 +335,8 @@ def initialize_game():
ship_coords.append(list)
# add ships to the user's board
for ship in range(len(SHIPS)):
place_ship(player_board, ship_coords[ship], ship)
for ship_index in range(len(SHIPS)):
place_ship(player_board, ship_coords[ship_index], ship_index)
# see if the player wants the computer's ship
# locations printed out and if the player wants to
@@ -366,9 +346,9 @@ def initialize_game():
while input_loop:
player_start = input("DO YOU WANT TO START? ")
if player_start == "WHERE ARE YOUR SHIPS?":
for ship in range(len(SHIPS)):
print(SHIPS[ship][0])
coords = computer_ship_coords[ship]
for ship_index in range(len(SHIPS)):
print(SHIPS[ship_index][0])
coords = computer_ship_coords[ship_index]
for coord in coords:
x = coord[0]
y = coord[1]
@@ -384,14 +364,11 @@ def initialize_game():
print_computer_shots = True
global first_turn
global second_turn
if player_start.lower() != "yes":
first_turn = COMPUTER
second_turn = PLAYER
# calculate the initial number of shots for each
global num_computer_shots
global num_player_shots
global num_computer_shots, num_player_shots
num_player_shots = calculate_shots(player_board)
num_computer_shots = calculate_shots(computer_board)
@@ -407,31 +384,22 @@ def initialize_game():
# forth, replicating the gotos in the original game
# initialize the first_turn function to the
# player's turn
# initialize the first_turn function to the player's turn
first_turn = PLAYER
# initialize the second_turn to the computer's
# turn
second_turn = COMPUTER
def execute_turn(turn: bool, current_turn: int) -> int:
global num_computer_shots, num_player_shots
def execute_turn(turn, current_turn):
global num_computer_shots
global num_player_shots
# print out the number of shots the current
# player has
# print out the number of shots the current player has
board = None
num_shots = 0
if turn == COMPUTER:
print("I HAVE", num_computer_shots, "SHOTS.")
print(f"I HAVE {num_computer_shots} SHOTS.")
board = player_board
num_shots = num_computer_shots
else:
print("YOU HAVE", num_player_shots, "SHOTS.")
print(f"YOU HAVE {num_player_shots} SHOTS.")
board = computer_board
num_shots = num_player_shots
@@ -486,34 +454,22 @@ def execute_turn(turn, current_turn):
def main() -> None:
# keep track of the turn
current_turn = 0
# initialize the player and computer
# boards
initialize_game()
# execute turns until someone wins or we run
# out of squares to shoot
game_over = False
while not game_over:
# increment the turn
current_turn = current_turn + 1
current_turn += 1
print("\n")
print("TURN", current_turn)
# print("computer")
# print_board(computer_board)
# print("player")
# print_board(player_board)
if execute_turn(first_turn, current_turn) == 0:
game_over = True
continue
if execute_turn(second_turn, current_turn) == 0:
if (
execute_turn(first_turn, current_turn) == 0
or execute_turn(not first_turn, current_turn) == 0
):
game_over = True
continue