Analyze chess evidence#
Exact facts, move effects, authored puzzle correctness, and counterfactual validity are different kinds of evidence. This notebook keeps them separate while using one ordinary python-chess board. No neural model is required.
[1]:
import chess
from lczerolens import (
Puzzle,
PuzzleContinuation,
PuzzleSolution,
analyze_line,
analyze_move,
sibling_counterfactual,
)
from lczerolens.facts import FactPerspective, MaterialAnalyzer
board = chess.Board()
white_material = MaterialAnalyzer(FactPerspective.WHITE).analyze(board)
move = analyze_move(board, "e2e4")
line = analyze_line(board, ["e2e4", "e7e5", "g1f3"])
{
"white_material": white_material.value,
"move_effects": [effect.value for effect in move.effects],
"changed_facts": len(move.changed),
"line_plies": len(line.steps),
"history_complete": line.final_position.history_complete,
"terminal": line.terminal.is_terminal,
}
[1]:
{'white_material': 39,
'move_effects': [],
'changed_facts': 18,
'line_plies': 3,
'history_complete': True,
'terminal': False}
A sibling counterfactual compares two legal children of the same parent. Its validity and history guarantee are explicit; they do not say that either move is strategically better.
[2]:
pair = sibling_counterfactual(board, factual="e2e4", alternative="d2d4")
assert pair.succeeded
{
"validity": pair.validity.value,
"shared_parent": pair.history.shared_parent,
"reachability_proven": pair.history.reachability_proven,
"factual_fen": pair.factual.fen,
"alternative_fen": pair.alternative.fen,
}
[2]:
{'validity': 'history_consistent',
'shared_parent': True,
'reachability_proven': True,
'factual_fen': 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1',
'alternative_fen': 'rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1'}
Puzzle correctness comes from an authored solution tree. A solution leaf may mark the task solved even if the resulting chess position is not terminal.
[3]:
task = Puzzle.from_board(
board,
PuzzleSolution((PuzzleContinuation("e2e4"), PuzzleContinuation("d2d4"))),
)
accepted = tuple(move.uci() for move in task.accepted_moves())
solved = task.grade(["e2e4"])
failed = task.grade(["g1f3"])
{"accepted": accepted, "solved": solved.status.value, "failed": failed.status.value}
[3]:
{'accepted': ('e2e4', 'd2d4'), 'solved': 'solved', 'failed': 'failed'}