mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 23:00:43 -08:00
MAINT: Apply pre-commit
Remove byte-order-marker pre-commit check as there would be many adjustments necessary
This commit is contained in:
@@ -40,8 +40,9 @@ def place_ship(sea: SeaType, size: int, code: int) -> None:
|
||||
point = add_vector(point, vector)
|
||||
points.append(point)
|
||||
|
||||
if (not all([is_within_sea(point, sea) for point in points]) or
|
||||
any([value_at(point, sea) for point in points])):
|
||||
if not all([is_within_sea(point, sea) for point in points]) or any(
|
||||
[value_at(point, sea) for point in points]
|
||||
):
|
||||
# ship out of bounds or crosses other ship, trying again
|
||||
continue
|
||||
|
||||
@@ -54,7 +55,7 @@ def place_ship(sea: SeaType, size: int, code: int) -> None:
|
||||
|
||||
def print_encoded_sea(sea: SeaType) -> None:
|
||||
for x in range(len(sea)):
|
||||
print(' '.join([str(sea[y][x]) for y in range(len(sea) - 1, -1, -1)]))
|
||||
print(" ".join([str(sea[y][x]) for y in range(len(sea) - 1, -1, -1)]))
|
||||
|
||||
|
||||
def is_within_sea(point: PointType, sea: SeaType) -> bool:
|
||||
@@ -70,18 +71,18 @@ def count_sunk(sea: SeaType, *codes: int) -> int:
|
||||
|
||||
|
||||
def value_at(point: PointType, sea: SeaType) -> int:
|
||||
return sea[point[1] - 1][point[0] -1]
|
||||
return sea[point[1] - 1][point[0] - 1]
|
||||
|
||||
|
||||
def set_value_at(value: int, point: PointType, sea: SeaType) -> None:
|
||||
sea[point[1] - 1][point[0] -1] = value
|
||||
sea[point[1] - 1][point[0] - 1] = value
|
||||
|
||||
|
||||
def get_next_target(sea: SeaType) -> PointType:
|
||||
while True:
|
||||
try:
|
||||
guess = input('? ')
|
||||
point = guess.split(',')
|
||||
guess = input("? ")
|
||||
point = guess.split(",")
|
||||
|
||||
if len(point) != 2:
|
||||
raise ValueError()
|
||||
@@ -93,7 +94,9 @@ def get_next_target(sea: SeaType) -> PointType:
|
||||
|
||||
return point
|
||||
except ValueError:
|
||||
print(f'INVALID. SPECIFY TWO NUMBERS FROM 1 TO {len(sea)}, SEPARATED BY A COMMA.')
|
||||
print(
|
||||
f"INVALID. SPECIFY TWO NUMBERS FROM 1 TO {len(sea)}, SEPARATED BY A COMMA."
|
||||
)
|
||||
|
||||
|
||||
def setup_ships(sea: SeaType):
|
||||
@@ -106,23 +109,27 @@ def setup_ships(sea: SeaType):
|
||||
|
||||
|
||||
def main() -> None:
|
||||
sea = tuple(([0 for _ in range(SEA_WIDTH)] for _ in range(SEA_WIDTH)))
|
||||
sea = tuple([0 for _ in range(SEA_WIDTH)] for _ in range(SEA_WIDTH))
|
||||
setup_ships(sea)
|
||||
print(f'''
|
||||
print(
|
||||
f"""
|
||||
BATTLE
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
|
||||
HAS BEEN CAPTURED BUT NOT DECODED:
|
||||
|
||||
''')
|
||||
"""
|
||||
)
|
||||
print_encoded_sea(sea)
|
||||
print('''
|
||||
print(
|
||||
"""
|
||||
|
||||
DE-CODE IT AND USE IT IF YOU CAN
|
||||
BUT KEEP THE DE-CODING METHOD A SECRET.
|
||||
|
||||
START GAME''')
|
||||
START GAME"""
|
||||
)
|
||||
splashes = 0
|
||||
hits = 0
|
||||
|
||||
@@ -131,33 +138,39 @@ START GAME''')
|
||||
target_value = value_at(target, sea)
|
||||
|
||||
if target_value < 0:
|
||||
print(f'YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT.')
|
||||
print(
|
||||
f"YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT."
|
||||
)
|
||||
|
||||
if target_value <= 0:
|
||||
print('SPLASH! TRY AGAIN.')
|
||||
print("SPLASH! TRY AGAIN.")
|
||||
splashes += 1
|
||||
continue
|
||||
|
||||
print(f'A DIRECT HIT ON SHIP NUMBER {target_value}')
|
||||
print(f"A DIRECT HIT ON SHIP NUMBER {target_value}")
|
||||
hits += 1
|
||||
set_value_at(-target_value, target, sea)
|
||||
|
||||
if not has_ship(sea, target_value):
|
||||
print('AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.')
|
||||
print('SO FAR, THE BAD GUYS HAVE LOST')
|
||||
print(f'{count_sunk(sea, 1, 2)} DESTROYER(S),',
|
||||
f'{count_sunk(sea, 3, 4)} CRUISER(S),',
|
||||
f'AND {count_sunk(sea, 5, 6)} AIRCRAFT CARRIER(S).')
|
||||
print("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.")
|
||||
print("SO FAR, THE BAD GUYS HAVE LOST")
|
||||
print(
|
||||
f"{count_sunk(sea, 1, 2)} DESTROYER(S),",
|
||||
f"{count_sunk(sea, 3, 4)} CRUISER(S),",
|
||||
f"AND {count_sunk(sea, 5, 6)} AIRCRAFT CARRIER(S).",
|
||||
)
|
||||
|
||||
if any(has_ship(sea, code) for code in range(1, 7)):
|
||||
print(f'YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}')
|
||||
print(f"YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}")
|
||||
continue
|
||||
|
||||
print('YOU HAVE TOTALLY WIPED OUT THE BAD GUYS\' FLEET '
|
||||
f'WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}')
|
||||
print(
|
||||
"YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "
|
||||
f"WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}"
|
||||
)
|
||||
|
||||
if not splashes:
|
||||
print('CONGRATULATIONS -- A DIRECT HIT EVERY TIME.')
|
||||
print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.")
|
||||
|
||||
print("\n****************************")
|
||||
break
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from dataclasses import dataclass
|
||||
from random import randrange
|
||||
|
||||
|
||||
DESTROYER_LENGTH = 2
|
||||
CRUISER_LENGTH = 3
|
||||
AIRCRAFT_CARRIER_LENGTH = 4
|
||||
@@ -14,10 +13,10 @@ class Point:
|
||||
y: int
|
||||
|
||||
@classmethod
|
||||
def random(cls, start: int, stop: int) -> 'Point':
|
||||
def random(cls, start: int, stop: int) -> "Point":
|
||||
return Point(randrange(start, stop), randrange(start, stop))
|
||||
|
||||
def __add__(self, vector: 'Vector') -> 'Point':
|
||||
def __add__(self, vector: "Vector") -> "Point":
|
||||
return Point(self.x + vector.x, self.y + vector.y)
|
||||
|
||||
|
||||
@@ -27,10 +26,10 @@ class Vector:
|
||||
y: int
|
||||
|
||||
@staticmethod
|
||||
def random() -> 'Vector':
|
||||
def random() -> "Vector":
|
||||
return Vector(randrange(-1, 2, 2), randrange(-1, 2, 2))
|
||||
|
||||
def __mul__(self, factor: int) -> 'Vector':
|
||||
def __mul__(self, factor: int) -> "Vector":
|
||||
return Vector(self.x * factor, self.y * factor)
|
||||
|
||||
|
||||
@@ -38,25 +37,25 @@ class Sea:
|
||||
WIDTH = 6
|
||||
|
||||
def __init__(self):
|
||||
self._graph = tuple(([0 for _ in range(self.WIDTH)] for _ in range(self.WIDTH)))
|
||||
self._graph = tuple([0 for _ in range(self.WIDTH)] for _ in range(self.WIDTH))
|
||||
|
||||
def _validate_item_indices(self, point: Point) -> None:
|
||||
if not isinstance(point, Point):
|
||||
raise ValueError(f'Sea indices must be Points, not {type(point).__name__}')
|
||||
raise ValueError(f"Sea indices must be Points, not {type(point).__name__}")
|
||||
|
||||
if not((1 <= point.x <= self.WIDTH) and (1 <= point.y <= self.WIDTH)):
|
||||
raise IndexError('Sea index out of range')
|
||||
if not ((1 <= point.x <= self.WIDTH) and (1 <= point.y <= self.WIDTH)):
|
||||
raise IndexError("Sea index out of range")
|
||||
|
||||
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)]`
|
||||
def __getitem__(self, point: Point) -> int:
|
||||
self._validate_item_indices(point)
|
||||
|
||||
return self._graph[point.y - 1][point.x -1]
|
||||
return self._graph[point.y - 1][point.x - 1]
|
||||
|
||||
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)] = 3`
|
||||
def __setitem__(self, point: Point, value: int) -> None:
|
||||
self._validate_item_indices(point)
|
||||
self._graph[point.y - 1][point.x -1] = value
|
||||
self._graph[point.y - 1][point.x - 1] = value
|
||||
|
||||
# Allows us to check if a point exists in the sea for example, `if Point(3,2) in sea:`
|
||||
def __contains__(self, point: Point) -> bool:
|
||||
@@ -70,9 +69,14 @@ class Sea:
|
||||
# Redefines how python will render this object when asked as a str
|
||||
def __str__(self):
|
||||
# Display it encoded
|
||||
return "\n".join([' '.join([str(self._graph[y][x])
|
||||
for y in range(self.WIDTH - 1, -1, -1)])
|
||||
for x in range(self.WIDTH)])
|
||||
return "\n".join(
|
||||
[
|
||||
" ".join(
|
||||
[str(self._graph[y][x]) for y in range(self.WIDTH - 1, -1, -1)]
|
||||
)
|
||||
for x in range(self.WIDTH)
|
||||
]
|
||||
)
|
||||
|
||||
def has_ship(self, ship_code: int) -> bool:
|
||||
return any(ship_code in row for row in self._graph)
|
||||
@@ -96,8 +100,8 @@ class Battle:
|
||||
def _next_target(self) -> Point:
|
||||
while True:
|
||||
try:
|
||||
guess = input('? ')
|
||||
coordinates = guess.split(',')
|
||||
guess = input("? ")
|
||||
coordinates = guess.split(",")
|
||||
|
||||
if len(coordinates) != 2:
|
||||
raise ValueError()
|
||||
@@ -109,11 +113,13 @@ class Battle:
|
||||
|
||||
return point
|
||||
except ValueError:
|
||||
print(f'INVALID. SPECIFY TWO NUMBERS FROM 1 TO {Sea.WIDTH}, SEPARATED BY A COMMA.')
|
||||
print(
|
||||
f"INVALID. SPECIFY TWO NUMBERS FROM 1 TO {Sea.WIDTH}, SEPARATED BY A COMMA."
|
||||
)
|
||||
|
||||
@property
|
||||
def splash_hit_ratio(self) -> str:
|
||||
return f'{self.splashes}/{self.hits}'
|
||||
return f"{self.splashes}/{self.hits}"
|
||||
|
||||
@property
|
||||
def _is_finished(self) -> bool:
|
||||
@@ -126,8 +132,10 @@ class Battle:
|
||||
# Get potential ship points
|
||||
points = [start + vector * i for i in range(size)]
|
||||
|
||||
if not (all([point in self.sea for point in points]) and
|
||||
not any([self.sea[point] for point in points])):
|
||||
if not (
|
||||
all([point in self.sea for point in points])
|
||||
and not any([self.sea[point] for point in points])
|
||||
):
|
||||
# ship out of bounds or crosses other ship, trying again
|
||||
continue
|
||||
|
||||
@@ -137,53 +145,59 @@ class Battle:
|
||||
|
||||
break
|
||||
|
||||
|
||||
def loop(self):
|
||||
while True:
|
||||
target = self._next_target()
|
||||
target_value = self.sea[target]
|
||||
|
||||
if target_value < 0:
|
||||
print(f'YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT.')
|
||||
print(
|
||||
f"YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT."
|
||||
)
|
||||
|
||||
if target_value <= 0:
|
||||
print('SPLASH! TRY AGAIN.')
|
||||
print("SPLASH! TRY AGAIN.")
|
||||
self.splashes += 1
|
||||
continue
|
||||
|
||||
print(f'A DIRECT HIT ON SHIP NUMBER {target_value}')
|
||||
print(f"A DIRECT HIT ON SHIP NUMBER {target_value}")
|
||||
self.hits += 1
|
||||
self.sea[target] = -target_value
|
||||
|
||||
if not self.sea.has_ship(target_value):
|
||||
print('AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.')
|
||||
print("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.")
|
||||
self._display_sunk_report()
|
||||
|
||||
if self._is_finished:
|
||||
self._display_game_end()
|
||||
break
|
||||
|
||||
print(f'YOUR CURRENT SPLASH/HIT RATIO IS {self.splash_hit_ratio}')
|
||||
print(f"YOUR CURRENT SPLASH/HIT RATIO IS {self.splash_hit_ratio}")
|
||||
|
||||
def _display_sunk_report(self):
|
||||
print('SO FAR, THE BAD GUYS HAVE LOST',
|
||||
f'{self.sea.count_sunk(1, 2)} DESTROYER(S),',
|
||||
f'{self.sea.count_sunk(3, 4)} CRUISER(S),',
|
||||
f'AND {self.sea.count_sunk(5, 6)} AIRCRAFT CARRIER(S).')
|
||||
print(
|
||||
"SO FAR, THE BAD GUYS HAVE LOST",
|
||||
f"{self.sea.count_sunk(1, 2)} DESTROYER(S),",
|
||||
f"{self.sea.count_sunk(3, 4)} CRUISER(S),",
|
||||
f"AND {self.sea.count_sunk(5, 6)} AIRCRAFT CARRIER(S).",
|
||||
)
|
||||
|
||||
def _display_game_end(self):
|
||||
print('YOU HAVE TOTALLY WIPED OUT THE BAD GUYS\' FLEET '
|
||||
f'WITH A FINAL SPLASH/HIT RATIO OF {self.splash_hit_ratio}')
|
||||
print(
|
||||
"YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "
|
||||
f"WITH A FINAL SPLASH/HIT RATIO OF {self.splash_hit_ratio}"
|
||||
)
|
||||
|
||||
if not self.splashes:
|
||||
print('CONGRATULATIONS -- A DIRECT HIT EVERY TIME.')
|
||||
print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.")
|
||||
|
||||
print("\n****************************")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
game = Battle()
|
||||
print(f'''
|
||||
print(
|
||||
f"""
|
||||
BATTLE
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
@@ -195,7 +209,8 @@ HAS BEEN CAPTURED BUT NOT DECODED:
|
||||
DE-CODE IT AND USE IT IF YOU CAN
|
||||
BUT KEEP THE DE-CODING METHOD A SECRET.
|
||||
|
||||
START GAME''')
|
||||
START GAME"""
|
||||
)
|
||||
game.loop()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user