Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
John Donnal
/
battleship
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
5988c774
authored
Oct 01, 2020
by
source_reader
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
lab4 start
parent
2313150e
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
251 additions
and
3 deletions
README.md
comms.py
main.py
players/__init__.py
players/player_state.py
players/remote.py
ship.py
README.md
View file @
5988c774
...
@@ -51,3 +51,21 @@ your computer. Code is required in the following locations:
...
@@ -51,3 +51,21 @@ your computer. Code is required in the following locations:
|
``utilities.py``
|
``draw_misses:35``
| add miss 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 |
|
``player_state.py``
|
``all_sunk:72``
| determine if a player has lost |
|
``ship.py``
|
``is_hit:38``
| determine if a ship is hit |
|
``ship.py``
|
``is_hit:38``
| determine if a ship is hit |
Lab 4: Playing Local Games
--------------------------
By the end of this lab you should be able to play a game of battleship against a local opponent
with a USB serial connection. Code is required in the following locations:
| File Name | Function:Line | Description |
|--------------------|------------------------------|------------------------------------------|
|
``remote.py``
|
``get_guess:18``
| get a guess from another computer |
|
``remote.py``
|
``send_guess:31``
| send a guess to another computer |
|
``remote.py``
|
``get_state:44``
| get a PlayerState from another computer |
|
``remote.py``
|
``send_state:65``
| send a PlayerState to another computer |
|
``comms.py``
|
``connect_serial_client:44``
| connect to an existing battleship server |
|
``player_state.py``
|
``to_dict:106``
| encode PlayerState as a dictionary |
|
``player_state.py``
|
``from_dict:119``
| create PlayerState from a dictionary |
|
``player_state.py``
|
``all_sunk:72``
| update logic to handle remote players |
|
``ship.py``
|
``to_dict:58``
| encode Ship as a dictionary |
|
``ship.py``
|
``from_dict:67``
| create a Ship from a dictionary |
comms.py
0 → 100644
View file @
5988c774
from
typing
import
Tuple
from
random
import
randint
import
serial
import
time
def
host_serial_server
(
port
)
->
Tuple
[
int
,
serial
.
Serial
]:
"""
Host a battleship server on the specified serial port
Uses priority 0
"""
# create a Serial object associated with the specified port
device
=
serial
.
Serial
(
port
)
# send synchronization messages until a response is received
# to connect the client waits until it sees a message and then repeats it back to the server
# note: this is not fool proof, if the client does not reply correctly we just crash
while
True
:
# synchronization is a random number between 0-1000
server_syn
=
randint
(
0
,
1000
)
server_syn_str
=
"
%
d
\n
"
%
server_syn
server_syn_bytes
=
server_syn_str
.
encode
(
'ascii'
)
device
.
write
(
server_syn_bytes
)
# wait to see if a client has replied
time
.
sleep
(
0.5
)
if
device
.
in_waiting
>
0
:
# yes, a client replied, validate the message
client_ack_bytes
=
device
.
readline
()
client_ack_str
=
client_ack_bytes
.
decode
(
'ascii'
)
client_ack
=
int
(
client_ack_str
)
if
client_ack
==
server_syn
:
# valid acknowledgement?
return
0
,
device
else
:
# corrupt response, client is not synchronized
raise
Exception
(
"ERROR: client did not ack"
)
def
connect_serial_client
(
port
)
->
Tuple
[
int
,
serial
.
Serial
]:
"""
Connect to a battleship server on the specified serial port
Uses priority 3
"""
# --------- BEGIN YOUR CODE ----------
# create a Serial object associated with the specified port
# listen for a synchronize message
# acknowledge the synchronize message
return
3
,
None
# <-- replace None with the Serial object
# --------- END YOUR CODE ----------
main.py
View file @
5988c774
import
pygame
import
pygame
from
pygame.locals
import
*
from
pygame.locals
import
*
import
sys
import
sys
import
argparse
import
comms
import
engine
import
engine
import
players
import
players
import
sprites
import
sprites
...
@@ -45,19 +47,42 @@ def main():
...
@@ -45,19 +47,42 @@ def main():
# --------- END YOUR CODE ----------
# --------- END YOUR CODE ----------
# parse command line arguments
parser
=
argparse
.
ArgumentParser
(
"Battleship Simulator"
)
parser
.
add_argument
(
"--type"
,
"-t"
,
choices
=
[
'cpu'
,
'serial-server'
,
'serial-client'
],
default
=
'cpu'
)
parser
.
add_argument
(
"--interface"
,
"-i"
,
help
=
"serial device"
)
args
=
parser
.
parse_args
()
# determine the type of opponent
if
args
.
type
==
'cpu'
:
them
=
players
.
Computer
()
elif
args
.
type
==
'serial-server'
:
(
priority
,
file
)
=
comms
.
host_serial_server
(
args
.
interface
)
them
=
players
.
Remote
(
file
,
priority
=
priority
)
elif
args
.
type
==
'serial-client'
:
(
priority
,
file
)
=
comms
.
connect_serial_client
(
args
.
interface
)
them
=
players
.
Remote
(
file
,
priority
=
priority
)
else
:
print
(
"Invalid player type"
)
return
me
=
players
.
Human
()
me
=
players
.
Human
()
my_board
.
initialize
()
my_board
.
initialize
()
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
# Create an engine to run the game
if
me
.
priority
>
them
.
priority
:
# I go first
game
=
engine
.
Engine
(
me
,
my_board
,
them
,
their_board
,
display_message
)
game
=
engine
.
Engine
(
me
,
my_board
,
them
,
their_board
,
display_message
)
else
:
# They go first
game
=
engine
.
Engine
(
them
,
their_board
,
me
,
my_board
,
display_message
)
# Play battleship!
# Play battleship!
winner
=
game
.
play
()
winner
=
game
.
play
()
...
...
players/__init__.py
View file @
5988c774
from
.remote
import
Remote
from
.computer
import
Computer
from
.computer
import
Computer
from
.human
import
Human
from
.human
import
Human
from
.player
import
Player
from
.player
import
Player
players/player_state.py
View file @
5988c774
from
random
import
randint
from
random
import
randint
from
typing
import
List
,
Optional
,
Tuple
from
typing
import
List
,
Optional
,
Tuple
,
Dict
from
ship
import
Ship
from
ship
import
Ship
from
ship
import
from_dict
as
ship_from_dict
SHIP_TYPES
=
[
2
,
3
,
3
,
4
,
5
]
SHIP_TYPES
=
[
2
,
3
,
3
,
4
,
5
]
...
@@ -69,6 +70,12 @@ class PlayerState:
...
@@ -69,6 +70,12 @@ class PlayerState:
return
self
.
_hit
return
self
.
_hit
def
all_sunk
(
self
)
->
bool
:
def
all_sunk
(
self
)
->
bool
:
# NOTE: Modify the logic below to check if BOTH
# 1.) All of the ships are present in _ships
# 2.) All of the ships in _ships are sunk
# Remote players only send their sunk ships which means it is not sufficient
# to check if all the Ship objects in _ships are sunk
# --------- BEGIN YOUR CODE ----------
# --------- BEGIN YOUR CODE ----------
# return True if all of the ships are sunk
# return True if all of the ships are sunk
...
@@ -82,3 +89,43 @@ class PlayerState:
...
@@ -82,3 +89,43 @@ class PlayerState:
def
sunk_ships
(
self
):
def
sunk_ships
(
self
):
return
[
ship
for
ship
in
self
.
_ships
if
ship
.
is_sunk
()]
return
[
ship
for
ship
in
self
.
_ships
if
ship
.
is_sunk
()]
def
update
(
self
,
new_state
:
'PlayerState'
):
# update our attributes based on new_state
# new_state is received from a remote computer
# add any newly sunken ships
self
.
_ships
=
new_state
.
_ships
# whether or not the last guess was a hit
self
.
_hit
=
new_state
.
_hit
def
to_dict
(
self
,
reveal
=
False
)
->
Dict
:
# if reveal is True, encode all ships
if
reveal
:
ships
=
self
.
_ships
# otherwise only encode the sunk ships
else
:
ships
=
self
.
sunk_ships
()
data
=
{}
# --------- BEGIN YOUR CODE ----------
# populate data with the following keys:
# 'ships': an array of ships encoded as dicts
# 'hit': the value of _hit
# --------- END YOUR CODE ----------
return
data
def
from_dict
(
data
:
Dict
)
->
PlayerState
:
state
=
PlayerState
()
# --------- BEGIN YOUR CODE ----------
# set the _hit and _ships attributes based on the provided data
# --------- END YOUR CODE ----------
return
state
players/remote.py
0 → 100644
View file @
5988c774
import
json
from
typing
import
Tuple
,
Callable
from
.player
import
Player
from
.player_state
import
PlayerState
,
from_dict
class
Remote
(
Player
):
""" A remote player"""
def
__init__
(
self
,
pipe
,
priority
):
# whether this player goes first or second
self
.
priority
=
priority
self
.
pipe
=
pipe
self
.
_state
=
None
def
get_guess
(
self
,
alert
:
Callable
[[
str
],
None
])
->
Tuple
[
int
,
int
]:
# --------- BEGIN YOUR CODE ----------
# read message (blocks until a complete message arrives)
# convert binary data into a JSON string
# convert the JSON string into a guess
# return the guess
return
0
,
0
# <-- remove this!
# --------- END YOUR CODE ----------
def
send_guess
(
self
,
guess
:
Tuple
[
int
,
int
]):
# --------- BEGIN YOUR CODE ----------
# encode guess as json string and append a '\n' to terminate the message
# convert string into binary data
# send binary data (make sure to flush the pipe)
pass
# <-- remove this!
# --------- END YOUR CODE ----------
def
get_state
(
self
)
->
PlayerState
:
# --------- BEGIN YOUR CODE ----------
# read message (blocks until a complete message arrives)
# convert binary data into a JSON string
# convert the JSON string into a dictionary
# convert the dictionary into a PlayerState
# if _state is None, replace it with the new state
# otherwise update _state with the new state
# --------- END YOUR CODE ----------
return
self
.
_state
def
send_state
(
self
,
state
:
PlayerState
,
reveal
=
False
):
# --------- BEGIN YOUR CODE ----------
# encode the state as a dict
# convert the dict into a JSON string and append a '\n' to terminate the message
# convert the message string into binary data
# send binary data (make sure to flush the pipe)
pass
# <-- remove this!
# --------- END YOUR CODE ----------
def
close
(
self
):
self
.
pipe
.
close
()
ship.py
View file @
5988c774
from
typing
import
Dict
,
Optional
,
List
class
Ship
:
class
Ship
:
def
__init__
(
self
,
length
:
int
,
row
:
int
,
def
__init__
(
self
,
length
:
int
,
row
:
int
,
col
:
int
,
is_vertical
:
bool
):
col
:
int
,
is_vertical
:
bool
,
hits
:
Optional
[
List
]
=
None
):
""" Ships are specified by their length, the (row,col)
""" Ships are specified by their length, the (row,col)
coordinate of the bow, and whether they are vertical or
coordinate of the bow, and whether they are vertical or
horizontal. Two examples are illustrated on the board below:
horizontal. Two examples are illustrated on the board below:
...
@@ -28,7 +31,11 @@ class Ship:
...
@@ -28,7 +31,11 @@ class Ship:
self
.
is_vertical
=
is_vertical
self
.
is_vertical
=
is_vertical
# keep track of which parts of the ship are hit
# keep track of which parts of the ship are hit
# the hits parameter is provided when creating a ship with from_dict (see below)
if
hits
is
None
:
self
.
_hits
=
[]
self
.
_hits
=
[]
else
:
self
.
_hits
=
hits
def
is_hit
(
self
,
guess
):
def
is_hit
(
self
,
guess
):
# ignore duplicate guesses
# ignore duplicate guesses
...
@@ -46,3 +53,21 @@ class Ship:
...
@@ -46,3 +53,21 @@ class Ship:
def
is_sunk
(
self
):
def
is_sunk
(
self
):
return
len
(
self
.
_hits
)
==
self
.
length
return
len
(
self
.
_hits
)
==
self
.
length
def
to_dict
(
self
)
->
Dict
:
# --------- BEGIN YOUR CODE ----------
# populate data with the following keys based on our attributes:
# 'length', 'row', 'col', 'is_vertical', 'hits'
return
{}
# <-- replace with your code
# --------- END YOUR CODE ----------
def
from_dict
(
data
:
Dict
):
# --------- BEGIN YOUR CODE ----------
# create a Ship based off the data provided, make sure to include the hits!
pass
# <-- remove this!
# --------- END YOUR CODE ----------
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment