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:
|
``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 |
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
from
pygame.locals
import
*
import
sys
import
argparse
import
comms
import
engine
import
players
import
sprites
...
...
@@ -45,19 +47,42 @@ def main():
# --------- 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
()
my_board
.
initialize
()
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
if
me
.
priority
>
them
.
priority
:
# I go first
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!
winner
=
game
.
play
()
...
...
players/__init__.py
View file @
5988c774
from
.remote
import
Remote
from
.computer
import
Computer
from
.human
import
Human
from
.player
import
Player
players/player_state.py
View file @
5988c774
from
random
import
randint
from
typing
import
List
,
Optional
,
Tuple
from
typing
import
List
,
Optional
,
Tuple
,
Dict
from
ship
import
Ship
from
ship
import
from_dict
as
ship_from_dict
SHIP_TYPES
=
[
2
,
3
,
3
,
4
,
5
]
...
...
@@ -69,6 +70,12 @@ class PlayerState:
return
self
.
_hit
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 ----------
# return True if all of the ships are sunk
...
...
@@ -82,3 +89,43 @@ class PlayerState:
def
sunk_ships
(
self
):
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
:
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)
coordinate of the bow, and whether they are vertical or
horizontal. Two examples are illustrated on the board below:
...
...
@@ -28,7 +31,11 @@ class Ship:
self
.
is_vertical
=
is_vertical
# 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
=
[]
else
:
self
.
_hits
=
hits
def
is_hit
(
self
,
guess
):
# ignore duplicate guesses
...
...
@@ -46,3 +53,21 @@ class Ship:
def
is_sunk
(
self
):
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