def test_draws(self): # Check nullmove draw conditions board = ataxx.Board() board.makemove(ataxx.Move.null()) board.makemove(ataxx.Move.null()) self.assertTrue(board.gameover()) self.assertFalse(board.fifty_move_draw()) self.assertFalse(board.max_length_draw()) # Check double move draw conditions board = ataxx.Board() for i in range(500): if i < 50: self.assertFalse(board.gameover()) self.assertFalse(board.fifty_move_draw()) self.assertFalse(board.max_length_draw()) elif i < 400: self.assertTrue(board.gameover()) self.assertTrue(board.fifty_move_draw()) self.assertFalse(board.max_length_draw()) else: self.assertTrue(board.gameover()) self.assertTrue(board.fifty_move_draw()) self.assertTrue(board.max_length_draw()) if i % 2 == 0: board.makemove(ataxx.Move.from_san("g1g3")) board.makemove(ataxx.Move.from_san("a1a3")) else: board.makemove(ataxx.Move.from_san("g3g1")) board.makemove(ataxx.Move.from_san("a3a1"))
def main(): """ Time various parts of the library """ fens = ["startpos", "x4oo/x3ooo/4xxx/3o1x1/1ooo3/1oo3x/6x x"] total = 0 # Perft t1 = time.time() for fen in fens: board = ataxx.Board(fen) for i in range(4): board.perft(i) t2 = time.time() total += t2 - t1 print(F"Perft: {t2-t1:.4f} seconds") # Movegen t1 = time.time() for fen in fens: board = ataxx.Board(fen) for i in range(10000): for move in board.legal_moves(): assert move t2 = time.time() total += t2 - t1 print(F"Movegen: {t2-t1:.4f} seconds") # FEN parsing t1 = time.time() for fen in fens: for i in range(10000): board = ataxx.Board(fen) t2 = time.time() total += t2 - t1 print(F"Parsing: {t2-t1:.4f} seconds") # Negamax t1 = time.time() for fen in fens: board = ataxx.Board(fen) move = ataxx.players.negamax(board, 2) t2 = time.time() total += t2 - t1 print(F"Negamax: {t2-t1:.4f} seconds") # Alphabeta t1 = time.time() for fen in fens: board = ataxx.Board(fen) move = ataxx.players.alphabeta(board, -999999, 999999, 3) t2 = time.time() total += t2 - t1 print(F"Alphabeta: {t2-t1:.4f} seconds") print(F"Total: {total:.4f} seconds")
def test_gameover(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1", False], ["x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1", False], ["x5o/7/3-3/2-1-2/3-3/7/o5x x 0 1", False], ["7/7/7/7/7/7/7 x 0 1", True], ["7/7/7/7/7/7/7 o 0 1", True], ["x5o/7/7/7/7/7/o5x x 0 1", False], ["x5o/7/7/7/7/7/o5x x 99 1", False], ["x5o/7/7/7/7/7/o5x x 100 1", True], ["x5o/7/7/7/7/7/o5x x 101 1", True], ["x6/7/7/7/7/7/7 x 0 1", True], ["x6/7/7/7/7/7/7 o 0 1", True], ["o6/7/7/7/7/7/7 x 0 1", True], ["o6/7/7/7/7/7/7 o 0 1", True], ["7/7/7/7/4ooo/4ooo/4oox x 0 1", False], ["7/7/7/7/4ooo/4ooo/4oox o 0 1", False], ["7/7/7/7/-------/-------/x5o x 0 1", False], ["7/7/7/7/-------/-------/x5o o 0 1", False], ["7/7/7/7/-------/-------/xxxoooo x 0 1", True], ["7/7/7/7/-------/-------/xxxoooo o 0 1", True], ["7/7/7/7/---1---/-------/xxxoooo x 0 1", False], ["7/7/7/7/---1---/-------/xxxoooo o 0 1", False], ] for fen, gameover in tests: board = ataxx.Board(fen) self.assertTrue(board.gameover() == gameover) num_moves = len(board.legal_moves()) if gameover: self.assertTrue(num_moves == 0) else: self.assertTrue(num_moves > 0)
def test_is_legal(self): fens = [ "x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1", "7/3o3/7/3x3/7/7/3oo2 x 0 1", "7/7/7/7/4ooo/4ooo/4oox x 0 1", "7/7/7/7/4ooo/4ooo/4oox o 0 1", "7/7/7/7/4xxx/4xxx/4xxo x 0 1", "7/7/7/7/4xxx/4xxx/4xxo o 0 1", "7/7/7/7/7/7/7 x 0 1", "7/7/7/7/7/7/7 o 0 1", "7/7/7/7/-------/-------/xxx1ooo x 0 1", "7/7/7/7/-------/-------/xxx1ooo o 0 1", "7/7/7/7/-------/-------/xxxxooo x 0 1", "7/7/7/7/-------/-------/xxxxooo o 0 1", "7/7/7/7/---1---/-------/xxxxooo x 0 1", "7/7/7/7/---1---/-------/xxxxooo o 0 1", "xxxxxxx/ooooooo/xxxxxxx/ooooooo/xxxxxxx/ooooooo/xxxxxxx x 0 1", "7/7/7/7/7/7/6x x 0 1", "7/7/7/7/7/7/6x o 0 1", "7/7/7/7/7/7/6o x 0 1", "7/7/7/7/7/7/6o o 0 1" ] all_moves = [ataxx.Move.from_san(n) for n in movestrings] for fen in fens: board = ataxx.Board(fen) generated_moves = [n for n in board.legal_moves()] found = 0 for move in all_moves: if move in generated_moves: self.assertTrue(board.is_legal(move)) found += 1 else: self.assertFalse(board.is_legal(move)) self.assertTrue(found == len(generated_moves))
def test_greedy(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1", ["f1", "f2", "g2", "a6", "b6", "b7"]], ["x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", ["a2", "b1", "b2", "g6", "f6", "f7"]], ["x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", ["f1", "f2", "g2", "a6", "b6", "b7"]], ["x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1", ["a2", "b1", "b2", "g6", "f6", "f7"]], ["7/3o3/7/3x3/7/7/7 x 0 1", ["c5", "d5", "e5"]], ["3o3/7/7/3x3/7/7/7 x 0 1", ["d4c6", "d4d6", "d4e6"]], ["3o3/7/3x3/3x3/7/7/7 x 0 1", ["c6", "d6", "e6"]], ["o4oo/7/x5x/7/7/7/7 x 0 1", ["f6", "g6"]], ["7/3o3/7/3x3/7/7/3oo2 x 0 1", ["d4d2", "d4e2"]], ["7/7/7/7/7/7/7 x 0 1", ["0000"]] ] for fen, moves in tests: board = ataxx.Board(fen) # Greedy player for _ in range(100): move = ataxx.players.greedy(board) if board.gameover(): self.assertTrue(move == ataxx.Move.null()) else: self.assertTrue(move in board.legal_moves()) self.assertTrue(str(move) in moves)
def test_pgn_random(self): # Try parse some random games # These won't have variations or comments in them for _ in range(10): board = ataxx.Board() while not board.gameover() and board.halfmove_clock < 500: move = ataxx.players.random_move(board) board.makemove(move) pgn = ataxx.pgn.Game() pgn.headers["Event"] = random_phrase(12) pgn.headers["Black"] = random_phrase(12) pgn.headers["White"] = random_phrase(12) pgn.headers["FEN"] = ataxx.FEN_STARTPOS pgn.headers["Result"] = board.result() pgn.from_board(board) # Human readable pgn string pgn_string = str(pgn) # Test: pgn string ---> pgn ---> pgn string self.assertTrue(str(ataxx.pgn.parse(pgn_string)) == pgn_string) # Check the pgn main line matches the board moves = [n.move for n in pgn.main_line()] self.assertTrue(moves == board.main_line())
def test_counters(self): positions = [ { "move": "g1f3", "fen": "x5o/7/7/7/5x1/7/o6 o 1 1" }, { "move": "a1c1", "fen": "x5o/7/7/7/5x1/7/2o4 x 2 2" }, { "move": "b6", "fen": "x5o/1x5/7/7/5x1/7/2o4 o 0 2" }, { "move": "c1e3", "fen": "x5o/1x5/7/7/4oo1/7/7 x 0 3" }, { "move": "0000", "fen": "x5o/1x5/7/7/4oo1/7/7 o 1 3" }, ] board = ataxx.Board() for position in positions: move = position["move"] fen = position["fen"] board.makemove(ataxx.Move.from_san(move)) self.assertTrue(board.get_fen() == fen)
def test_pass(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1", False], ["x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1", False], ["x5o/7/3-3/2-1-2/3-3/7/o5x x 0 1", False], ["7/7/7/7/4ooo/4ooo/4oox x 0 1", True], ["7/7/7/7/4ooo/4ooo/4oox o 0 1", False], ["7/7/7/7/4xxx/4xxx/4xxo x 0 1", False], ["7/7/7/7/4xxx/4xxx/4xxo o 0 1", True], ["xxxxxxx/-------/-------/7/7/-------/ooooooo x 0 1", True], ["xxxxxxx/-------/-------/7/7/-------/ooooooo o 0 1", False], ["ooooooo/-------/-------/7/7/-------/xxxxxxx x 0 1", False], ["ooooooo/-------/-------/7/7/-------/xxxxxxx o 0 1", True], ["x5o/7/7/7/7/7/o5x x 0 100", False], ["7/7/7/7/4ooo/4ooo/4oox x 0 100", True], ] for fen, passing in tests: board = ataxx.Board(fen) moves = board.legal_moves() num_moves = len(moves) self.assertTrue(board.must_pass() == passing) if board.gameover(): self.assertTrue(num_moves == 0) else: if passing: self.assertTrue(num_moves == 1) self.assertTrue(moves[0] == ataxx.Move.null()) else: self.assertTrue(num_moves > 0) self.assertTrue(ataxx.Move.null() not in moves)
def main(path, directory): # Check the input file if not os.path.isfile(path): print(F"File \"{path}\" doesn't exist") return # Check the output directory if not os.path.isdir(directory): print(F"Directory \"{directory}\" doesn't exist") return for idx, game in enumerate(ataxx.pgn.GameIterator(path), 1): print(F"Processing game {idx:04d}... ", end="") board = ataxx.Board(game.headers["FEN"]) # Number of pieces black = [] white = [] # Get piece counts num_black, num_white, _, num_empty = board.count() # Max pieces max_pieces = num_black + num_white + num_empty # Track piece count history black.append(num_black) white.append(num_white) for node in game.main_line(): board.makemove(node.move) # Get piece counts num_black, num_white, _, _ = board.count() # Track piece count history black.append(num_black) white.append(num_white) if 'Black' in game.headers: black_player = game.headers['Black'][:40] else: black_player = "Unknown" if 'White' in game.headers: white_player = game.headers['White'][:40] else: white_player = "Unknown" # Try accommodate particularly long player names if len(black_player) + len(white_player) > 48: title = F"{black_player}\nvs\n{white_player}" else: title = F"{black_player} vs {white_player}" graph(F"{directory}/game_{idx:04d}.png", title, black, white, max_pieces) print("done")
def test_make_undo(self): fens = [ "x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1", "7/3o3/7/3x3/7/7/3oo2 x 0 1" ] for fen in fens: board = ataxx.Board(fen) while not board.gameover() and board.halfmove_clock < 500: current_fen = board.get_fen() # Test all legal moves for move in board.legal_moves(): board.makemove(move) board.undo() self.assertTrue(board.get_fen() == current_fen) # Test null move board.makemove(ataxx.Move.null()) board.undo() self.assertTrue(board.get_fen() == current_fen) # Pick a random move and keep going move = random.choice(board.legal_moves()) board.makemove(move) # Undo every move in the game while board.main_line(): board.undo() # Make sure we're back where we started self.assertTrue(board.get_fen() == fen)
def main(): """ Play a game solo """ fen = input("FEN: ") if fen == "": fen = "startpos" board = ataxx.Board(fen) while not board.gameover(): print("\n\n\n") print(F"FEN: {board.get_fen()}") print(board) try: move_string = input("Move: ") move = ataxx.Move.from_san(move_string) if board.is_legal(move): board.makemove(move) else: print(F"Illegal move: {move}") except KeyboardInterrupt: print("") break print(F"Result: {board.result()}")
def from_line(self, line): val, fen = line.split(sep=None, maxsplit=1) bd = ataxx.Board(fen) white, black = util.to_tensors(bd) return torch.tensor([ bd.turn ]).float(), white.float(), black.float(), torch.tensor([int(val) ]).float()
def test_set_get(self): nums = [0, 1, 2, 3, 4, 5, 6] squares = [[f, r] for f in nums for r in nums] board = ataxx.Board("empty") for x, y in squares: for piece in [ataxx.BLACK, ataxx.WHITE, ataxx.GAP, ataxx.EMPTY]: board.set(x, y, piece) self.assertTrue(piece == board.get(x, y))
def test_pgn(self): def random_phrase(n): return ''.join( random.choices(string.ascii_uppercase + string.ascii_lowercase + string.punctuation + string.digits + " ", k=n)) pgns = [ "[Event \"Example 1\"]\n[Black \"Player 1\"]\n[White \"Player 2\"]\n[UTCDate \"1970.01.01\"]\n[UTCTime \"00:00:00\"]\n[FEN \"x5o/7/7/7/7/7/o5x x\"]\n[Result \"*\"]\n\n1. a7c5 a2 2. g2 *", "[Event \"Example 2\"]\n[Black \"Player 1\"]\n[White \"Player 2\"]\n[UTCDate \"1970.01.01\"]\n[UTCTime \"00:00:00\"]\n[FEN \"x5o/7/7/7/7/7/o5x x\"]\n[Result \"*\"]\n\n1. a7c5 { Test 123 } 1... a2 { Test } 2. g2 *", "[Event \"Example 3\"]\n[Black \"Player 1\"]\n[White \"Player 2\"]\n[UTCDate \"1970.01.01\"]\n[UTCTime \"00:00:00\"]\n[FEN \"x5o/7/7/7/7/7/o5x x\"]\n[Result \"*\"]\n\n1. a7c7 (1. a7c5 { Test }) 1... g7f5 (1... a2 { Test } 2. g2 (2. f2 { Test })) 2. g1f3 a1b3 { Test 123 } *", "[Event \"Example 4\"]\n[Black \"Player 1\"]\n[White \"Player 2\"]\n[UTCDate \"1970.01.01\"]\n[UTCTime \"00:00:00\"]\n[FEN \"x5o/7/7/7/7/7/o5x x\"]\n[Result \"*\"]\n\n1. a7c7 { Test } (1. a7c5 { Test }) 1... g7f5 (1... a2 { Test } 2. g2 (2. f2 { Test } 2... a1c2)) 2. g1f3 a1b3 { Test 123 } *" ] # Test some known pgn strings for pgn in pgns: self.assertTrue(str(ataxx.pgn.parse(pgn)) == pgn) # Try parse some random games # These won't have variations or comments in them for _ in range(10): board = ataxx.Board() while not board.gameover() and board.halfmove_clock < 500: move = ataxx.players.random_move(board) board.makemove(move) pgn = ataxx.pgn.Game() pgn.headers["Event"] = random_phrase(12) pgn.headers["Black"] = random_phrase(12) pgn.headers["White"] = random_phrase(12) pgn.headers["FEN"] = ataxx.FEN_STARTPOS pgn.headers["Result"] = board.result() pgn.from_board(board) # Human readable pgn string pgn_string = str(pgn) # Test: pgn string ---> pgn ---> pgn string self.assertTrue(str(ataxx.pgn.parse(pgn_string)) == pgn_string) # Check the pgn main line matches the board moves = [n.move for n in pgn.main_line()] self.assertTrue(moves == board.main_line()) # Create a pgn ourselves game = ataxx.pgn.Game() game.headers["FEN"] = ataxx.FEN_STARTPOS game.headers["Result"] = "*" node = game.add_variation(ataxx.Move.from_san("g2"), comment="First move") node = node.add_variation(ataxx.Move.from_san("a1a3"), comment="Second move") self.assertTrue( str(game) == "[Event \"Example\"]\n[FEN \"x5o/7/7/7/7/7/o5x x 0 1\"]\n[Result \"*\"]\n\n1. g2 { First move } a1a3 { Second move } *" )
def test_single_double(self): fens = [ "x5o/7/7/7/7/7/o5x x", "x5o/7/2-1-2/7/2-1-2/7/o5x o", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x", "x5o/7/3-3/2-1-2/3-3/7/o5x o" ] for fen in fens: board = ataxx.Board(fen) for move in board.legal_moves(): self.assertTrue(move.is_single() != move.is_double())
def test_main_line(self): for _ in range(10): history = [] # Play random moves on the board board1 = ataxx.Board("startpos") while not board1.gameover() and len(history) < 50: moves = board1.legal_moves() move = random.choice(moves) board1.makemove(move) history.append(move) # Replay the moves on a new board board2 = ataxx.Board("startpos") for move in board1.main_line(): board2.makemove(move) self.assertTrue(board1.main_line() == history) self.assertTrue(board1.get_fen() == board2.get_fen())
def test_from_san(self): fens = [ "x5o/7/7/7/7/7/o5x x", "x5o/7/2-1-2/7/2-1-2/7/o5x o", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x", "x5o/7/3-3/2-1-2/3-3/7/o5x o" ] for fen in fens: board = ataxx.Board(fen) for move in board.legal_moves(): self.assertTrue(ataxx.Move.from_san(str(move)) == move)
def test_known(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1", 0x94aea6ca1a7b7761], ["x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1", 0x94aea6ca1a7b7761], ["1oo1o2/7/7/3xo2/1x5/7/7 x 0 1", 0xb97cab11e44bc9ef], ["7/7/7/7/-------/-------/x5o x 0 1", 0x4cb62ab58e84a8d7], ] for fen, hash in tests: board = ataxx.Board(fen) self.assertTrue(board.get_hash() == hash)
def test_null_move(self): nullmove = ataxx.Move.null() self.assertTrue(nullmove == ataxx.Move(-1, -1, -1, -1)) self.assertTrue(nullmove == ataxx.Move.null()) self.assertTrue(nullmove != ataxx.Move(0, 0, 0, 0)) self.assertTrue(str(nullmove) == "0000") board1 = ataxx.Board() board2 = ataxx.Board() # Make the null move board2.makemove(nullmove) pieces1, turn1, halfmoves1, _ = board1.get_fen().split(" ") pieces2, turn2, halfmoves2, _ = board2.get_fen().split(" ") # Check changes made self.assertTrue(pieces1 == pieces2) self.assertTrue(turn1 != turn2) self.assertTrue(int(halfmoves1) + 1 == int(halfmoves2))
def test_fen(self): fens = [ "x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1" ] for fen in fens: board = ataxx.Board(fen) self.assertTrue(board.get_fen() == fen) fens = [ "", "a x 0 1", "x5o/7/7/7/7/7/o5x a 0 1", "x5o/7/7/7/7/7/o5x x a 1", "x5o/7/7/7/7/7/o5x x 0 a", "x5o/7/7/7/7/7/o5x x 0 1 a", "x5o/7/7/7/7/7/o5x x -5 1", "x5o/7/7/7/7/7/o5x x 0 -5" ] for fen in fens: board = ataxx.Board() self.assertTrue(board.set_fen(fen) != True)
def test_complete(self): fens = [ "x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1" ] for fen in fens: board = ataxx.Board() self.assertTrue(board.set_fen(fen) == True) self.assertTrue(board.get_fen() == fen) self.assertTrue(board.start_fen() == fen)
def test_result(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1", "*"], ["x5o/7/7/7/7/7/o5x o 0 1", "*"], ["x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1", "*"], ["x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "*"], ["x6/7/7/7/7/7/7 x 0 1", "1-0"], ["x6/7/7/7/7/7/7 o 0 1", "1-0"], ["o6/7/7/7/7/7/7 x 0 1", "0-1"], ["o6/7/7/7/7/7/7 o 0 1", "0-1"], [ "1xxxxxx/xxxxxxx/xxxxxxx/xxxxooo/ooooooo/ooooooo/ooooooo x 0 1", "*" ], [ "1xxxxxx/xxxxxxx/xxxxxxx/xxxxooo/ooooooo/ooooooo/ooooooo o 0 1", "*" ], [ "1oooooo/ooooooo/ooooooo/ooooxxx/xxxxxxx/xxxxxxx/xxxxxxx x 0 1", "*" ], [ "1oooooo/ooooooo/ooooooo/ooooxxx/xxxxxxx/xxxxxxx/xxxxxxx o 0 1", "*" ], [ "xxxxxxx/xxxxxxx/xxxxxxx/xxxxooo/ooooooo/ooooooo/ooooooo x 0 1", "1-0" ], [ "xxxxxxx/xxxxxxx/xxxxxxx/xxxxooo/ooooooo/ooooooo/ooooooo o 0 1", "1-0" ], [ "ooooooo/ooooooo/ooooooo/ooooxxx/xxxxxxx/xxxxxxx/xxxxxxx x 0 1", "0-1" ], [ "ooooooo/ooooooo/ooooooo/ooooxxx/xxxxxxx/xxxxxxx/xxxxxxx o 0 1", "0-1" ], ["7/7/7/7/7/7/7 o 0 1", "1/2-1/2"], ["x5o/7/7/7/7/7/o5x x 99 0", "*"], ["x5o/7/7/7/7/7/o5x x 100 0", "1/2-1/2"], ["x5o/7/7/7/7/7/o5x x 0 400", "*"], ] for fen, result in tests: board = ataxx.Board(fen) self.assertTrue(board.result() == result)
def test_counters(self): tests = [ ["g2", 0, 1], ["a2", 0, 2], ["g3", 0, 2], ["a2a4", 1, 3], ["a7a5", 2, 3], ["a1b3", 3, 4], ["b4", 0, 4], ["f6", 0, 5], ] board = ataxx.Board("x5o/7/7/7/7/7/o5x x 0 1") for movestr, half, full in tests: move = ataxx.Move.from_san(movestr) board.makemove(move) self.assertTrue(board.halfmove_clock == half) self.assertTrue(board.fullmove_clock == full) board = ataxx.Board("x5o/7/7/7/7/7/o5x x 0 1") board.makemove(ataxx.Move.null()) self.assertTrue(board.halfmove_clock == 1)
def test_malformed(self): tests = [ ["x5o/7/7/7/7/7/o5x x 0 1 ", "x5o/7/7/7/7/7/o5x x 0 1"], ["x5o/7/7/7/7/7/o5x x 0 1 ", "x5o/7/7/7/7/7/o5x x 0 1"], ["x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/7/7/7/7/o5x x 0 1"], ["x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/7/7/7/7/o5x x 0 1"], ["x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/7/7/7/7/o5x x 0 1"], [" x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/7/7/7/7/o5x x 0 1"], ] for fen, corrected in tests: board = ataxx.Board() self.assertTrue(board.set_fen(fen) == True) self.assertTrue(board.get_fen() == corrected)
def test_positions(self): def test(board, depth): if depth == 0 or board.gameover(): return num_legal = 0 for move in board.legal_moves(): movestr = str(move) self.assertTrue(board.is_legal(move)) self.assertTrue(ataxx.Move.from_san(movestr) == move) if move != ataxx.Move.null(): if move.is_single(): self.assertTrue(movestr in singles) else: self.assertTrue(movestr in doubles) num_legal += 1 board.makemove(move) test(board, depth-1) board.undo() # Compare how many legal moves were found against the entire list of possible moves for s in singles: move = ataxx.Move.from_san(s) if board.is_legal(move): num_legal -= 1 for d in doubles: move = ataxx.Move.from_san(d) if board.is_legal(move): num_legal -= 1 if board.is_legal(ataxx.Move.null()): num_legal -= 1 self.assertTrue(num_legal == 0) fens = [ "x5o/7/7/7/7/7/o5x x 0 1", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1", "7/7/7/2x1o2/7/7/7 x 0 1", "7/7/7/7/4ooo/4ooo/4oox x 0 1" ] for fen in fens: board = ataxx.Board(fen) test(board, 2)
def main(): """ A game between the random player and the greedy player """ board = ataxx.Board() while not board.gameover(): if board.turn == ataxx.BLACK: move = ataxx.players.random_move(board) else: move = ataxx.players.greedy(board) board.makemove(move) moves = " ".join([str(move) for move in board.main_line()]) print(F"Result: {board.result()}") print(F"Moves: {moves}")
def test_partial(self): tests = [["x5o/7/7/7/7/7/o5x", "x5o/7/7/7/7/7/o5x x 0 1"], [ "x5o/7/2-1-2/7/2-1-2/7/o5x o", "x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1" ], [ "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0", "x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1" ], [ "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1", "x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1" ]] for fen, corrected in tests: board = ataxx.Board() self.assertTrue(board.set_fen(fen) == True) self.assertTrue(board.get_fen() == corrected)
def test_players(self): positions = [{ "fen": "x5o/7/7/7/7/7/o5x x", "moves": ["f1", "f2", "g2", "a6", "b6", "b7"] }, { "fen": "x5o/7/2-1-2/7/2-1-2/7/o5x o", "moves": ["a2", "b1", "b2", "g6", "f6", "f7"] }, { "fen": "x5o/7/2-1-2/3-3/2-1-2/7/o5x x", "moves": ["f1", "f2", "g2", "a6", "b6", "b7"] }, { "fen": "x5o/7/3-3/2-1-2/3-3/7/o5x o", "moves": ["a2", "b1", "b2", "g6", "f6", "f7"] }, { "fen": "7/3o3/7/3x3/7/7/7 x", "moves": ["c5", "d5", "e5"] }, { "fen": "3o3/7/7/3x3/7/7/7 x", "moves": ["d4c6", "d4d6", "d4e6"] }, { "fen": "3o3/7/3x3/3x3/7/7/7 x", "moves": ["c6", "d6", "e6"] }, { "fen": "o4oo/7/x5x/7/7/7/7 x", "moves": ["f6", "g6"] }, { "fen": "7/3o3/7/3x3/7/7/3oo2 x", "moves": ["d4d2", "d4e2"] }, { "fen": "7/7/7/7/7/7/7 x", "moves": ["0000"] }] for position in positions: fen = position["fen"] moves = position["moves"] # Greedy player board = ataxx.Board(fen) for _ in range(100): move = ataxx.players.greedy(board) self.assertTrue(str(move) in moves)
def test_invalid(self): fens = [ "", "a", "a x 0 1", "x5o/7/7/7/7/7/o5g x 0 1", "x5o/7/7/7/7/7/o5 x 0 1", "x5o/7/7/7/7/7/o5xg x 0 1", "x5o/7/7/7/7/7/o5x a 0 1", "x5o/7/7/7/7/7/o5x x a 1", "x5o/7/7/7/7/7/o5x x 0 a", "x5o/7/7/7/7/7/o5x x 0 1 a", "x5o/7/7/7/7/7/o5x x -5 1", "x5o/7/7/7/7/7/o5x x 0 -5", "x5o/7/7/7/7/7/o5x x 0 1 a", "7/7/7/7/7/7/7/7 x 0 1", ] for fen in fens: board = ataxx.Board() self.assertTrue(board.set_fen(fen) == False)
def test_perft(self): positions = [ { "fen": "7/7/7/7/7/7/7 x", "nodes": [1, 0, 0, 0, 0] }, { "fen": "x5o/7/7/7/7/7/o5x x", "nodes": [1, 16, 256, 6460, 155888] }, { "fen": "x5o/7/2-1-2/7/2-1-2/7/o5x o", "nodes": [1, 14, 196, 4184, 86528] }, { "fen": "x5o/7/2-1-2/3-3/2-1-2/7/o5x x", "nodes": [1, 14, 196, 4100, 83104] }, { "fen": "x5o/7/3-3/2-1-2/3-3/7/o5x o", "nodes": [1, 16, 256, 5948, 133264] }, { "fen": "7/7/7/7/2-----/2-----/2--x1o x", "nodes": [1, 1, 0, 0, 0] }, { "fen": "7/7/7/7/2-----/2-----/2--x1o o", "nodes": [1, 1, 0, 0, 0] }, ] depth = 4 for position in positions: fen = position["fen"] board = ataxx.Board(fen) for idx, nodes in enumerate(position["nodes"]): if idx > depth: break self.assertTrue(board.perft(idx) == nodes)