Created
January 28, 2026 03:11
-
-
Save coopermayne/5f48132e45e19f1b64ebaefb66ec54df to your computer and use it in GitHub Desktop.
Two Not Touch - Puzzle JSON to PBM (no stars)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Converts Two Not Touch puzzle JSON to PBM image (puzzle only, no stars). | |
| Usage: | |
| python3 puzzle_to_pbm.py puzzle.json # outputs puzzle.pbm | |
| python3 puzzle_to_pbm.py puzzle.json -o output.pbm # custom output path | |
| python3 puzzle_to_pbm.py < puzzle.json > output.pbm # stdin/stdout | |
| cat puzzle.json | python3 puzzle_to_pbm.py # pipe | |
| """ | |
| import json | |
| import sys | |
| import argparse | |
| from typing import List | |
| GRID_SIZE = 10 | |
| def create_puzzle_pbm(regions: List[List[int]], cell_size: int = 30, | |
| border_width: int = 4, grid_line_width: int = 1) -> str: | |
| """ | |
| Create PBM image data for puzzle grid (no stars). | |
| Returns PBM as a string. | |
| """ | |
| grid_size = len(regions) | |
| img_size = grid_size * cell_size + border_width | |
| # Initialize white image (0 = white, 1 = black) | |
| pixels = [[0] * img_size for _ in range(img_size)] | |
| # Draw outer border (thick) | |
| for i in range(img_size): | |
| for b in range(border_width): | |
| pixels[b][i] = 1 # Top | |
| pixels[img_size - 1 - b][i] = 1 # Bottom | |
| pixels[i][b] = 1 # Left | |
| pixels[i][img_size - 1 - b] = 1 # Right | |
| # Draw cell boundaries | |
| for r in range(grid_size): | |
| for c in range(grid_size): | |
| cell_top = r * cell_size + border_width // 2 | |
| cell_left = c * cell_size + border_width // 2 | |
| cell_bottom = cell_top + cell_size | |
| cell_right = cell_left + cell_size | |
| # Right edge of cell | |
| if c < grid_size - 1: | |
| is_region_border = regions[r][c] != regions[r][c + 1] | |
| line_width = border_width if is_region_border else grid_line_width | |
| line_x = cell_right - line_width // 2 | |
| for y in range(cell_top, cell_bottom): | |
| for w in range(line_width): | |
| if 0 <= line_x + w < img_size and 0 <= y < img_size: | |
| pixels[y][line_x + w] = 1 | |
| # Bottom edge of cell | |
| if r < grid_size - 1: | |
| is_region_border = regions[r][c] != regions[r + 1][c] | |
| line_width = border_width if is_region_border else grid_line_width | |
| line_y = cell_bottom - line_width // 2 | |
| for x in range(cell_left, cell_right): | |
| for w in range(line_width): | |
| if 0 <= x < img_size and 0 <= line_y + w < img_size: | |
| pixels[line_y + w][x] = 1 | |
| # Build PBM string | |
| lines = ["P1", f"{img_size} {img_size}"] | |
| for row in pixels: | |
| lines.append(" ".join(str(p) for p in row)) | |
| return "\n".join(lines) + "\n" | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Convert Two Not Touch puzzle JSON to PBM image" | |
| ) | |
| parser.add_argument("input", nargs="?", help="Input JSON file (default: stdin)") | |
| parser.add_argument("-o", "--output", help="Output PBM file (default: stdout or input.pbm)") | |
| parser.add_argument("--cell-size", type=int, default=30, help="Cell size in pixels (default: 30)") | |
| parser.add_argument("--border-width", type=int, default=4, help="Region border width (default: 4)") | |
| args = parser.parse_args() | |
| # Read input | |
| if args.input: | |
| with open(args.input, 'r') as f: | |
| data = json.load(f) | |
| else: | |
| data = json.load(sys.stdin) | |
| # Get puzzle (support both 'puzzle' and 'regions' keys for compatibility) | |
| regions = data.get('puzzle') or data.get('regions') | |
| if not regions: | |
| print("Error: JSON must contain 'puzzle' or 'regions' key", file=sys.stderr) | |
| sys.exit(1) | |
| # Generate PBM | |
| pbm_data = create_puzzle_pbm(regions, args.cell_size, args.border_width) | |
| # Write output | |
| if args.output: | |
| with open(args.output, 'w') as f: | |
| f.write(pbm_data) | |
| print(f"PBM saved to: {args.output}", file=sys.stderr) | |
| elif args.input and not sys.stdin.isatty(): | |
| # Input file provided but also piped - write to stdout | |
| sys.stdout.write(pbm_data) | |
| elif args.input: | |
| # Input file provided, derive output name | |
| output_path = args.input.rsplit('.', 1)[0] + '.pbm' | |
| with open(output_path, 'w') as f: | |
| f.write(pbm_data) | |
| print(f"PBM saved to: {output_path}", file=sys.stderr) | |
| else: | |
| # stdin input, stdout output | |
| sys.stdout.write(pbm_data) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment