Commit 2313150e by source_reader

lab3 start

parent 64f02887
......@@ -33,4 +33,21 @@ Code is required in the following locations:
|``main.py`` |``display_message:73`` | show a message box |
|``utilities.py`` |``draw_ships:18`` | draw ships on a board |
|``player_state.py``| ``place_ships:43`` | randomly place ships |
|``sprites.py`` | ``--global--:28`` | create sprite objects |
\ No newline at end of file
|``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
from pygame.locals import *
import sys
import engine
import players
import sprites
import colors
......@@ -13,11 +14,12 @@ NBLOCKS = 11
TOP_MARGIN = 30
PADDING = 10
screen: pygame.Surface = pygame.display.set_mode(((BLOCK_SIZE * NBLOCKS) * 2 + PADDING * 3,
BLOCK_SIZE * NBLOCKS + TOP_MARGIN + PADDING))
def main():
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)
pygame.display.set_caption('USNA Battleship')
sprites.initialize()
......@@ -48,23 +50,36 @@ def main():
utilities.draw_ships(my_board, me.state.all_ships())
my_board.draw()
them = players.Computer()
their_board.initialize()
their_board.draw()
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
while True:
for event in pygame.event.get():
if event.type == QUIT:
_display_message(screen, "Bye!")
display_message("Bye!")
pygame.display.update()
pygame.time.wait(1000)
pygame.quit()
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
"""
......@@ -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
box = sprites.msg_box.copy()
# save a copy of the original screen
backup = screen.copy()
# --------- BEGIN YOUR CODE ----------
# create a text object with size 42 font of [msg]
......@@ -85,6 +103,13 @@ def _display_message(screen: pygame.Surface, msg: str):
# --------- 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__":
main()
from .computer import Computer
from .human import Human
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
from typing import Tuple
from typing import Tuple, Callable
from .player import Player
from .player_state import PlayerState
import utilities
class Human(Player):
""" A human player"""
......@@ -13,3 +13,28 @@ class Human(Player):
self.state = PlayerState()
# add ships to the board randomly
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
......@@ -9,7 +9,7 @@ class Player:
def send_state(self, state: PlayerState, reveal=False):
pass
def get_guess(self) -> Tuple[int, int]:
def get_guess(self, alert: Callable[[str], None]) -> Tuple[int, int]:
pass
def send_guess(self, guess: Tuple[int, int]):
......
......@@ -56,6 +56,29 @@ class PlayerState:
print("")
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):
return self._ships
def sunk_ships(self):
return [ship for ship in self._ships if ship.is_sunk()]
class Ship:
def __init__(self, length: int, row: int,
......@@ -29,4 +27,22 @@ class Ship:
self.col = col
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 sprites
import ship
import game_board
import sys
from pygame import QUIT
FONT_PATH = 'assets/FutilePro.ttf'
......@@ -21,3 +24,31 @@ def draw_ships(board: game_board.GameBoard, ships: List[ship.Ship]):
pass # <-- remove this
# --------- 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