Commit 2313150e by source_reader

lab3 start

parent 64f02887
...@@ -34,3 +34,20 @@ Code is required in the following locations: ...@@ -34,3 +34,20 @@ Code is required in the following locations:
|``utilities.py`` |``draw_ships:18`` | draw ships on a board | |``utilities.py`` |``draw_ships:18`` | draw ships on a board |
|``player_state.py``| ``place_ships:43`` | randomly place ships | |``player_state.py``| ``place_ships:43`` | randomly place ships |
|``sprites.py`` | ``--global--:28`` | create sprite objects | |``sprites.py`` | ``--global--:28`` | create sprite objects |
Lab 3: Playing the Computer
---------------------------
By the end of this lab you should be able to play a game of battleship against
your computer. Code is required in the following locations:
| File Name | Function:Line | Description |
|--------------------|-------------------|--------------------------------|
|``human.py`` | ``get_guess:26`` | get a guess from the keyboard |
|``computer.py`` | ``__init__:20`` | initialize _unguessed_spaces |
|``computer.py`` | ``get_guess:34`` | make a random guess |
|``engine.py`` | ``play:55`` | run player2's turn |
|``utilities.py`` | ``draw_hits:26`` | add hit sprites to a board |
|``utilities.py`` | ``draw_misses:35``| add miss sprites to a board |
|``player_state.py`` | ``all_sunk:72`` | determine if a player has lost |
|``ship.py`` | ``is_hit:38`` | determine if a ship is hit |
import pygame
import sys
from pygame.locals import *
import players
import utilities
class Engine:
def __init__(self, player1: players.Player, board1, player2: players.Player, board2, alert):
self.player1 = player1
self.board1 = board1
self.player2 = player2
self.board2 = board2
self.alert = alert
def play(self) -> players.Player:
player2_hits = []
player2_misses = []
player1_hits = []
player1_misses = []
while True:
# ==== Player1's turn ====
guess = self.player1.get_guess(self.alert)
self.player2.send_guess(guess)
player2_state = self.player2.get_state()
self.player1.send_state(player2_state)
if player2_state.is_hit():
player2_hits.append(guess)
else:
player2_misses.append(guess)
utilities.draw_ships(self.board2, player2_state.sunk_ships())
utilities.draw_hits(self.board2, player2_hits)
utilities.draw_misses(self.board2, player2_misses)
self.board2.draw()
pygame.display.update()
# Did Player1 win?
if player2_state.all_sunk():
return self.player1
# ==== Player2's turn ====
# --------- BEGIN YOUR CODE ----------
# Following the pattern above implement the logic for Player2's turn
# --------- END YOUR CODE ----------
# process event queue, quit if user clicks 'X' button
# if you don't do this the game will appear frozen
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
...@@ -2,6 +2,7 @@ import pygame ...@@ -2,6 +2,7 @@ import pygame
from pygame.locals import * from pygame.locals import *
import sys import sys
import engine
import players import players
import sprites import sprites
import colors import colors
...@@ -13,11 +14,12 @@ NBLOCKS = 11 ...@@ -13,11 +14,12 @@ NBLOCKS = 11
TOP_MARGIN = 30 TOP_MARGIN = 30
PADDING = 10 PADDING = 10
screen: pygame.Surface = pygame.display.set_mode(((BLOCK_SIZE * NBLOCKS) * 2 + PADDING * 3,
BLOCK_SIZE * NBLOCKS + TOP_MARGIN + PADDING))
def main(): def main():
pygame.init() pygame.init()
screen: pygame.Surface = pygame.display.set_mode(((BLOCK_SIZE * NBLOCKS) * 2 + PADDING * 3,
BLOCK_SIZE * NBLOCKS + TOP_MARGIN + PADDING))
screen.fill(colors.screen_bkgd) screen.fill(colors.screen_bkgd)
pygame.display.set_caption('USNA Battleship') pygame.display.set_caption('USNA Battleship')
sprites.initialize() sprites.initialize()
...@@ -48,23 +50,36 @@ def main(): ...@@ -48,23 +50,36 @@ def main():
utilities.draw_ships(my_board, me.state.all_ships()) utilities.draw_ships(my_board, me.state.all_ships())
my_board.draw() my_board.draw()
them = players.Computer()
their_board.initialize() their_board.initialize()
their_board.draw() their_board.draw()
pygame.display.update() pygame.display.update()
# Create an engine to run the game
game = engine.Engine(me, my_board, them, their_board, display_message)
# Play battleship!
winner = game.play()
# Display the winner in a message box
if winner == me:
display_message(screen, "You Win!")
elif winner == them:
display_message(screen, "You Lose!")
# wait until the user closes the game # wait until the user closes the game
while True: while True:
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == QUIT: if event.type == QUIT:
_display_message(screen, "Bye!") display_message("Bye!")
pygame.display.update() pygame.display.update()
pygame.time.wait(1000) pygame.time.wait(1000)
pygame.quit() pygame.quit()
sys.exit() sys.exit()
def _display_message(screen: pygame.Surface, msg: str): def display_message(msg: str):
""" """
Display [msg] in the message box sprite in the center of the screen Display [msg] in the message box sprite in the center of the screen
""" """
...@@ -72,6 +87,9 @@ def _display_message(screen: pygame.Surface, msg: str): ...@@ -72,6 +87,9 @@ def _display_message(screen: pygame.Surface, msg: str):
# make a copy of the msg_box sprite because we need to edit it # make a copy of the msg_box sprite because we need to edit it
box = sprites.msg_box.copy() box = sprites.msg_box.copy()
# save a copy of the original screen
backup = screen.copy()
# --------- BEGIN YOUR CODE ---------- # --------- BEGIN YOUR CODE ----------
# create a text object with size 42 font of [msg] # create a text object with size 42 font of [msg]
...@@ -85,6 +103,13 @@ def _display_message(screen: pygame.Surface, msg: str): ...@@ -85,6 +103,13 @@ def _display_message(screen: pygame.Surface, msg: str):
# --------- END YOUR CODE ---------- # --------- END YOUR CODE ----------
# wait 1 second
pygame.time.wait(1000)
# restore the original screen
screen.blit(backup, (0, 0))
pygame.display.update()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
from .computer import Computer
from .human import Human from .human import Human
from .player import Player from .player import Player
from random import randint
import time
from typing import List, Tuple, Callable
from .player import Player
from .player_state import PlayerState
class Computer(Player):
""" A computer player"""
def __init__(self):
self.priority = 1
# list of board spaces that have not been guessed
# eg: _unguessed_spaces = [(0,0), (0,1), (0,2),...]
self._unguessed_spaces = []
# --------- BEGIN YOUR CODE ----------
# Populate _unguessed_spaces with every possible board space
# Hint: use two nested for loops
# --------- END YOUR CODE ----------
self.state = PlayerState()
# the debug flag prints ship locations to the terminal
self.state.place_ships(debug=False)
def get_state(self):
return self.state
def get_guess(self, alert: Callable[[str], None]) -> Tuple[int, int]:
# --------- BEGIN YOUR CODE ----------
# guess a random entry from _unguessed_spaces
# remove the guess from _unguessed_spaces
# enforce a short delay to make the computer
# appear to "think" about its guess
time.sleep(0.5)
return 0, 0 # <-- remove this!
# --------- END YOUR CODE ----------
def send_guess(self, guess: Tuple[int, int]):
self.state.add_guess(guess)
import string import string
from typing import Tuple from typing import Tuple, Callable
from .player import Player from .player import Player
from .player_state import PlayerState from .player_state import PlayerState
import utilities
class Human(Player): class Human(Player):
""" A human player""" """ A human player"""
...@@ -13,3 +13,28 @@ class Human(Player): ...@@ -13,3 +13,28 @@ class Human(Player):
self.state = PlayerState() self.state = PlayerState()
# add ships to the board randomly # add ships to the board randomly
self.state.place_ships() self.state.place_ships()
def get_state(self, all_ships=False):
return self.state
def get_guess(self, alert: Callable[[str], None]) -> Tuple[int, int]:
"""
Get a row,column guess from the user. The user should enter a lower case letter
followed by a number.
"""
# --------- BEGIN YOUR CODE ----------
# Use the get_key_input from the utilities module to read
# keyboard input, return a numeric tuple indicating the coordinates (row,col) of the user's guess
# If the user enters anything besides a-f for the row, use the alert function
# to display "bad row" and let the user make a new guess
# If the user enters anything besides 0-9 for the column, use the alert function
# to display "bad column" and let the user make a new guess (starting with the row)
return 0, 0 # <-- remove this!
# --------- END YOUR CODE ----------
def send_guess(self, guess: Tuple[int, int]):
self.state.add_guess(guess)
from typing import Tuple from typing import Tuple, Callable
from .player_state import PlayerState from .player_state import PlayerState
...@@ -9,7 +9,7 @@ class Player: ...@@ -9,7 +9,7 @@ class Player:
def send_state(self, state: PlayerState, reveal=False): def send_state(self, state: PlayerState, reveal=False):
pass pass
def get_guess(self) -> Tuple[int, int]: def get_guess(self, alert: Callable[[str], None]) -> Tuple[int, int]:
pass pass
def send_guess(self, guess: Tuple[int, int]): def send_guess(self, guess: Tuple[int, int]):
......
...@@ -56,6 +56,29 @@ class PlayerState: ...@@ -56,6 +56,29 @@ class PlayerState:
print("") print("")
print("=" * 10) print("=" * 10)
def add_guess(self, guess):
for ship in self._ships:
if ship.is_hit(guess):
self._hits.append(guess)
self._hit = True
return
self._misses.append(guess)
self._hit = False
def is_hit(self) -> bool:
return self._hit
def all_sunk(self) -> bool:
# --------- BEGIN YOUR CODE ----------
# return True if all of the ships are sunk
# return False otherwise
return False # <-- remove this!
# --------- END YOUR CODE ----------
def all_ships(self): def all_ships(self):
return self._ships return self._ships
def sunk_ships(self):
return [ship for ship in self._ships if ship.is_sunk()]
class Ship: class Ship:
def __init__(self, length: int, row: int, def __init__(self, length: int, row: int,
...@@ -29,4 +27,22 @@ class Ship: ...@@ -29,4 +27,22 @@ class Ship:
self.col = col self.col = col
self.is_vertical = is_vertical self.is_vertical = is_vertical
# keep track of which parts of the ship are hit
self._hits = []
def is_hit(self, guess):
# ignore duplicate guesses
if guess in self._hits:
return True
# --------- BEGIN YOUR CODE ----------
# check if the guess corresponds to part of the ship
# if so add the guess to _hits and return True
# otherwise return False
return False # <-- remove this!
# --------- END YOUR CODE ----------
def is_sunk(self):
return len(self._hits) == self.length
from typing import List from typing import List, Tuple
import pygame import pygame
import sprites import sprites
import ship import ship
import game_board import game_board
import sys
from pygame import QUIT
FONT_PATH = 'assets/FutilePro.ttf' FONT_PATH = 'assets/FutilePro.ttf'
...@@ -21,3 +24,31 @@ def draw_ships(board: game_board.GameBoard, ships: List[ship.Ship]): ...@@ -21,3 +24,31 @@ def draw_ships(board: game_board.GameBoard, ships: List[ship.Ship]):
pass # <-- remove this pass # <-- remove this
# --------- END YOUR CODE ---------- # --------- END YOUR CODE ----------
def draw_hits(board: game_board.GameBoard, hits: List[Tuple[int, int]]):
# --------- BEGIN YOUR CODE ----------
# add a "hit" sprite at every hit
pass # <-- remove this
# --------- END YOUR CODE ----------
def draw_misses(board: game_board.GameBoard, misses: List[Tuple[int, int]]):
# --------- BEGIN YOUR CODE ----------
# add a "miss" sprite at every miss
pass # <-- remove this
# --------- END YOUR CODE ----------
def get_key_input() -> str:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
return event.unicode
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment