summaryrefslogtreecommitdiff
path: root/annotate_pgns/quiet_common.py
blob: 77544ef630873dffa0e47086bb9a806c0539fe94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/usr/bin/env python3
"""Shared quiet-position predicate, per NNUE.md: exclude positions
where the side to move is in check, or where a given move is an
SEE-positive (>=50cp) capture.

Used two ways by this pipeline, with a deliberately different choice
of *which* move gets SEE-checked in each case:

- Pre-search filtering (annotate.py): checked against the move actually
  played in the source human game, since that's the only move known
  before the engine has searched anything -- the whole point is to
  skip the sd-N search entirely for positions that would be discarded
  anyway.
- Post-hoc filtering (quiet_filter.py, quiet_filter_games.py): checked
  against the first move of the already-recorded sd-N search PV (the
  engine's own considered-best reply), per NNUE.md's original filter
  design. This remains the definition used for data already annotated
  before pre-filtering existed, and is NOT the same training
  distribution as the game-move variant above -- do not mix rows
  produced under the two definitions into one training run without
  accounting for that.

Both call the same `position_is_quiet` predicate once a concrete move
is in hand; only how that move is obtained differs.

Reuses eval_tune/tune_eval_dna.py's existing static_exchange_eval() --
a classic swap-list SEE (ignores pins/x-rays/discoveries, a
documented, accepted simplification; see check_see_against_engine.py
for a cross-check against the engine's real SEE()) -- rather than
reimplementing it.
"""
import os
import sys
from pathlib import Path

_EVAL_TUNE_DIR = Path(__file__).resolve().parent.parent / "src" / "eval_tune"
sys.path.insert(0, str(_EVAL_TUNE_DIR))
import chess  # noqa: E402

# tune_eval_dna imports dna_diff, which reads "../eval.c" relative to
# the *current working directory* at import time, not relative to its
# own file -- chdir there just for the import, then restore, so this
# module can be imported from any directory.
_cwd = os.getcwd()
os.chdir(_EVAL_TUNE_DIR)
try:
    from tune_eval_dna import static_exchange_eval, has_hanging_material  # noqa: E402,F401
finally:
    os.chdir(_cwd)

SEE_THRESHOLD = 50  # matches has_hanging_material's own default


def is_see_positive_capture(board, move, threshold=SEE_THRESHOLD):
    """board is the position *before* move. Does not mutate board."""
    if not board.is_capture(move):
        return False
    return static_exchange_eval(board, move) >= threshold


def position_is_quiet(board, move, threshold=SEE_THRESHOLD):
    """board is the position *before* move; move is whichever move the
    caller has decided to SEE-check (played move or PV's first move --
    see module docstring). True means: keep this position in the
    training set."""
    if board.is_check():
        return False
    return not is_see_positive_capture(board, move, threshold)