File size: 6,912 Bytes
67acd34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# #!/usr/bin/env python3
# import re
# import sys
# from itertools import chain

# """
# Usage:
#   # From a file
#   python convert_flowfree.py puzzle.txt

#   # Or pipe the raw generator output
#   printf "3 5 5\n(2, 0) (2, 2)\n(3, 3) (1, 3)\n(1, 0) (1, 4)\n" | python convert_flowfree.py

# Notes:
# - Expects lines like:
#     <num_colors> <cols> <rows>
#     (x1, y1) (x2, y2)
#     ...
#   for exactly <num_colors> lines after the header.
# - Coordinates are zero-based and are (col, row).
# - Colors are labeled A, B, C, ... (wraps to aA... if >52 colors).
# """

# HEADER_RE = re.compile(r"^\s*(\d+)\s+(\d+)\s+(\d+)\s*$")
# PAIR_RE   = re.compile(
#     r"\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)"
# )

# def letter_labels(n):
#     base = [chr(i) for i in range(ord('A'), ord('Z')+1)]
#     extra = [chr(i) for i in range(ord('a'), ord('z')+1)]
#     # If still more needed, go to double letters: AA, AB, ...
#     if n <= len(base) + len(extra):
#         return list(chain(base, extra))[:n]
#     labels = list(chain(base, extra))
#     i = 0
#     while len(labels) < n:
#         for ch in base:
#             labels.append(base[i % len(base)] + ch)
#             if len(labels) == n:
#                 break
#         i += 1
#     return labels

# def parse_blocks(lines):
#     """
#     Yields (num_colors, cols, rows, pairs_list) for each detected puzzle block.
#     pairs_list is a list of ((x1,y1),(x2,y2)) of length num_colors.
#     """
#     i = 0
#     while i < len(lines):
#         m = HEADER_RE.match(lines[i])
#         if not m:
#             i += 1
#             continue

#         num_colors, cols, rows = map(int, m.groups())
#         i += 1

#         pairs = []
#         while i < len(lines) and len(pairs) < num_colors:
#             pm = PAIR_RE.search(lines[i])
#             if pm:
#                 x1, y1, x2, y2 = map(int, pm.groups())
#                 pairs.append(((x1, y1), (x2, y2)))
#                 i += 1
#             else:
#                 # skip noise lines between pairs
#                 i += 1

#         if len(pairs) == num_colors:
#             yield num_colors, cols, rows, pairs
#         # else: header without enough pairs; skip and keep scanning

# def grid_from_puzzle(cols, rows, pairs, labels):
#     grid = [['.' for _ in range(cols)] for _ in range(rows)]
#     for label, ((x1, y1), (x2, y2)) in zip(labels, pairs):
#         for (x, y) in [(x1, y1), (x2, y2)]:
#             if not (0 <= x < cols and 0 <= y < rows):
#                 raise ValueError(f"Coordinate {(x,y)} out of bounds for {cols}x{rows}")
#             if grid[y][x] != '.':
#                 raise ValueError(f"Cell {(x,y)} already occupied (overlap).")
#             grid[y][x] = label
#     return grid

# def print_grid(grid):
#     for row in grid:
#         print(''.join(row))

# def main():
#     data = sys.stdin.read() if sys.stdin and not sys.stdin.isatty() else None
#     if data is None:
#         if len(sys.argv) < 2:
#             print("Provide a file or pipe the generator output to stdin.", file=sys.stderr)
#             sys.exit(1)
#         with open(sys.argv[1], 'r', encoding='utf-8') as f:
#             data = f.read()

#     lines = data.splitlines()
#     any_printed = False
#     for num_colors, cols, rows, pairs in parse_blocks(lines):
#         labels = letter_labels(num_colors)
#         grid = grid_from_puzzle(cols, rows, pairs, labels)
#         print('"""')
#         print_grid(grid)
#         print('"""')
#         any_printed = True

#     if not any_printed:
#         print("No valid puzzle blocks found.", file=sys.stderr)
#         sys.exit(2)

# if __name__ == "__main__":
#     main()




#!/usr/bin/env python3
import re
import sys
from itertools import chain

HEADER_RE = re.compile(r"^\s*(\d+)\s+(\d+)\s+(\d+)\s*$")
COORD_RE  = re.compile(r"\(\s*(\d+)\s*,\s*(\d+)\s*\)")

def letter_labels(n):
    base = [chr(i) for i in range(ord('A'), ord('Z')+1)]
    extra = [chr(i) for i in range(ord('a'), ord('z')+1)]
    if n <= len(base) + len(extra):
        return list(chain(base, extra))[:n]
    labels = list(chain(base, extra))
    i = 0
    while len(labels) < n:
        for ch in base:
            labels.append(base[i % len(base)] + ch)
            if len(labels) == n:
                break
        i += 1
    return labels

def parse_blocks(lines):
    """
    Yields (num_colors, cols, rows, paths_list) per puzzle block.
    paths_list: list of lists of (x,y). For endpoints format, each list has len 2.
    For solution format, each list may have len > 2 (full path).
    """
    i = 0
    while i < len(lines):
        m = HEADER_RE.match(lines[i])
        if not m:
            i += 1
            continue

        num_colors, cols, rows = map(int, m.groups())
        i += 1

        paths = []
        while i < len(lines) and len(paths) < num_colors:
            coords = COORD_RE.findall(lines[i])
            if coords:
                path = [(int(x), int(y)) for x, y in coords]
                paths.append(path)
            i += 1  # always advance; skip noise automatically

        if len(paths) == num_colors:
            yield num_colors, cols, rows, paths
        # else: header without enough lines -> skip and keep scanning

def build_grid(cols, rows):
    return [['.' for _ in range(cols)] for _ in range(rows)]

def place_paths_on_grid(cols, rows, paths, labels):
    """
    Fills the grid with labels along either endpoints (len 2) or full paths (len > 2).
    """
    grid = build_grid(cols, rows)
    for label, path in zip(labels, paths):
        for (x, y) in path:
            if not (0 <= x < cols and 0 <= y < rows):
                raise ValueError(f"Coordinate {(x,y)} out of bounds for {cols}x{rows}")
            if grid[y][x] != '.' and grid[y][x] != label:
                raise ValueError(f"Cell {(x,y)} already occupied by '{grid[y][x]}' (overlap).")
            grid[y][x] = label
    return grid

def print_grid(grid):
    for row in grid:
        print(''.join(row))

def read_all_input():
    # Prefer stdin if piped; else read from file arg.
    if sys.stdin and not sys.stdin.isatty():
        return sys.stdin.read()
    if len(sys.argv) < 2:
        print("Provide a file or pipe the generator/solution output to stdin.", file=sys.stderr)
        sys.exit(1)
    with open(sys.argv[1], 'r', encoding='utf-8') as f:
        return f.read()

def main():
    data = read_all_input()
    lines = data.splitlines()

    any_printed = False
    for num_colors, cols, rows, paths in parse_blocks(lines):
        labels = letter_labels(num_colors)
        grid = place_paths_on_grid(cols, rows, paths, labels)
        print('"""')
        print_grid(grid)
        print('"""')
        any_printed = True

    if not any_printed:
        print("No valid puzzle blocks found.", file=sys.stderr)
        sys.exit(2)

if __name__ == "__main__":
    main()