def parse_and_decode(cls, data: bytes) -> "InvalidMove": if len(data) < cls.calc_size() + 1: raise NotEnoughData() if data[0] != cls.type: raise InvalidType() res = unpack('<' + cls.fmt, data[1:cls.calc_size() + 1]) return cls(Move.from_bytes(res[0]), Board.from_bytes(res[1]))
def test_get_required_moves(self): b = Board([BoardLocation(False, False, False)] * 64) b[0, 2] = BoardLocation(True, True, True) b[1, 3] = BoardLocation(True, True, False) self.assertListEqual([Move(0, 2, Direction.Positive, Direction.Positive)], b.get_required_moves(Direction.Positive, True)) self.assertListEqual([], b.get_required_moves(Direction.Positive, False))
def __init__(self, player_one: "Session", player_two: "Session", logger: Logger): """ A Game takes two players, assign the one with lower rating to be player one and go first Also generate a default board start :param player_one: The first player in the game :param player_two: The second player in the game """ if player_one.username == player_two.username: logger.warning( "Attempting to start game between two of {user}".format( user=player_one.username)) raise InvalidGameException() if player_one.rating < player_two.rating: self.__player_one = player_one self.__player_two = player_two else: self.__player_one = player_two self.__player_two = player_one self.__logger = logger self.__board = Board.generate_game_start() self.game_active = True player_one.on_game_start(player_two.username, player_two.rating, self) player_two.on_game_start(player_one.username, player_one.rating, self) self.__next_to_go = self.__player_one # Request a move from the first player self.__next_to_go.on_request_move( Move(0, 0, Direction.Negative, Direction.Negative), self.__board)
def get_move(self) -> Move: def get_location(): try: x = int(input("X value for piece to move:")) y = int(input("Y value for piece to move:")) return x - 1, y - 1 except ValueError: self.display_message("Please enter values from 1-8") return get_location() def get_direction(): def get_x_dir(): first_input = input("Would you like to move the piece left or right? (left/right): ") if first_input == "left": return Direction.Negative elif first_input == "right": return Direction.Positive else: return get_x_dir() def get_y_dir(): first_input = input("Would you like to move the piece up or down? (up/down): ") if first_input == "up": return Direction.Negative elif first_input == "down": return Direction.Positive else: return get_y_dir() return get_x_dir(), get_y_dir() return Move(*get_location(), *get_direction())
def test_game_over(self): b = Board([BoardLocation(False, False, False)] * 64) b[0, 2] = BoardLocation(True, True, True) b[1, 3] = BoardLocation(True, True, False) self.assertFalse(b.check_game_over(True)) self.assertFalse(b.check_game_over(False)) b.apply_move(Move(0, 2, Direction.Positive, Direction.Positive), Direction.Negative, True) self.assertTrue(b.check_game_over(True)) self.assertFalse(b.check_game_over(False))
def __msg_on_processing_game_state(self, msg: Message): """ Logic to do while in the Processing Game State state :param msg: Message received """ if isinstance(msg, YourTurn): # This is the edge of the DFA from Processing Game State -> User Move (Your Turn) self.__protocol_state = ProtocolState.USER_MOVE # Check if this is the beginning of the game if msg.last_move == Move(0, 0, Direction.Negative, Direction.Negative): self.__ui.display_message("You go first!") else: self.__ui.display_message("Last Move was : {move}".format( move=msg.last_move.__repr__())) self.__ui.display(msg.board) self.__send(MakeMove(self.__ui.get_move())) self.__protocol_state = ProtocolState.PROCESSING_GAME_STATE elif isinstance(msg, CompulsoryMove): # This is the edge of the DFA from Processing Game State -> Processing Game State (Compulsory Move) self.__ui.display_message( "Compulsory Move: {move}".format(move=msg.move.__repr__())) self.__ui.display(msg.board) elif isinstance(msg, InvalidMove): # This is the edge of the DFA from Processing Game State -> User Move (Invalid Move) self.__protocol_state = ProtocolState.USER_MOVE self.__ui.display_message( "{move} Is an invalid move".format(move=msg.move.__repr__())) self.__ui.display(msg.board) self.__send(MakeMove(self.__ui.get_move())) self.__protocol_state = ProtocolState.PROCESSING_GAME_STATE elif isinstance(msg, GameOver): # This is the edge of the DFA from Processing Game State -> Game End (Game Over) self.__protocol_state = ProtocolState.GAME_END self.__ui.game_over(msg.board, msg.old_rating, msg.new_rating, msg.you_won != 0) if self.__ui.prompt_play_again(): self.__protocol_state = ProtocolState.IN_QUEUE self.__send(ReQueue()) else: self.shutdown() elif isinstance(msg, OpponentDisconnect): # This is the edge of the DFA from Processing Game State -> Game End (Opponent Disconnect) self.__protocol_state = ProtocolState.GAME_END self.__ui.opponent_left() if self.__ui.prompt_play_again(): self.__protocol_state = ProtocolState.IN_QUEUE self.__send(ReQueue()) else: self.shutdown() else: self.__logger.fatal( "Received invalid message of type {type} for state {state}". format(type=msg.__class__.__name__, state=self.__protocol_state.name)) self.shutdown()
def test_game_over_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = GameOver(0xFF, 1338, 1337, move, board) self.assertEqual(0xFF, m.you_won) self.assertEqual(1338, m.new_rating) self.assertEqual(1337, m.old_rating) self.assertEqual(move, m.winning_move) self.assertEqual(board, m.board)
def test_move(self): m = Move(1, 2, Direction.Negative, Direction.Positive) self.assertEqual(m.to_bytes(), b"\x29") self.assertEqual(m.to_bytes(), Move.from_bytes(m.to_bytes()).to_bytes()) self.assertEqual(m.x_pos, 1) self.assertEqual(m.y_pos, 2) self.assertEqual(m.x_direction, Direction.Negative) self.assertEqual(m.y_direction, Direction.Positive) self.assertEqual((1, 2), m.pos) self.assertEqual((0, 3), m.after_move_pos) self.assertEqual((-1, 4), m.after_double_move_pos) self.assertEqual("(2, 3) -> (1, 4)", m.__repr__())
def apply_move(self, move: Move, user: "******"): """ Apply a Move, raise InvalidMoveException if the move is invalid. Process internal state until user input is again needed :param move: The move to apply :param user: The user that made the move """ self.__logger.info("{user} is trying to apply move {move}".format( user=user.username, move=move.__repr__())) # If it is an invalid move for that user if self.__next_to_go != user: self.__logger.info( "{user} failed to apply a move because it was not their turn". format(user=user.username)) raise InvalidMoveException() allowed_y_direction = self.__get_allowed_direction(user) is_primary_player = self.__is_primary(user) # Try applying the move to the lower level Board type, raise InvalidMoveException if it fails try: self.__board.apply_move(move, allowed_y_direction, is_primary_player) except InvalidMove: raise InvalidMoveException() # After applying the move, switch to the next player self.__next_to_go = self.__get_opponent(user) # Process game state (making compulsory moves and switching players turns as needed) last_move = self.__process_game_state() # The last move made was either the one process_game_state returned or the one the last user made last_move = last_move or move # Check if either player won if self.__board.check_game_over(True): self.__finish_game(self.__player_one, last_move) elif self.__board.check_game_over(False): self.__finish_game(self.__player_two, last_move) else: # Lastly, get a move from whichever player needs to go next self.__next_to_go.on_request_move( last_move, self.get_board(self.__next_to_go))
def test_apply_move(self): b = Board.generate_game_start() b.apply_move(Move(0, 2, Direction.Positive, Direction.Positive), Direction.Positive, False) self.assertEqual(BoardLocation(False, False, False), b[0, 2]) self.assertEqual(BoardLocation(True, False, False), b[1, 3]) with self.assertRaises(InvalidMove): # Try moving a piece that doesn't exist b.apply_move(Move(0, 2, Direction.Positive, Direction.Positive), Direction.Positive, False) with self.assertRaises(InvalidMove): # Try jumping your own piece b.apply_move(Move(0, 0, Direction.Positive, Direction.Positive), Direction.Positive, False) # Move a piece right b.apply_move(Move(1, 3, Direction.Positive, Direction.Positive), Direction.Positive, False) # Move a piece left b.apply_move(Move(2, 2, Direction.Negative, Direction.Positive), Direction.Positive, False) with self.assertRaises(InvalidMove): # Try moving a piece the wrong direction b.apply_move(Move(1, 3, Direction.Positive, Direction.Negative), Direction.Positive, False) with self.assertRaises(InvalidMove): # Try moving an opponent's piece b.apply_move(Move(3, 5, Direction.Negative, Direction.Negative), Direction.Positive, False) b.apply_move(Move(1, 3, Direction.Negative, Direction.Positive), Direction.Positive, False) # Jump! b.apply_move(Move(3, 5, Direction.Negative, Direction.Negative), Direction.Negative, True) # Cheat a little bit to delete some pieces and check that promotion works b[2, 6] = BoardLocation(True, False, False) b[3, 7] = BoardLocation(False, False, False) # Move to the edge and promote b.apply_move(Move(2, 6, Direction.Positive, Direction.Positive), Direction.Positive, False) self.assertTrue(b[3, 7].promoted) # Cheat a little to check moving to the bottom b[4, 2] = BoardLocation(True, False, True) b[2, 0] = BoardLocation(False, False, False) b.apply_move(Move(4, 2, Direction.Negative, Direction.Negative), Direction.Negative, True) self.assertTrue(b[2, 0].promoted) # Try to move out of bounds with self.assertRaises(InvalidMove): b.apply_move(Move(2, 0, Direction.Negative, Direction.Negative), Direction.Negative, True)
def test_get_all_moves(self): b = Board([BoardLocation(False, False, False)] * 64) b[0, 2] = BoardLocation(True, True, True) self.assertListEqual( [Move(0, 2, Direction.Positive, Direction.Positive), Move(0, 2, Direction.Positive, Direction.Negative)], b.get_possible_moves(Direction.Negative, True))
class TestAllMessages(TestCase): messages_to_test = [ (Connect, [1, b"username", b"password"], b"\x01\x01username\x00\x00\x00\x00\x00\x00\x00\x00password\x00\x00\x00\x00\x00\x00\x00\x00" ), (InvalidLogin, [InvalidLogin.Reasons.AccountDoesNotExist], b"\x02\x00"), (InvalidVersion, [1, 2], b"\x0D\x01\x02"), (QueuePosition, [1, 2, 3], b"\x03\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00"), (GameStart, [b"opponent", 16], b'\x04opponent\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00'), (YourTurn, [ Move(1, 2, Direction.Positive, Direction.Negative), Board([BoardLocation(True, False, True)] * 64) ], b"\x05*\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm" ), (MakeMove, [Move(2, 1, Direction.Negative, Direction.Positive)], b"\x06\x45"), (CompulsoryMove, [ Move(1, 2, Direction.Positive, Direction.Negative), Board([BoardLocation(True, False, True)] * 64) ], b"\x07*\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm" ), (InvalidMove, [ Move(1, 2, Direction.Positive, Direction.Negative), Board([BoardLocation(True, False, True)] * 64) ], b"\x08*\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm" ), (OpponentDisconnect, [], b"\x09"), (GameOver, [ 255, 2, 1, Move(1, 2, Direction.Positive, Direction.Negative), Board([BoardLocation(True, False, True)] * 64) ], b"\n\xff\x02\x00\x00\x00\x01\x00\x00\x00*\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm\xb6\xdbm" ), (ReQueue, [], b"\x0B"), (LogOut, [], b"\x0C"), ] def test_messages(self): for constructor, args, packed in TestAllMessages.messages_to_test: obj = constructor(*args) self.assertEqual(packed, obj.encode(), msg="packed vs encode for {}".format( constructor.__name__)) self.assertEqual(obj.encode(), constructor.parse_and_decode(packed).encode(), msg="encode vs repack for {}".format( constructor.__name__)) self.assertEqual(obj.__dict__, constructor.parse_and_decode(packed).__dict__, msg="compare dict for {}".format( constructor.__name__)) with self.assertRaises( NotEnoughData, msg="Message {} still works with too little data".format( constructor.__name__)): constructor.parse_and_decode(packed[:-1]) with self.assertRaises( InvalidType, msg="Message {} still works with wrong type number".format( constructor.__name__)): constructor.parse_and_decode(b'\x00' + packed[1:]) def test_strip(self): self.assertEqual(bytes_strip(b"abc\x00"), b"abc") self.assertEqual(bytes_strip(b"abc"), b"abc") def test_connect_message(self): m = Connect(1, b"cameron", b"password") self.assertEqual(1, m.version) self.assertEqual(b"cameron", m.username) self.assertEqual(b"password", m.password) def test_invalid_login_message(self): m = InvalidLogin(InvalidLogin.Reasons.AccountDoesNotExist) self.assertEqual(InvalidLogin.Reasons.AccountDoesNotExist, m.reason) def test_invalid_version_message(self): m = InvalidVersion(3, 2) self.assertEqual(3, m.highest_supported_version) self.assertEqual(2, m.lowest_supported_version) def test_queue_position_message(self): m = QueuePosition(3, 2, 1337) self.assertEqual(3, m.queue_size) self.assertEqual(2, m.queue_pos) self.assertEqual(1337, m.rating) def test_game_start_message(self): m = GameStart(b"opp", 24) self.assertEqual(b"opp", m.opponent_name) self.assertEqual(24, m.opponent_rating) def test_your_turn_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = YourTurn(move, board) self.assertEqual(move, m.last_move) self.assertEqual(board, m.board) def test_make_move_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) m = MakeMove(move) self.assertEqual(move, m.move) def test_compulsory_move_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = CompulsoryMove(move, board) self.assertEqual(move, m.move) self.assertEqual(board, m.board) def test_invalid_move_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = InvalidMove(move, board) self.assertEqual(move, m.move) self.assertEqual(board, m.board) def test_opponent_disconnect_message(self): m = OpponentDisconnect() # No fields to test def test_game_over_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = GameOver(0xFF, 1338, 1337, move, board) self.assertEqual(0xFF, m.you_won) self.assertEqual(1338, m.new_rating) self.assertEqual(1337, m.old_rating) self.assertEqual(move, m.winning_move) self.assertEqual(board, m.board) def test_re_queue_message(self): m = ReQueue() # no fields to test def test_log_out_message(self): m = LogOut()
def test_invalid_move_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = InvalidMove(move, board) self.assertEqual(move, m.move) self.assertEqual(board, m.board)
def test_make_move_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) m = MakeMove(move) self.assertEqual(move, m.move)
def test_your_turn_message(self): move = Move(1, 2, Direction.Positive, Direction.Positive) board = Board([BoardLocation(True, False, True)] * 64) m = YourTurn(move, board) self.assertEqual(move, m.last_move) self.assertEqual(board, m.board)