def make_move( request: chupacabra_pb2.MoveRequest, game_map: Dict[str, GameServerStub], session_handler: SessionHandler ) -> game_structs_pb2.GameStatusResponse: """Make a move in the game.""" username = request.game_info.username session_id = request.game_info.session_id user_data = session_handler.authenticate_session(username, session_id) if user_data is None: return game_structs_pb2.GameStatusResponse( success=False, message=AUTHENTICATION_FAILED) game_stub = game_map.get(request.game_info.game_type) if game_stub is None: return game_structs_pb2.GameStatusResponse( success=False, message=GAME_TYPE_NOT_FOUND.format(request.game_info.game_type)) user_game_info = game_server_pb2.UserGameInfo( player_id=user_data.user_id, game_id=request.game_info.game_id) internal_request = game_server_pb2.MoveRequest(game_info=user_game_info, move=request.move) response = game_stub.MakeMove(internal_request) return response
def forfeit_game( request: game_server_pb2.UserGameInfo ) -> game_structs_pb2.GameStatusResponse: """Forfeit the game.""" handler = get_default_tictactoe_cache_handler() validated = _validate_game(request.game_id, request.player_id, handler) if validated is False: response = game_structs_pb2.GameStatusResponse( success=False, message='Cannot find game of this id for this user.') return response # Lock the state so that we can make a move lock_key = _make_state_lock_key(request.game_id) with handler.lock(lock_key, blocking_timeout=TTT_MOVE_BLOCK_TIME): # Now grab the state from redis state_key = _make_state_key(request.game_id) state = handler.get(state_key) if state is None: logger.error('Game {} validated but data not found'.format( request.game_id)) not_found_response = game_structs_pb2.GameStatusResponse( success=False, message='Game data not found.') return not_found_response internal_state = ttt.deserialize_state(state) # If the game is over, we don't want to do anything if internal_state.mode != ttt.PLAY_MODE: return _convert_to_status_response( request.player_id, 'Game already over. Cannot forfeit.', internal_state, False) internal_state.mode = ttt.FINISHED_MODE winner_idx = None for idx, player_id in enumerate(internal_state.player_ids): if player_id != request.player_id: winner_idx = idx break if winner_idx is None: raise AssertionError('Could not find the winning player.') internal_state.winner = winner_idx serialized_state = ttt.serialize_state(internal_state) timestamp = arrow.utcnow().timestamp state_lifetime = int(internal_state.game_expiration_time - timestamp + GAME_PADDED_LIFETIME) handler.set(state_key, serialized_state, lifetime=state_lifetime) success_message = 'Success. You have forfeited the game.' response = _convert_to_status_response(request.player_id, success_message, internal_state, True) return response
def make_move( request: game_server_pb2.MoveRequest ) -> game_structs_pb2.GameStatusResponse: """Make a move.""" handler = get_default_tictactoe_cache_handler() validated = _validate_game(request.game_info.game_id, request.game_info.player_id, handler) if validated is False: response = game_structs_pb2.GameStatusResponse( success=False, message='Cannot find game of this id for this user.') return response # Lock the state so that we can make a move lock_key = _make_state_lock_key(request.game_info.game_id) with handler.lock(lock_key, blocking_timeout=TTT_MOVE_BLOCK_TIME): # Now grab the state from redis state_key = _make_state_key(request.game_info.game_id) state = handler.get(state_key) if state is None: logger.error('Game {} validated but data not found.'.format( request.game_info.game_id)) not_found_response = game_structs_pb2.GameStatusResponse( success=False, message='Game data not found.') return not_found_response internal_state = ttt.deserialize_state(state) # Make the move message, new_state = ttt.make_move(internal_state, request.move, request.game_info.player_id) # If the move was successful, write back into redis and release lock if new_state is not None: serialized_state = ttt.serialize_state(new_state) timestamp = arrow.utcnow().timestamp state_lifetime = int(internal_state.game_expiration_time - timestamp + GAME_PADDED_LIFETIME) handler.set(state_key, serialized_state, lifetime=state_lifetime) success = True else: new_state = internal_state success = False # craft and return the correct response response = _convert_to_status_response(request.game_info.player_id, message, new_state, success) return response
def get_game_status( request: game_server_pb2.UserGameInfo ) -> game_structs_pb2.GameStatusResponse: """Get the current status of the game.""" handler = get_default_tictactoe_cache_handler() lock_key = _make_state_lock_key(request.game_id) with handler.lock(lock_key, TTT_MOVE_BLOCK_TIME): message, internal_state, save_new_state = _get_game_state( request.game_id, request.player_id, handler) if save_new_state: serialized_state = ttt.serialize_state(internal_state) state_key = _make_state_key(request.game_id) timestamp = arrow.utcnow().timestamp state_lifetime = int(internal_state.game_expiration_time - timestamp + GAME_PADDED_LIFETIME) if state_lifetime > 0: handler.set(state_key, serialized_state, lifetime=state_lifetime) if internal_state is None: return game_structs_pb2.GameStatusResponse(success=False, message=message) success_message = 'Success' response = _convert_to_status_response(request.player_id, success_message, internal_state, True) return response
def _convert_to_status_response( player_id: str, success_message: str, internal_state: ttt.TicTacToeInternalState, success: bool) -> game_structs_pb2.GameStatusResponse: board = _make_game_board(internal_state.board, internal_state.players) current_player = internal_state.player_ids[internal_state.turn] if (internal_state.mode == ttt.PLAY_MODE and current_player == player_id): legal_moves = [description.PLACE_MARK_DESCRIPTION.name] else: legal_moves = [] status = _get_game_status(internal_state.mode, internal_state.winner, internal_state.players) state = game_structs_pb2.GameState( status=status, boards=[board], mode=internal_state.mode, ) status_info = game_structs_pb2.GameStatusInfo( id=internal_state.id, players=internal_state.players, state=state, legal_moves=legal_moves) response = game_structs_pb2.GameStatusResponse(success=success, message=success_message, status_info=status_info) return response
def forfeit_game( request: chupacabra_pb2.PlayerGameInfo, game_map: Dict[str, GameServerStub], session_handler: SessionHandler ) -> game_structs_pb2.GameStatusResponse: """Immediately forfeit the game.""" username = request.username session_id = request.session_id user_data = session_handler.authenticate_session(username, session_id) if user_data is None: return game_structs_pb2.GameStatusResponse( success=False, message=AUTHENTICATION_FAILED) game_stub = game_map.get(request.game_type) if game_stub is None: return game_structs_pb2.GameStatusResponse( success=False, message=GAME_TYPE_NOT_FOUND.format(request.game_type)) internal_request = game_server_pb2.UserGameInfo( player_id=user_data.user_id, game_id=request.game_id) response = game_stub.ForfeitGame(internal_request) return response
def check_legal_moves( request: chupacabra_pb2.PlayerGameInfo, game_map: Dict[str, GameServerStub], session_handler: SessionHandler ) -> game_structs_pb2.LegalMovesResponse: """Check what types of moves are available for the player at this point in the game.""" username = request.username session_id = request.session_id user_data = session_handler.authenticate_session(username, session_id) if user_data is None: return game_structs_pb2.GameStatusResponse( success=False, message=AUTHENTICATION_FAILED) game_stub = game_map.get(request.game_type) if game_stub is None: return game_structs_pb2.GameStatusResponse( success=False, message=GAME_TYPE_NOT_FOUND.format(request.game_type)) internal_request = game_server_pb2.UserGameInfo( player_id=user_data.user_id, game_id=request.game_id) response = game_stub.GetLegalMoves(internal_request) return response