kaupane commited on
Commit
4b6bfbd
·
verified ·
1 Parent(s): 05151db

Upload mapping.py

Browse files
Files changed (1) hide show
  1. mapping.py +141 -0
mapping.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Tuple, Set
2
+
3
+ # --- Constants --- #
4
+ MAX_HALFMOVES = 128 # cap for embedding table size
5
+ MAX_FULLMOVES = 256 # cap for embedding table size
6
+
7
+ # --- Helper Mappings --- #
8
+ PIECE_TO_IDX: Dict[str, int] = {
9
+ 'P': 0, 'N': 1, 'B': 2, 'R': 3, 'Q': 4, 'K': 5,
10
+ 'p': 6, 'n': 7, 'b': 8, 'r': 9, 'q': 10, 'k': 11,
11
+ '.': 12
12
+ }
13
+ IDX_TO_PIECE: Dict[int, str] = {v: k for k, v in PIECE_TO_IDX.items()}
14
+ EMPTY_SQ_IDX = PIECE_TO_IDX['.']
15
+ # Map algebraic square notation (e.g., 'a1', 'h8') to 0-63 index
16
+ # a1=0, b1=1, ..., h1=7, a2=8, ..., h8=63
17
+ SQUARE_TO_IDX: Dict[str, int] = {
18
+ f"{file}{rank}": (rank - 1) * 8 + (ord(file) - ord('a'))
19
+ for rank in range(1, 9)
20
+ for file in 'abcdefgh'
21
+ }
22
+ IDX_TO_SQUARE: Dict[int, str] = {v: k for k, v in SQUARE_TO_IDX.items()}
23
+
24
+
25
+
26
+ # --- Coordinate and Notation Helpers ---
27
+
28
+ # Precompute maps for efficiency
29
+ _IDX_TO_COORDS: Dict[int, Tuple[int, int]] = {i: (i // 8, i % 8) for i in range(64)} # (rank, file) 0-7
30
+ _COORDS_TO_IDX: Dict[Tuple[int, int], int] = {v: k for k, v in _IDX_TO_COORDS.items()}
31
+ _IDX_TO_ALG: Dict[int, str] = {
32
+ i: f"{chr(ord('a') + file)}{rank + 1}"
33
+ for i, (rank, file) in _IDX_TO_COORDS.items()
34
+ }
35
+ _ALG_TO_IDX: Dict[str, int] = {v: k for k, v in _IDX_TO_ALG.items()}
36
+
37
+ def _coords_to_alg(r: int, f: int) -> str:
38
+ """Converts 0-indexed (rank, file) to algebraic notation."""
39
+ if 0 <= r < 8 and 0 <= f < 8:
40
+ return f"{chr(ord('a') + f)}{r + 1}"
41
+ # This should not happen with valid indices, but good for safety
42
+ raise ValueError(f"Invalid coordinates: ({r}, {f})")
43
+
44
+ def generate_structurally_valid_move_map() -> Dict[str, int]:
45
+ """
46
+ Generates a dictionary mapping chess moves that are geometrically possible
47
+ by *some* standard piece (K, Q, R, B, N, or P) to unique integer indices.
48
+ It excludes moves that are structurally impossible for any piece to make
49
+ in one turn (e.g., a1->h5 for non-knight).
50
+
51
+ Includes standard UCI promotions (e.g., "e7e8q"), replacing the
52
+ corresponding simple pawn move to the final rank (e.g., "e7e8").
53
+ This is based purely on piece movement geometry, not the current board state.
54
+
55
+ Returns:
56
+ Dict[str, int]: A map from the valid UCI move string to a unique
57
+ integer index (0 to N-1). The size N is expected
58
+ to be around 1800-1900.
59
+ """
60
+ valid_moves: Set[str] = set()
61
+ # Keep track of base moves (like 'e7e8') that are replaced by promotions
62
+ # according to UCI standard.
63
+ promo_base_moves_to_exclude: Set[str] = set()
64
+
65
+ # 1. Generate all geometrically possible non-promotion moves
66
+ for from_idx in range(64):
67
+ from_r, from_f = _IDX_TO_COORDS[from_idx]
68
+ from_alg = _IDX_TO_ALG[from_idx]
69
+
70
+ for to_idx in range(64):
71
+ if from_idx == to_idx:
72
+ continue
73
+
74
+ to_r, to_f = _IDX_TO_COORDS[to_idx]
75
+ to_alg = _IDX_TO_ALG[to_idx]
76
+ dr, df = to_r - from_r, to_f - from_f
77
+ abs_dr, abs_df = abs(dr), abs(df)
78
+
79
+ # Check if the geometry matches any standard piece movement
80
+ # Note: Queen moves are covered by Rook + Bishop checks.
81
+ # Note: Pawn single pushes/captures are covered by King/Rook/Bishop geometry.
82
+ # Note: Pawn double pushes are covered by Rook geometry.
83
+ is_king_move = max(abs_dr, abs_df) == 1
84
+ is_knight_move = (abs_dr == 2 and abs_df == 1) or (abs_dr == 1 and abs_df == 2)
85
+ is_rook_move = dr == 0 or df == 0 # Includes King horiz/vert & pawn double push
86
+ is_bishop_move = abs_dr == abs_df # Includes King diagonal & pawn capture/push
87
+
88
+ if is_king_move or is_knight_move or is_rook_move or is_bishop_move:
89
+ uci_move = f"{from_alg}{to_alg}"
90
+ valid_moves.add(uci_move)
91
+
92
+
93
+ # 2. Generate promotion moves explicitly and mark base moves for exclusion
94
+ promo_pieces = ['q', 'r', 'b', 'n']
95
+ for from_f in range(8):
96
+ # White promotions (from rank 7 (idx 6) to rank 8 (idx 7))
97
+ from_r_w, to_r_w = 6, 7
98
+ if from_r_w != 7: # Ensure we are on the correct rank before promotion
99
+ from_alg_w = _coords_to_alg(from_r_w, from_f)
100
+ # Possible destinations: push (df=0), capture left (df=-1), capture right (df=1)
101
+ for df in [-1, 0, 1]:
102
+ to_f_w = from_f + df
103
+ if 0 <= to_f_w < 8:
104
+ to_alg_w = _coords_to_alg(to_r_w, to_f_w)
105
+ base_move = f"{from_alg_w}{to_alg_w}"
106
+ #promo_base_moves_to_exclude.add(base_move) # Mark e.g. "e7e8" for exclusion
107
+ for p in promo_pieces:
108
+ valid_moves.add(f"{base_move}{p}") # Add e.g. "e7e8q"
109
+
110
+ # Black promotions (from rank 2 (idx 1) to rank 1 (idx 0))
111
+ from_r_b, to_r_b = 1, 0
112
+ if from_r_b != 0: # Ensure we are on the correct rank before promotion
113
+ from_alg_b = _coords_to_alg(from_r_b, from_f)
114
+ # Possible destinations: push (df=0), capture left (df=-1), capture right (df=1)
115
+ for df in [-1, 0, 1]:
116
+ to_f_b = from_f + df
117
+ if 0 <= to_f_b < 8:
118
+ to_alg_b = _coords_to_alg(to_r_b, to_f_b)
119
+ base_move = f"{from_alg_b}{to_alg_b}"
120
+ #promo_base_moves_to_exclude.add(base_move) # Mark e.g. "e2e1" for exclusion
121
+ for p in promo_pieces:
122
+ valid_moves.add(f"{base_move}{p}") # Add e.g. "e2e1q"
123
+
124
+ # 3. Remove the base moves that were replaced by promotions
125
+ final_valid_moves = valid_moves - promo_base_moves_to_exclude
126
+
127
+ # 4. Add draw claim
128
+ final_valid_moves.add("<claim_draw>")
129
+
130
+ # 5. Create the final map with sorted keys for deterministic indices
131
+ sorted_moves = sorted(list(final_valid_moves))
132
+ move_map = {move: i for i, move in enumerate(sorted_moves)}
133
+
134
+ # Optional: Print the number of moves found for verification
135
+ # print(f"Generated {len(move_map)} structurally valid unique UCI moves.")
136
+
137
+ return move_map
138
+
139
+
140
+ UCI_MOVE_TO_IDX = generate_structurally_valid_move_map()
141
+ IDX_TO_UCI_MOVE = {v:k for k,v in UCI_MOVE_TO_IDX.items()}