From 14ad77beae05fa3a73d9f10d2ae2fc343ceee2b8 Mon Sep 17 00:00:00 2001 From: folkert Date: Thu, 24 Oct 2019 21:25:06 +0200 Subject: [PATCH 01/11] also close sockets for process.kill --- ataxx/process.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ataxx/process.py b/ataxx/process.py index 2c99fd6..1f3399e 100644 --- a/ataxx/process.py +++ b/ataxx/process.py @@ -39,4 +39,6 @@ def terminate(self): def kill(self): self.stop.set() self.process.kill() + self.process.stdout.close() + self.process.stdin.close() return self.process.wait() From 4799bb192aa89b18adc569adace0bc01a6314527 Mon Sep 17 00:00:00 2001 From: folkert Date: Fri, 25 Oct 2019 14:24:30 +0200 Subject: [PATCH 02/11] first kill process, then stop listener --- ataxx/process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ataxx/process.py b/ataxx/process.py index 1f3399e..74f2f41 100644 --- a/ataxx/process.py +++ b/ataxx/process.py @@ -37,8 +37,8 @@ def terminate(self): return self.process.wait() def kill(self): - self.stop.set() self.process.kill() + self.stop.set() self.process.stdout.close() self.process.stdin.close() return self.process.wait() From df09d05178313456e38edd8836dad3cc10e94713 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sun, 27 Oct 2019 03:19:16 +0100 Subject: [PATCH 03/11] wait --- ataxx/process.py | 2 +- ataxx/uai.py | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ataxx/process.py b/ataxx/process.py index 2c99fd6..15bf743 100644 --- a/ataxx/process.py +++ b/ataxx/process.py @@ -37,6 +37,6 @@ def terminate(self): return self.process.wait() def kill(self): - self.stop.set() self.process.kill() + self.stop.set() return self.process.wait() diff --git a/ataxx/uai.py b/ataxx/uai.py index d13c9fe..d421dae 100644 --- a/ataxx/uai.py +++ b/ataxx/uai.py @@ -77,10 +77,14 @@ def uai(self): self.send_line("uai") self.uaiok_received.wait() - def isready(self): + def isready(self, to=0): with self.readyok_received: self.send_line("isready") - self.readyok_received.wait() + + if to: + self.readyok_received.wait(to) + else: + self.readyok_received.wait() def uainewgame(self): self.send_line("uainewgame") @@ -91,25 +95,19 @@ def position(self, fen, moves=None): else: self.send_line(F"position fen {fen}") - def go(self, times=None, movetime=None, depth=None, nodes=None): + def go(self, times=None, movetime=None, depth=None, nodes=None, maxwait=None): self.bestmove = None with self.bestmove_received: - maxwait = 0 - if times: btime, wtime, binc, winc = times self.send_line(F"go btime {btime} wtime {wtime} binc {binc} winc {winc}") - maxwait = (btime + wtime) / 2.0 # FIXME elif movetime: self.send_line(F"go movetime {movetime}") - maxwait = movetime elif depth: self.send_line(F"go depth {depth}") - maxwait = None elif nodes: self.send_line(F"go nodes {nodes} simulations {nodes}") - maxwait = None if maxwait: self.bestmove_received.wait(maxwait) From 5c0d1c53f1849e912be061ba473c35ae29c30359 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Fri, 1 Nov 2019 21:33:46 +0100 Subject: [PATCH 04/11] zobrist hashing --- ataxx/zobrist.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ataxx/zobrist.py diff --git a/ataxx/zobrist.py b/ataxx/zobrist.py new file mode 100644 index 0000000..1e0c92d --- /dev/null +++ b/ataxx/zobrist.py @@ -0,0 +1,59 @@ +import ataxx + +turn = 0x2e98304a94e1000d + +piece = [ + [ + 0xddd67db865dc92f9, 0x31ad7f3d49884764, 0xfc810e82600d77ed, + 0xa329bc2fe9a585c2, 0x0dd7013c7b5f9ee0, 0xcbfb18e330c5152b, + 0x5ca13c8237e969f0, 0xcfe82be3298f4860, 0xd74ee79ab8cd59d4, + 0x76c9804b3dd3dd9a, 0x62fe43ac3416344b, 0x84f44cfc5640f1fb, + 0xee80f44f274d194f, 0x9e132307eaa1062a, 0x8ef3b83e05104b8c, + 0x7458cb4e38a56b1b, 0x496ae857824032b5, 0x297892ab4aed9139, + 0x2c63a6ed1e5c503b, 0xb806e46b057da75c, 0x8e3ee3fc5ea21f4d, + 0x7bb71073c6b32631, 0xa34cbc35fea7986d, 0x2cf34121f29c2073, + 0x3ff14ac1cfb3ac44, 0x9a1f60c67467b9eb, 0x4f69347f2d665427, + 0x4d51d73c213217bc, 0x6b3089c84d5d78e3, 0xa2923e3c0bbc0f50, + 0xe4931d122c14c947, 0xa1b03c639fe421a7, 0x2789a4dc1574310c, + 0x4e3f96a633e0ede0, 0xe4f29bf5ff2869df, 0x72e7fe1c77af10b3, + 0x245e4d0677acbd90, 0x5eb16a0a6cff1f55, 0x26e93216b330c067, + 0x0eced566d7522ad3, 0xdc64bc04aa39e421, 0x401987ae3cb114db, + 0xd0d0b88ece5f0c16, 0x13275209f4ddff5a, 0xffb20d8b83a2ce28, + 0x8f2100bf98b54217, 0x3769a2d70b7209e7, 0x61b3cca215f1a975, + 0x8d1158e421453a0f, + ], + [ + 0xd33fd8453c18380c, 0xd7949af7fa5e24ca, 0xa75e294be7913781, + 0x3b615f1e3322be8c, 0xe4c885ecb48d6187, 0x9b92f432a8d105f3, + 0xbff867477fb93a23, 0x5ff3e0b5e6e72ab4, 0xe60144f2900bc655, + 0xf29a0de00bdc3aee, 0xde9e042c06230fd8, 0x4264ec62f70ba708, + 0x5f175b53ebb138c7, 0x439e71025499179e, 0xcf39b10d6a8437fd, + 0x0fcf1fc30f9e0de4, 0x6a502645f6a31fef, 0xd47dd00cf21ddfdd, + 0x7e4ca0ec0c2e7f68, 0x37401f0941e5a469, 0x3efddb557fd01cb4, + 0x846bfb50b6965c90, 0xc0fa4a424c735605, 0xe5fbff674c562239, + 0x3826959008ef40f0, 0x4a712b5964a4b7a1, 0x68f594a0740c456f, + 0x1ee9e39d56bd32e6, 0x08ebd76592898d31, 0x3df7c91613fb946d, + 0xef3e760a57d5a399, 0x6ef3128154e16b88, 0x15e27065e3d4c108, + 0xd5fe096a16b5eed2, 0xb2964fa8f1bddad1, 0x3ebb6f3af8a61dc5, + 0x9776dc9ebc5243d2, 0x4660bff8b53c4a78, 0x73507ecb48163932, + 0x05967644e76ae2f8, 0x9c53ce9f0fdcb066, 0xcf6e214c6acea558, + 0x53ee7540169475d8, 0xc8a49004b87946b8, 0x1d75d48ff9dc94b2, + 0xec4f8821305bbd96, 0x43ed7506a2bd6b16, 0xf0eb5d0784e3eb03, + 0xe578cac94b342a86, + ], + ] + +def calculate_hash(b): + key = 0 + + if b.turn == ataxx.BLACK: + key ^= turn + + for y in range(7): + for x in range(7): + if b.get(x, y) == ataxx.BLACK: + key ^= piece[0][y * 7 + x] + elif b.get(x, y) == ataxx.WHITE: + key ^= piece[1][y * 7 + x] + + return key From 7a3ceec5542ab777bc933bf70e1750fe0b570197 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 19:53:19 +0100 Subject: [PATCH 05/11] does not work --- ataxx/__init__.py | 17 +++++++++++++++++ ataxx/zobrist.py | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/ataxx/__init__.py b/ataxx/__init__.py index e0c6903..8f5bb6f 100644 --- a/ataxx/__init__.py +++ b/ataxx/__init__.py @@ -2,6 +2,8 @@ __url__ = "https://github.com/kz04px/python-ataxx" __version__ = "2.0.0" +from ataxx.zobrist import calculate_hash, get_turn_hash, get_sq_hash + BLACK, WHITE, GAP, EMPTY = 0, 1, 2, 3 SINGLES = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] DOUBLES = [(-2, -2), (-2, -1), (-2, 0), (-2, 1), (-2, 2), (-1, -2), (-1, 2), (0, -2), (0, 2), (1, -2), (1, 2), (2, -2), (2, -1), (2, 0), (2, 1), (2, 2)] @@ -302,6 +304,8 @@ def set_fen(self, fen): # Save fen self._start_fen = ' '.join(parts) + self.hash = calculate_hash(self) + return True def makemove(self, move): @@ -320,12 +324,16 @@ def makemove(self, move): # Null move if move == Move.null(): self.turn = opponent + self.hash ^= get_turn_hash(self.turn) self.history.append(move) self.halfmove_clock += 1 return self.set(move.to_x, move.to_y, self.turn) + self.hash ^= get_sq_hash(move.to_x, move.to_y, self.turn) + if move.is_double(): + self.hash ^= get_sq_hash(move.fr_x, move.fr_y, self.get(move.fr_x, move.fr_y)) self.set(move.fr_x, move.fr_y, EMPTY) for idx, (dx, dy) in enumerate(SINGLES): @@ -333,11 +341,14 @@ def makemove(self, move): if self.get(x, y) == opponent: move.flipped[idx] = True self.set(x, y, self.turn) + self.hash ^= get_sq_hash(x, y, opponent) + self.hash ^= get_sq_hash(x, y, self.turn) else: move.flipped[idx] = False self.history.append(move) self.turn = opponent + self.hash ^= get_turn_hash(self.turn) self.halfmove_clock += 1 if move.is_single(): self.halfmove_clock = 0 @@ -362,18 +373,24 @@ def undo(self): return # Remove the piece we placed + self.hash ^= get_sq_hash(move.to_x, move.to_y, self.get(move.to_x, move.to_y)) self.set(move.to_x, move.to_y, EMPTY) # Restore the piece we removed if move.is_double(): + self.hash ^= get_sq_hash(move.fr_x, move.fr_y, self.get(move.fr_x, move.fr_y)) self.set(move.fr_x, move.fr_y, us) # Restore the pieces we captured for idx, val in enumerate(move.flipped): if val: dx, dy = SINGLES[idx] + self.hash ^= get_sq_hash(move.to_x + dx, move.to_y + dy, self.get(move.to_x + dx, move.to_y + dy)) self.set(move.to_x + dx, move.to_y + dy, them) + def get_hash(self): + return self.hash + def main_line(self): """Return the list of moves that have been applied to the board""" diff --git a/ataxx/zobrist.py b/ataxx/zobrist.py index 1e0c92d..03176b8 100644 --- a/ataxx/zobrist.py +++ b/ataxx/zobrist.py @@ -57,3 +57,15 @@ def calculate_hash(b): key ^= piece[1][y * 7 + x] return key + +def get_turn_hash(side): + return key if side == ataxx.BLACK else 0 + +def get_sq_hash(x, y, side): + if side == ataxx.BLACK: + return piece[0][y * 7 + x] + + if side == ataxx.WHITE: + return piece[1][y * 7 + x] + + return 0 From 2154f1b7b4f6f8a01ff64320e8938857d59d7ff8 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 20:03:29 +0100 Subject: [PATCH 06/11] still broken --- ataxx/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ataxx/__init__.py b/ataxx/__init__.py index 8f5bb6f..0d46641 100644 --- a/ataxx/__init__.py +++ b/ataxx/__init__.py @@ -368,6 +368,7 @@ def undo(self): move = self.history.pop() self.halfmove_clock = self._halfmove_stack.pop() self.turn = us + self.hash ^= get_turn_hash(self.turn) if move == Move.null(): return @@ -385,8 +386,9 @@ def undo(self): for idx, val in enumerate(move.flipped): if val: dx, dy = SINGLES[idx] - self.hash ^= get_sq_hash(move.to_x + dx, move.to_y + dy, self.get(move.to_x + dx, move.to_y + dy)) self.set(move.to_x + dx, move.to_y + dy, them) + self.hash ^= get_sq_hash(move.to_x + dx, move.to_y + dy, us) + self.hash ^= get_sq_hash(move.to_x + dx, move.to_y + dy, them) def get_hash(self): return self.hash From f615b36fb9d65da2a4c54bb11cc3907990fc4a41 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 20:04:25 +0100 Subject: [PATCH 07/11] still broken --- ataxx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ataxx/__init__.py b/ataxx/__init__.py index 0d46641..80c9aaa 100644 --- a/ataxx/__init__.py +++ b/ataxx/__init__.py @@ -360,6 +360,7 @@ def undo(self): us = WHITE else: us = BLACK + self.hash ^= get_turn_hash(self.turn) them = self.turn if self.turn == BLACK: @@ -368,7 +369,6 @@ def undo(self): move = self.history.pop() self.halfmove_clock = self._halfmove_stack.pop() self.turn = us - self.hash ^= get_turn_hash(self.turn) if move == Move.null(): return From 476cf2250575333d6e7f02c748c9a8cf81700a7d Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 20:09:25 +0100 Subject: [PATCH 08/11] still broken --- ataxx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ataxx/__init__.py b/ataxx/__init__.py index 80c9aaa..3a58f91 100644 --- a/ataxx/__init__.py +++ b/ataxx/__init__.py @@ -333,7 +333,7 @@ def makemove(self, move): self.hash ^= get_sq_hash(move.to_x, move.to_y, self.turn) if move.is_double(): - self.hash ^= get_sq_hash(move.fr_x, move.fr_y, self.get(move.fr_x, move.fr_y)) + self.hash ^= get_sq_hash(move.fr_x, move.fr_y, them) self.set(move.fr_x, move.fr_y, EMPTY) for idx, (dx, dy) in enumerate(SINGLES): From dc4ffeef551d6cd166a32ff02a9f07f58873d3fb Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 20:10:32 +0100 Subject: [PATCH 09/11] still broken --- ataxx/zobrist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ataxx/zobrist.py b/ataxx/zobrist.py index 03176b8..651f8ff 100644 --- a/ataxx/zobrist.py +++ b/ataxx/zobrist.py @@ -59,7 +59,7 @@ def calculate_hash(b): return key def get_turn_hash(side): - return key if side == ataxx.BLACK else 0 + return turn if side == ataxx.BLACK else 0 def get_sq_hash(x, y, side): if side == ataxx.BLACK: From 56a4afb0e1569b72af80e5c3ea4bc02e108e3d2f Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 2 Nov 2019 20:17:03 +0100 Subject: [PATCH 10/11] 1 ply works --- ataxx/__init__.py | 4 ++-- ataxx/zobrist.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ataxx/__init__.py b/ataxx/__init__.py index 3a58f91..dce7066 100644 --- a/ataxx/__init__.py +++ b/ataxx/__init__.py @@ -333,7 +333,7 @@ def makemove(self, move): self.hash ^= get_sq_hash(move.to_x, move.to_y, self.turn) if move.is_double(): - self.hash ^= get_sq_hash(move.fr_x, move.fr_y, them) + self.hash ^= get_sq_hash(move.fr_x, move.fr_y, self.turn) self.set(move.fr_x, move.fr_y, EMPTY) for idx, (dx, dy) in enumerate(SINGLES): @@ -379,7 +379,7 @@ def undo(self): # Restore the piece we removed if move.is_double(): - self.hash ^= get_sq_hash(move.fr_x, move.fr_y, self.get(move.fr_x, move.fr_y)) + self.hash ^= get_sq_hash(move.fr_x, move.fr_y, us) self.set(move.fr_x, move.fr_y, us) # Restore the pieces we captured diff --git a/ataxx/zobrist.py b/ataxx/zobrist.py index 651f8ff..f2eb5b3 100644 --- a/ataxx/zobrist.py +++ b/ataxx/zobrist.py @@ -59,7 +59,7 @@ def calculate_hash(b): return key def get_turn_hash(side): - return turn if side == ataxx.BLACK else 0 + return turn def get_sq_hash(x, y, side): if side == ataxx.BLACK: From ff7021a75ab411a7eb6743760dc788849d791526 Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Tue, 5 Nov 2019 16:05:58 +0100 Subject: [PATCH 11/11] from_board: return last node so that one can easily add e.g. an adjudication reason or so --- ataxx/pgn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ataxx/pgn.py b/ataxx/pgn.py index d5ea972..cfc219c 100644 --- a/ataxx/pgn.py +++ b/ataxx/pgn.py @@ -172,6 +172,7 @@ def from_board(self, board): if board.start_fen() != ataxx.FEN_STARTPOS: self.headers["FEN"] = board.start_fen() self.headers["SetUp"] = "1" + return node def set_white(self, w): self.headers["White"] = w @@ -200,7 +201,7 @@ def recurse(self, children=[], depth=1): string += "" # Print new move number - if depth%2 == 1: + if depth&1 == 1: string += F"{(depth+1)//2}. " # Print move number if we just left a comment or variation else: