rogermt commited on
Commit
60aedad
·
verified ·
1 Parent(s): d69fe52

Add task 101 solver (266/266) — template stamping with multi-scale pattern matching

Browse files
Files changed (1) hide show
  1. medal-solvers/task101_solver_266.py +211 -0
medal-solvers/task101_solver_266.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Task 101 Solver — 266/266 verified
3
+
4
+ Rule:
5
+ 1. Template c2 = all c2 connected (through c2) to c1-adjacent c2
6
+ 2. Template c1 = all c1 cells
7
+ 3. Markers = remaining c2 cells
8
+ 4. Marker blocks match template c2 pattern at some scale s:
9
+ - Each template c2 cell becomes an s×s block in the marker
10
+ - Connected marker block can contain the ENTIRE scaled template c2 pattern
11
+ 5. c1 cells are stamped relative to the matched pattern
12
+
13
+ ONNX Status: NOT VIABLE — requires CC detection, dynamic pattern matching, variable grids (14x12, 17x14, 17x21)
14
+ """
15
+ import json
16
+ import numpy as np
17
+ from collections import deque
18
+
19
+
20
+ def solve_task101(inp_grid):
21
+ inp = np.array(inp_grid)
22
+ H, W = inp.shape
23
+ out = inp.copy()
24
+
25
+ c1_positions = list(map(tuple, np.argwhere(inp == 1)))
26
+ c2_positions = list(map(tuple, np.argwhere(inp == 2)))
27
+
28
+ if not c1_positions or not c2_positions:
29
+ return out.tolist()
30
+
31
+ c1_set = set(c1_positions)
32
+ c2_set = set(c2_positions)
33
+
34
+ # Find template c2: flood-fill from c1-adjacent c2 through c2
35
+ seed_c2 = set()
36
+ for pos in c2_positions:
37
+ r, c = pos
38
+ for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
39
+ if (r+dr, c+dc) in c1_set:
40
+ seed_c2.add(pos)
41
+ break
42
+
43
+ if not seed_c2:
44
+ return out.tolist()
45
+
46
+ template_c2_set = set(seed_c2)
47
+ queue = deque(seed_c2)
48
+ while queue:
49
+ pos = queue.popleft()
50
+ for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
51
+ nb = (pos[0]+dr, pos[1]+dc)
52
+ if nb in c2_set and nb not in template_c2_set:
53
+ template_c2_set.add(nb)
54
+ queue.append(nb)
55
+
56
+ template_c2 = sorted(template_c2_set)
57
+ marker_c2_set = c2_set - template_c2_set
58
+
59
+ if not marker_c2_set or not template_c2:
60
+ return out.tolist()
61
+
62
+ # Template c2 relative positions
63
+ tpl_c2_min_r = min(p[0] for p in template_c2)
64
+ tpl_c2_min_c = min(p[1] for p in template_c2)
65
+ tpl_c2_rel = [(r - tpl_c2_min_r, c - tpl_c2_min_c) for r, c in template_c2]
66
+
67
+ # Template c2 bbox dimensions
68
+ tpl_c2_h = max(r for r, c in tpl_c2_rel) + 1
69
+ tpl_c2_w = max(c for r, c in tpl_c2_rel) + 1
70
+
71
+ # Template c1 relative to template c2 TL
72
+ tpl_c1_rel = [(r - tpl_c2_min_r, c - tpl_c2_min_c) for r, c in c1_positions]
73
+
74
+ def stamp_at(origin_r, origin_c, scale):
75
+ for (tr, tc) in tpl_c1_rel:
76
+ for sr in range(scale):
77
+ for sc in range(scale):
78
+ r = origin_r + tr * scale + sr
79
+ c = origin_c + tc * scale + sc
80
+ if 0 <= r < H and 0 <= c < W and out[r, c] == 0:
81
+ out[r, c] = 1
82
+
83
+ def check_block_matches_pattern(block_cells, block_min_r, block_min_c, block_h, block_w, scale):
84
+ expected = set()
85
+ for (tr, tc) in tpl_c2_rel:
86
+ for sr in range(scale):
87
+ for sc in range(scale):
88
+ expected.add((block_min_r + tr * scale + sr, block_min_c + tc * scale + sc))
89
+ return expected == block_cells
90
+
91
+ # Group marker c2 into connected components
92
+ visited = set()
93
+ marker_blocks = []
94
+
95
+ for pos in sorted(marker_c2_set):
96
+ if pos in visited:
97
+ continue
98
+ component = set()
99
+ q = deque([pos])
100
+ visited.add(pos)
101
+ while q:
102
+ p = q.popleft()
103
+ component.add(p)
104
+ for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
105
+ nb = (p[0]+dr, p[1]+dc)
106
+ if nb in marker_c2_set and nb not in visited:
107
+ visited.add(nb)
108
+ q.append(nb)
109
+
110
+ rows_b = [p[0] for p in component]
111
+ cols_b = [p[1] for p in component]
112
+ min_r = min(rows_b)
113
+ min_c = min(cols_b)
114
+ block_h = max(rows_b) - min_r + 1
115
+ block_w = max(cols_b) - min_c + 1
116
+ marker_blocks.append((min_r, min_c, block_h, block_w, component))
117
+
118
+ n_anchors = len(template_c2)
119
+ processed = set()
120
+
121
+ for idx, (min_r, min_c, bh, bw, cells) in enumerate(marker_blocks):
122
+ if idx in processed:
123
+ continue
124
+ possible_scales = set()
125
+ if tpl_c2_h > 0 and bh % tpl_c2_h == 0:
126
+ s = bh // tpl_c2_h
127
+ if tpl_c2_w == 0 or bw == tpl_c2_w * s:
128
+ possible_scales.add(s)
129
+ if tpl_c2_w > 0 and bw % tpl_c2_w == 0:
130
+ s = bw // tpl_c2_w
131
+ if tpl_c2_h == 0 or bh == tpl_c2_h * s:
132
+ possible_scales.add(s)
133
+
134
+ for scale in sorted(possible_scales, reverse=True):
135
+ if check_block_matches_pattern(cells, min_r, min_c, bh, bw, scale):
136
+ stamp_at(min_r, min_c, scale)
137
+ processed.add(idx)
138
+ break
139
+
140
+ # Second pass: pair unprocessed blocks
141
+ unprocessed = [(idx, mb) for idx, mb in enumerate(marker_blocks) if idx not in processed]
142
+
143
+ if unprocessed and n_anchors > 1:
144
+ scale_blocks = {}
145
+ for idx, (min_r, min_c, bh, bw, cells) in unprocessed:
146
+ if bh == bw and len(cells) == bh * bw:
147
+ scale = bh
148
+ if scale not in scale_blocks:
149
+ scale_blocks[scale] = []
150
+ scale_blocks[scale].append((idx, min_r, min_c))
151
+ else:
152
+ if 1 not in scale_blocks:
153
+ scale_blocks[1] = []
154
+ for cell in cells:
155
+ scale_blocks[1].append((idx, cell[0], cell[1]))
156
+
157
+ ref_r, ref_c = tpl_c2_rel[0]
158
+ offsets_from_first = [(r - ref_r, c - ref_c) for r, c in tpl_c2_rel]
159
+
160
+ for scale in sorted(scale_blocks.keys(), reverse=True):
161
+ bl_list = scale_blocks[scale]
162
+ bl_positions = set((r, c) for (_, r, c) in bl_list)
163
+ used_positions = set()
164
+
165
+ for (idx, r, c) in sorted(bl_list, key=lambda x: (x[1], x[2])):
166
+ if (r, c) in used_positions:
167
+ continue
168
+ all_found = True
169
+ group_positions = [(r, c)]
170
+ for (dr, dc) in offsets_from_first[1:]:
171
+ partner = (r + dr * scale, c + dc * scale)
172
+ if partner not in bl_positions or partner in used_positions:
173
+ all_found = False
174
+ break
175
+ group_positions.append(partner)
176
+
177
+ if all_found:
178
+ for p in group_positions:
179
+ used_positions.add(p)
180
+ origin_r = r - ref_r * scale
181
+ origin_c = c - ref_c * scale
182
+ stamp_at(origin_r, origin_c, scale)
183
+
184
+ elif unprocessed and n_anchors == 1:
185
+ for idx, (min_r, min_c, bh, bw, cells) in unprocessed:
186
+ scale = min(bh, bw)
187
+ if bh == bw:
188
+ stamp_at(min_r, min_c, scale)
189
+ else:
190
+ for sr in range(0, bh, scale):
191
+ for sc in range(0, bw, scale):
192
+ sub_ok = all((min_r+sr+dr, min_c+sc+dc) in cells
193
+ for dr in range(scale) for dc in range(scale))
194
+ if sub_ok:
195
+ stamp_at(min_r + sr, min_c + sc, scale)
196
+
197
+ return out.tolist()
198
+
199
+
200
+ if __name__ == '__main__':
201
+ with open('/app/task-data/task101.json') as f:
202
+ data = json.load(f)
203
+
204
+ all_examples = data['train'] + data['test'] + data.get('arc-gen', [])
205
+ right, wrong = 0, 0
206
+ for i, ex in enumerate(all_examples):
207
+ if solve_task101(ex['input']) == ex['output']:
208
+ right += 1
209
+ else:
210
+ wrong += 1
211
+ print(f"Results: {right}/{right+wrong} pass ({wrong} fail)")