Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions scripts/reflectiveness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import subprocess
import chess
import random
import time
import sys

ENGINE_PATH = sys.argv[1] if len(sys.argv) > 1 else "./clockwork"

NUM_POSITIONS = 10000
MAX_RANDOM_PLIES = 40


class Engine:
def __init__(self, path):
self.proc = subprocess.Popen(
path,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
bufsize=1
)
self._init_uci()

def send(self, cmd):
self.proc.stdin.write(cmd + "\n")
self.proc.stdin.flush()

def read_until(self, keyword):
while True:
line = self.proc.stdout.readline()
if keyword in line:
return line.strip()

def _init_uci(self):
self.send("uci")
self.read_until("uciok")
self.send("isready")
self.read_until("readyok")

def eval(self, board):
fen = board.fen()
self.send(f"position fen {fen}")
self.send("eval")

# Expect: "Evaluation (stm): xxxx"
while True:
line = self.proc.stdout.readline()
if "Evaluation" in line:
try:
val = int(line.strip().split()[-1])
return val
except:
continue

def quit(self):
self.send("quit")
self.proc.kill()


def random_position():
board = chess.Board()

n_moves = random.randint(0, MAX_RANDOM_PLIES)

for _ in range(n_moves):
if board.is_game_over():
break

moves = list(board.legal_moves)
if not moves:
break

move = random.choice(moves)
board.push(move)

# No positions in check
if board.is_check():
return None

return board


def mirror_board(board):
return board.mirror()

def main():
engine = Engine(ENGINE_PATH)

tested = 0
failures = 0

start_time = time.time()

while tested < NUM_POSITIONS:
board = random_position()
if board is None:
continue

mirrored = mirror_board(board)

eval1 = engine.eval(board)
eval2 = engine.eval(mirrored)

if eval1 is None or eval2 is None:
continue

if eval1 != eval2:
failures += 1
print()
print("Asymmetry detected!")
print("FEN:", board.fen(), " --- ", mirrored.fen())
print("Eval:", eval1)
print("Mirrored Eval:", eval2)
print("Sum:", eval1 - eval2)

tested += 1

if tested % 100 == 0:
elapsed = time.time() - start_time
print(f"Tested: {tested}, Failures: {failures}, Time: {elapsed:.1f}s")

engine.quit()

print("\n==== DONE ====")
print(f"Positions tested: {tested}")
print(f"Failures: {failures}")
print(f"Failure rate: {failures / tested:.6f}")


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions src/bitboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "square.hpp"
#include "util/bit.hpp"
#include "util/types.hpp"
#include <bit>

namespace Clockwork {

Expand Down Expand Up @@ -51,7 +52,7 @@
return file_mask(2) | file_mask(3) | file_mask(4) | file_mask(5);
}

[[nodiscard]] static Bitboard fill_verticals(const Bitboard mask) {

Check warning on line 55 in src/bitboard.hpp

View workflow job for this annotation

GitHub Actions / Linter / cpp-linter

src/bitboard.hpp:55:65 [readability-identifier-naming]

invalid case style for constant 'mask'
Bitboard result = mask | (mask >> 8);
result |= result >> 16;
result |= result >> 32;
Expand All @@ -71,9 +72,8 @@
}

[[nodiscard]] Square msb() const {
return Square{static_cast<u8>(std::countl_zero(m_raw))};
return Square{static_cast<u8>(std::bit_width(m_raw) - 1)};
}

[[nodiscard]] Square lsb() const {
return Square{static_cast<u8>(std::countr_zero(m_raw))};
}
Expand Down Expand Up @@ -127,7 +127,7 @@
return shift(dir);
}

[[nodiscard]] Bitboard shift_relative(Color perspective, Direction dir, const i32 times) const {

Check warning on line 130 in src/bitboard.hpp

View workflow job for this annotation

GitHub Actions / Linter / cpp-linter

src/bitboard.hpp:130:87 [readability-identifier-naming]

invalid case style for constant 'times'
if (perspective == Color::Black) {
dir = static_cast<Direction>((static_cast<u32>(dir) + 4) % 8);
}
Expand Down
2 changes: 1 addition & 1 deletion src/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
struct PieceMask {
public:
constexpr PieceMask() :
m_raw(0) {

Check warning on line 37 in src/board.hpp

View workflow job for this annotation

GitHub Actions / Linter / cpp-linter

src/board.hpp:37:9 [modernize-use-default-member-init]

member initializer for 'm_raw' is redundant
}

constexpr explicit PieceMask(u16 raw) :
Expand All @@ -55,7 +55,7 @@
}

[[nodiscard]] PieceId msb() const {
return PieceId{static_cast<u8>(std::countl_zero(m_raw))};
return PieceId{static_cast<u8>(std::bit_width(m_raw) - 1)};
}

[[nodiscard]] PieceId lsb() const {
Expand Down
Loading
Loading