Commit f4a61829 by source_reader

lab5 start

parent 5988c774
......@@ -69,3 +69,18 @@ with a USB serial connection. Code is required in the following locations:
|``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 |
Lab 5: Playing Remote Games
---------------------------
By the end of this lab you should be able to play a game of battleship against a remote opponent
over a network connection. You should also be able to detect if your opponent cheated. Code is
required in the following locations:
| File Name | Function:Line | Description |
|--------------------|------------------------------|------------------------------------------|
|``engine.py`` | ``play:59`` | add cheat detection for player2 |
|``player_state.py`` | ``to_dict:125`` | add hash to dict encoding |
|``player_state.py`` | ``from_dict:155`` | set hash from dict encoding |
|``player_state.py`` | ``is_valid:155`` | verify PlayerState |
|``comms.py`` | ``host_tcp_server:61`` | host a battleship server |
|``comms.py`` | ``connect_tcp_client:79`` | connect to an existing battleship server |
from typing import Tuple
from typing import Tuple, BinaryIO
from random import randint
import serial
import time
......@@ -51,3 +51,38 @@ def connect_serial_client(port) -> Tuple[int, serial.Serial]:
return 3, None # <-- replace None with the Serial object
# --------- END YOUR CODE ----------
def host_tcp_server(port) -> Tuple[int, BinaryIO]:
"""
Host a battleship server on the specified port on all interfaces (0.0.0.0)
Uses priority 0
"""
# --------- BEGIN YOUR CODE ----------
# create Socket object
# bind to all interfaces (IP address = 0.0.0.0) on the specified port
# wait for a connection from a client
# return the connected socket
return 0, None
# --------- END YOUR CODE ----------
def connect_tcp_client(server_address) -> Tuple[int, BinaryIO]:
"""
Connect to a battleship server specified by ip_address:port
Uses priority 3
"""
# --------- BEGIN YOUR CODE ----------
# create a Socket object
# parse server_address into ip_address and port
# note the ip_address is a string and the port is an int
# connect the socket to the server
return 3, None
# --------- END YOUR CODE ----------
......@@ -42,10 +42,21 @@ class Engine:
# Did Player1 win?
if player2_state.all_sunk():
# share all of Player1's ships with Player2 (both sunk and unsunk)
# this way if Player2 is remote, they can verify that Player1 did not cheat
player1_solved_state = self.player1.get_state()
self.player2.send_state(player1_solved_state, reveal=True)
# verify for ourselves that Player1 did not cheat
if not player1_solved_state.is_valid():
print("Player 1 cheated!")
else:
print("Player1 played a fair game")
return self.player1
# ==== Player2's turn ====
# NOTE: Following the pattern above, implement cheat detection for Player2
# --------- BEGIN YOUR CODE ----------
# Following the pattern above implement the logic for Player2's turn
......
......@@ -49,8 +49,9 @@ def main():
# 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")
parser.add_argument("--type", "-t", choices=['cpu', 'serial-server', 'serial-client',
'tcp-server', 'tcp-client'], default='cpu')
parser.add_argument("--interface", "-i", help="serial device, port or IP address:PORT")
args = parser.parse_args()
# determine the type of opponent
......@@ -62,6 +63,12 @@ def main():
elif args.type == 'serial-client':
(priority, file) = comms.connect_serial_client(args.interface)
them = players.Remote(file, priority=priority)
elif args.type == 'tcp-server':
(priority, file) = comms.host_tcp_server(args.interface)
them = players.Remote(file, priority=priority)
elif args.type == 'tcp-client':
(priority, file) = comms.connect_tcp_client(args.interface)
them = players.Remote(file, priority=priority)
else:
print("Invalid player type")
return
......
from random import randint
from typing import List, Optional, Tuple, Dict
import json
import hashlib
from ship import Ship
from ship import from_dict as ship_from_dict
......@@ -43,6 +45,11 @@ class PlayerState:
pass # <-- remove this!
# --------- END YOUR CODE ----------
# compute the hash of the ships, used for cheat detection
ship_str = json.dumps([ship.to_dict() for ship in self._ships])
ship_bytes = ship_str.encode('ascii')
self._hash = hashlib.sha256(ship_bytes).hexdigest()
"""
Print the board as text, useful for debugging
"""
......@@ -100,6 +107,8 @@ class PlayerState:
# whether or not the last guess was a hit
self._hit = new_state._hit
# NOTE: ignore the hash, it cannot be trusted!
def to_dict(self, reveal=False) -> Dict:
# if reveal is True, encode all ships
if reveal:
......@@ -109,6 +118,8 @@ class PlayerState:
ships = self.sunk_ships()
data = {}
# NOTE: Add 'hash' to the data dictionary
# --------- BEGIN YOUR CODE ----------
# populate data with the following keys:
......@@ -120,9 +131,29 @@ class PlayerState:
return data
def is_valid(self) -> bool:
# --------- BEGIN YOUR CODE ----------
board_matrix: List[List[Optional[Ship]]] = [[None] * 10 for _ in range(10)]
# If any of the following checks fail, print a message and return False
# 1.) Make sure the ships are the right types
# 2.) Make sure ships are in valid locations
# 3.) Make sure the hits and misses are correct
# 4.) Validate board signature (make sure ships have not moved)
return True
# --------- END YOUR CODE ----------
def from_dict(data: Dict) -> PlayerState:
state = PlayerState()
# NOTE: Set the _hash attribute based on the provided data
# --------- BEGIN YOUR CODE ----------
# set the _hit and _ships attributes based on the provided data
......
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