Compare commits

...

2 Commits

Author SHA1 Message Date
a28dfac7a8 Side-by-side board display 2022-04-25 18:22:08 -07:00
335836b03e fix AI bug 2022-04-25 17:41:57 -07:00
5 changed files with 57 additions and 16 deletions

View File

@@ -60,7 +60,9 @@ class AIPlayer(Player):
def guess(self, opponent: Player) -> Coordinate: def guess(self, opponent: Player) -> Coordinate:
for guess in self.guesses: for guess in self.guesses:
if self.coord_status(guess, opponent) == CoordStatus.HIT: if self.coord_status(guess, opponent) == CoordStatus.HIT:
return self.guess_near(guess, opponent) candidate = self.guess_near(guess, opponent)
if candidate:
return candidate
while True: while True:
guess = random.choice(opponent.board.columns), random.choice(opponent.board.rows) guess = random.choice(opponent.board.columns), random.choice(opponent.board.rows)

View File

@@ -4,15 +4,30 @@ from typing import List
from .player import Player from .player import Player
from .ship import ShipType from .ship import ShipType
from .ui import clear_screen, format_board, opponent_cell_fn from .ui import clear_screen, opponent_cell_fn, format_boards
def show_boards(players: List[Player], active: Player):
players = list(sorted(players, key=lambda p: p.name))
active_idx = players.index(active)
cell_fns = [
opponent_cell_fn(players[0], players[1], players[0].last_guess),
opponent_cell_fn(players[1], players[0], players[1].last_guess)
]
boards = [players[1].board, players[0].board]
titles = [player.name for player in players]
titles[active_idx] = f"[ {titles[active_idx]} ]"
print(format_boards(cell_fns, boards, titles))
def play_turn(player: Player, opponent: Player): def play_turn(player: Player, opponent: Player):
guess = player.guess(opponent) guess = player.guess(opponent)
player.last_guess = guess
player.guesses.add(guess) player.guesses.add(guess)
clear_screen() clear_screen()
print(format_board(opponent_cell_fn(player, opponent, guess), opponent.board)) show_boards([player, opponent], player)
print() print()
ship = opponent.ship_at(guess) ship = opponent.ship_at(guess)
@@ -23,8 +38,7 @@ def play_turn(player: Player, opponent: Player):
else: else:
print(f"{player.name}: Hit!") print(f"{player.name}: Hit!")
print("\n") input("\nPress ENTER to continue.")
input("Press ENTER to continue.")
def play_battleship(player_1: Player, player_2: Player, ship_types: List[ShipType]): def play_battleship(player_1: Player, player_2: Player, ship_types: List[ShipType]):

View File

@@ -5,8 +5,7 @@ from typing import List
from .bs_types import Coordinate from .bs_types import Coordinate
from .player import Player, PlacementError from .player import Player, PlacementError
from .ship import Ship, ShipType from .ship import Ship, ShipType
from .ui import read_coordinate, format_board, player_cell_fn, read_orientation, opponent_cell_fn, \ from .ui import read_coordinate, format_board, player_cell_fn, read_orientation, clear_screen
clear_screen
class HumanPlayer(Player): class HumanPlayer(Player):
@@ -17,7 +16,7 @@ class HumanPlayer(Player):
clear_screen() clear_screen()
def place_ship(self, ship_type: ShipType) -> Ship: def place_ship(self, ship_type: ShipType) -> Ship:
current_board = format_board(player_cell_fn(self), self.board) current_board = format_board(player_cell_fn(self), self.board, self.name)
while True: while True:
clear_screen() clear_screen()
@@ -40,12 +39,7 @@ class HumanPlayer(Player):
input() input()
def guess(self, opponent: Player) -> Coordinate: def guess(self, opponent: Player) -> Coordinate:
current_board = format_board(opponent_cell_fn(self, opponent), opponent.board)
while True: while True:
clear_screen()
print(current_board)
print()
coord = read_coordinate(f"{self.name}, where would you like to aim your cannons? ", opponent.board) coord = read_coordinate(f"{self.name}, where would you like to aim your cannons? ", opponent.board)
if coord not in self.guesses: if coord not in self.guesses:

View File

@@ -20,6 +20,7 @@ class Player:
self.board = board self.board = board
self.guesses: Set[Coordinate] = set() self.guesses: Set[Coordinate] = set()
self.ships: List[Ship] = [] self.ships: List[Ship] = []
self.last_guess: Optional[Coordinate] = None
def place_ships(self, ship_types: List[ShipType]): def place_ships(self, ship_types: List[ShipType]):
for ship_type in ship_types: for ship_type in ship_types:

View File

@@ -1,9 +1,10 @@
from __future__ import annotations from __future__ import annotations
import itertools
import os import os
import re import re
from string import ascii_uppercase from string import ascii_uppercase
from typing import Optional from typing import Optional, List
from .board import Board from .board import Board
from .ship import Ship, ShipOrientation, ShipType from .ship import Ship, ShipOrientation, ShipType
@@ -95,12 +96,20 @@ def opponent_cell_fn(player, opponent, latest: Optional[Coordinate] = None) -> C
return _cell return _cell
def format_board(cell_fn: CellFn, board: Board, row_separator="-", column_separator="|", intersection="+") -> str: def format_board(
cell_fn: CellFn,
board: Board,
title: str,
row_separator="-",
column_separator="|",
intersection="+"
) -> str:
""" """
Formats the current board state as a string. Formats the current board state as a string.
:param cell_fn: A rendering function that takes a cell coordinate and returns a string. :param cell_fn: A rendering function that takes a cell coordinate and returns a string.
:param board: The board we're rendering. :param board: The board we're rendering.
:param title: A title to place at the top
:param row_separator: A character to render between rows. :param row_separator: A character to render between rows.
:param column_separator: A character to render between columns. :param column_separator: A character to render between columns.
:param intersection: A character to render at intersections. :param intersection: A character to render at intersections.
@@ -109,8 +118,8 @@ def format_board(cell_fn: CellFn, board: Board, row_separator="-", column_separa
y_label_width = len(str(board.width+1)) y_label_width = len(str(board.width+1))
cell_width = 3 cell_width = 3
rendered_board = ""
divider = " " * y_label_width + column_separator + (row_separator*cell_width + intersection) * board.width + "\n" divider = " " * y_label_width + column_separator + (row_separator*cell_width + intersection) * board.width + "\n"
rendered_board = title.center(len(divider)) + "\n"
# Render the header with tickmarks for the X axis # Render the header with tickmarks for the X axis
# We use letters to represent the X axis so that each header will always be a single cell # We use letters to represent the X axis so that each header will always be a single cell
@@ -137,6 +146,27 @@ def format_board(cell_fn: CellFn, board: Board, row_separator="-", column_separa
return rendered_board return rendered_board
def format_boards(
cell_fns: List[CellFn],
boards: List[Board],
titles: List[str],
row_separator="-",
column_separator="|",
intersection="+",
padding=4,
) -> str:
rendered_boards = [format_board(fn, board, title, row_separator, column_separator, intersection).splitlines(keepends=False)
for fn, board, title in zip(cell_fns, boards, titles)]
max_row_length = max(len(row) for row in itertools.chain(*rendered_boards))
spaces = " " * padding
merged_rows = [spaces.join(row.ljust(max_row_length) for row in rows)
for rows in zip(*rendered_boards)]
render = "\n".join(merged_rows)
return render
def print_test_board(): def print_test_board():
from .player import Player from .player import Player
board = Board(7, 7) board = Board(7, 7)