def test_possible_moves(): """Test single call of possible_moves() function on the grid.""" board = Board() for move in [(i, j) for i in range(4) for j in range(5)]: board.make_move(move, 1) expected_moves = [(4, 0), (4, 1), (4, 2), (4, 3), (4, 4)] assert list(board.possible_moves()) == expected_moves
def eval_list(array: List[List[int]]) -> int: """ Simulates evaluation on the 2d list by creating grid artificially. :param array: array of grid to be evaluated :return: score of the grid """ board = Board() for row in range(board.size): for col in range(board.size): board.make_move((row, col), array[row][col]) return board.score()
class RandomPlayer(Player): """ Random player plays moves randomly on empty positions. """ def reset(self) -> None: self.board = Board() def move(self, number: int): possible_moves = list(self.board.possible_moves()) if not possible_moves: raise IndexError("No moves available") picked_move = random.choice(possible_moves) self.board.make_move(picked_move, number)
class HumanPlayer(Player): """ Human player takes inputs from console after printing the board and the next move number """ def reset(self) -> None: self.board = Board() def move(self, number: int): print(self.board) print(f"Next card:\t{number}") row, col = None, None moves = set(self.board.possible_moves()) while (row, col) not in moves: row = int(input(f"Row number [0, {self.board.size}):\t")) col = int(input(f"Column number [0, {self.board.size}):\t")) self.board.make_move((row, col), number)
def test_str(): """Test string output of the grid.""" board = Board() assert str(board) == "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+" board.make_move((0, 0), 1) board.make_move((1, 0), 12) assert str(board) == "+--+--+--+--+--+\n" \ "| 1| | | | |\n" \ "+--+--+--+--+--+\n" \ "|12| | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+\n" \ "| | | | | |\n" \ "+--+--+--+--+--+"
def test_row_rle(): """Test basic properties of the row_rle() function.""" board = Board() board.make_move((0, 1), 1) board.make_move((0, 2), 3) for i in range(1, board.size): assert len(board.row_rle(i)) == 0 assert board.row_rle(0) == {1: 1, 3: 1}
def reset(self) -> None: self.reset_cards() self.board = Board()
class SimulationPlayer(Player): """ Run many random simulations and pick the move that yield the best average score. The move time is bounded either by max move time or number of simulations. """ def __init__(self, max_time: Optional[int], max_simulations: Optional[int]): """Note: time in nanoseconds""" assert max_time is not None or max_simulations is not None super().__init__() self.cards: List[int] = [] self.last_valid_card_idx = -1 self.reset_cards() self.max_time: int = max_time or 10e9 # 10 seconds self.max_simulations: int = max_simulations or 10e50 self.verbose = False def reset_cards(self): self.cards = [i for i in range(1, 14) for _ in range(4)] self.last_valid_card_idx = len(self.cards) - 1 def reset(self) -> None: self.reset_cards() self.board = Board() def invalidate_card(self, card_idx): swap(self.cards, card_idx, self.last_valid_card_idx) self.last_valid_card_idx -= 1 def revalidate_card(self, card_idx: int): self.last_valid_card_idx += 1 swap(self.cards, card_idx, self.last_valid_card_idx) def simulate_move(self, position: Tuple[int, int], move: int) -> int: """Note: return score, also clean up this move""" self.board.make_move(position, move) possible_moves = list(self.board.possible_moves()) if not possible_moves: self.board.unmake_move(position) return self.board.score() next_card_idx = random.randint(0, self.last_valid_card_idx) next_move = self.cards[next_card_idx] move_position = random.choice(possible_moves) self.invalidate_card(next_card_idx) score = self.simulate_move(move_position, next_move) self.revalidate_card(next_card_idx) self.board.unmake_move(position) return score def move(self, number: int): move_index = self.cards.index(number, 0, self.last_valid_card_idx) self.invalidate_card(move_index) possible_moves = list(self.board.possible_moves()) if len(possible_moves) == 1: self.board.make_move(possible_moves[0], number) return scores = [0] * len(possible_moves) simulations = [0] * len(possible_moves) total_simulations = 0 start_time = time_ns() while (time_ns() - start_time < self.max_time and total_simulations <= self.max_simulations - 1000): for _ in range(1000): # do not ask for time too many times for i, move in enumerate(possible_moves): score = self.simulate_move(move, number) scores[i] += score simulations[i] += 1 total_simulations += 1 final_scores = [score / it for score, it in zip(scores, simulations)] sorted_moves = sorted(zip(final_scores, possible_moves), reverse=True) best_move = sorted_moves[0][1] self.board.make_move(best_move, number) if self.verbose: print("Final scores:") pprint.pprint(final_scores)
def reset(self) -> None: self.board = Board()
def test_make_move(): """Test making move and updating member variables.""" board = Board() board.make_move((0, 0), 13) assert len(list(board.possible_moves())) == 5 * 5 - 1 assert all(board.row(0) == board.col(0)) assert (0, 0) not in board.possible_moves() assert board.occupied_cells == 1 board.make_move((4, 4), 1) assert (4, 4) not in board.possible_moves() assert (4, 3) in board.possible_moves() with pytest.raises(ValueError): board.make_move((4, 4), 5)
def test_empty_board(): """Check basic properties of the empty grid.""" board = Board() assert board.occupied_cells == 0 board.integrity_check() assert len(list(board.possible_moves())) == board.size**2
def test_col(): """Test basic properties of the col() function.""" board = Board() assert len(board.col(0)) == board.size for i in range(board.size): assert all(board.col(i) == np.full((board.size, ), EMPTY_CELL)) board.make_move((0, 1), 1) board.integrity_check() board.make_move((1, 1), 3) board.integrity_check() board.make_move((4, 1), 13) board.integrity_check() expected_col = np.asarray([1, 3, EMPTY_CELL, EMPTY_CELL, 13]) assert all(board.col(1) == expected_col)
def test_row(): """Test basic properties of the row() function.""" board = Board() assert len(board.row(0)) == board.size board.make_move((0, 1), 1) board.integrity_check() board.make_move((0, 2), 3) board.integrity_check() board.make_move((0, 4), 13) board.integrity_check() expected_row = np.asarray([EMPTY_CELL, 1, 3, EMPTY_CELL, 13]) assert all(board.row(0) == expected_row)
def test_raises(): """Empty board cannot be scored.""" board = Board() with pytest.raises(ValueError): board.score()