DarshanScripts commited on
Commit
51c8e36
·
verified ·
1 Parent(s): a5e6d75

Upload stratego/utils/move_processor.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. stratego/utils/move_processor.py +208 -0
stratego/utils/move_processor.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Move Processor: Utilities for parsing and analyzing moves.
2
+
3
+ Handles:
4
+ - Move parsing from LLM output
5
+ - Piece extraction from board
6
+ - Board state serialization
7
+ - Move direction computation
8
+ """
9
+
10
+ import re
11
+ from typing import Optional, Tuple, List
12
+ from dataclasses import dataclass
13
+
14
+
15
+ @dataclass
16
+ class MoveDetails:
17
+ """Parsed move information for logging."""
18
+ src_pos: str = ""
19
+ dst_pos: str = ""
20
+ piece_type: str = ""
21
+ target_piece: str = "" # Piece at destination (if any)
22
+ move_direction: str = "" # N/S/E/W
23
+ board_state: str = "" # Serialized board state
24
+ available_moves: str = "" # List of valid moves
25
+
26
+
27
+ def parse_move(action: str) -> Tuple[str, str]:
28
+ """
29
+ Extract source and destination positions from move string.
30
+
31
+ Args:
32
+ action: Move string like "[D4 E4]"
33
+
34
+ Returns:
35
+ Tuple of (src_pos, dst_pos) or ("", "") if parsing fails
36
+ """
37
+ move_pattern = r'\[([A-J]\d+)\s+([A-J]\d+)\]'
38
+ match = re.search(move_pattern, action)
39
+ if match:
40
+ return match.group(1), match.group(2)
41
+ return "", ""
42
+
43
+
44
+ def get_piece_at_position(board: List[List], position: str) -> str:
45
+ """
46
+ Get piece type at a board position.
47
+
48
+ Args:
49
+ board: 10x10 game board
50
+ position: Position string like "D4"
51
+
52
+ Returns:
53
+ piece_type or "" if not found
54
+ """
55
+ if not position:
56
+ return ""
57
+
58
+ try:
59
+ row = ord(position[0]) - ord('A')
60
+ col = int(position[1:])
61
+ piece = board[row][col]
62
+
63
+ if piece and isinstance(piece, dict) and 'rank' in piece:
64
+ return piece['rank']
65
+ except (IndexError, ValueError, TypeError):
66
+ pass
67
+
68
+ return ""
69
+
70
+
71
+ def compute_move_direction(src_pos: str, dst_pos: str) -> str:
72
+ """
73
+ Compute move direction from source to destination.
74
+
75
+ Args:
76
+ src_pos: Source position like "D4"
77
+ dst_pos: Destination position like "E4"
78
+
79
+ Returns:
80
+ Direction: "N", "S", "E", "W", or "" if invalid
81
+ """
82
+ if not src_pos or not dst_pos:
83
+ return ""
84
+ try:
85
+ src_row = ord(src_pos[0]) - ord('A')
86
+ dst_row = ord(dst_pos[0]) - ord('A')
87
+ src_col = int(src_pos[1:])
88
+ dst_col = int(dst_pos[1:])
89
+
90
+ if dst_row < src_row:
91
+ return "N" # Moving up (toward A)
92
+ elif dst_row > src_row:
93
+ return "S" # Moving down (toward J)
94
+ elif dst_col > src_col:
95
+ return "E" # Moving right
96
+ elif dst_col < src_col:
97
+ return "W" # Moving left
98
+ except (IndexError, ValueError):
99
+ pass
100
+ return ""
101
+
102
+
103
+ def serialize_board(board: List[List], player_id: int = 0) -> str:
104
+ """
105
+ Serialize board state to a compact string for training.
106
+ Format: Each cell as "RC:PIECE" where R=row, C=col, PIECE=short rank or ?/~/.
107
+
108
+ Args:
109
+ board: 10x10 game board
110
+ player_id: Current player (to show their pieces, hide opponent's)
111
+
112
+ Returns:
113
+ Compact board string representation
114
+ """
115
+ # Short forms matching the game board display (to store it in csv and datasets)
116
+ RANK_SHORT = {
117
+ "Flag": "FL",
118
+ "Spy": "SP",
119
+ "Scout": "SC",
120
+ "Miner": "MN",
121
+ "Sergeant": "SG",
122
+ "Lieutenant": "LT",
123
+ "Captain": "CP",
124
+ "Major": "MJ",
125
+ "Colonel": "CL",
126
+ "General": "GN",
127
+ "Marshal": "MS",
128
+ "Bomb": "BM",
129
+ }
130
+
131
+ if not board:
132
+ return ""
133
+
134
+ cells = []
135
+ row_labels = "ABCDEFGHIJ"
136
+
137
+ for r, row in enumerate(board):
138
+ for c, cell in enumerate(row):
139
+ pos = f"{row_labels[r]}{c}"
140
+ if cell is None:
141
+ cells.append(f"{pos}:.")
142
+ elif cell == "~":
143
+ cells.append(f"{pos}:~")
144
+ elif isinstance(cell, dict):
145
+ rank = cell.get('rank', '?')
146
+ owner = cell.get('player', -1)
147
+ if owner == player_id:
148
+ # Show own piece rank (use short form)
149
+ short_rank = RANK_SHORT.get(rank, rank)
150
+ cells.append(f"{pos}:{short_rank}")
151
+ else:
152
+ # Hide opponent's piece
153
+ cells.append(f"{pos}:?")
154
+ else:
155
+ cells.append(f"{pos}:{cell}")
156
+
157
+ return "|".join(cells)
158
+
159
+
160
+ def extract_available_moves(observation: str) -> str:
161
+ """
162
+ Extract available moves from observation string.
163
+
164
+ Args:
165
+ observation: Full observation text from environment
166
+
167
+ Returns:
168
+ Comma-separated list of available moves
169
+ """
170
+ # Pattern: "Available Moves: [D1 E1], [D5 E5], ..."
171
+ match = re.search(r'Available Moves:\s*(.+?)(?:\n|$)', observation)
172
+ if match:
173
+ moves_str = match.group(1).strip()
174
+ # Extract just the moves like "[D1 E1]"
175
+ moves = re.findall(r'\[[A-J]\d+\s+[A-J]\d+\]', moves_str)
176
+ return ",".join(moves)
177
+ return ""
178
+
179
+
180
+ def process_move(action: str, board: List[List], observation: str = "", player_id: int = 0) -> MoveDetails:
181
+ """
182
+ Process a move and extract all relevant details for logging.
183
+
184
+ Args:
185
+ action: Move string from LLM
186
+ board: Current game board
187
+ observation: Full observation text (for available moves)
188
+ player_id: Current player ID
189
+
190
+ Returns:
191
+ MoveDetails dataclass with all extracted info
192
+ """
193
+ src_pos, dst_pos = parse_move(action)
194
+ piece_type = get_piece_at_position(board, src_pos)
195
+ target_piece = get_piece_at_position(board, dst_pos)
196
+ move_direction = compute_move_direction(src_pos, dst_pos)
197
+ board_state = serialize_board(board, player_id)
198
+ available_moves = extract_available_moves(observation)
199
+
200
+ return MoveDetails(
201
+ src_pos=src_pos,
202
+ dst_pos=dst_pos,
203
+ piece_type=piece_type,
204
+ target_piece=target_piece,
205
+ move_direction=move_direction,
206
+ board_state=board_state,
207
+ available_moves=available_moves
208
+ )