Skip to content

Commit fcc4320

Browse files
author
Frederic Vogels
committed
python
1 parent 3584de2 commit fcc4320

File tree

5 files changed

+347
-247
lines changed

5 files changed

+347
-247
lines changed

python/pc.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python
2+
3+
from functools import reduce
4+
import argparse
5+
from zipfile import ZipFile
6+
import sys
7+
import os
8+
import re
9+
from picross.grid import Grid
10+
from picross.solver import show, solve_puzzle, derive_constraints, read_image_from_file
11+
12+
13+
def _parse_constraints(string):
14+
return [ [ int(n) for n in part.split(',') ] if part else [] for part in string.split(';') ]
15+
16+
17+
def _solve(args):
18+
row_constraints = _parse_constraints(args.row)
19+
column_constraints = _parse_constraints(args.column)
20+
print(show(solve_puzzle(column_constraints, row_constraints)))
21+
22+
23+
def _derive_constraints(args):
24+
filename = args.filename
25+
grid = read_image_from_file(filename)
26+
column_constraints, row_constraints = derive_constraints(grid)
27+
print(';'.join( ','.join( str(n) for n in ns ) for ns in column_constraints ))
28+
print(';'.join( ','.join( str(n) for n in ns ) for ns in row_constraints ))
29+
30+
31+
def _show(args):
32+
with ZipFile(args.filename, 'r') as zip:
33+
names = zip.namelist()
34+
players = [ name for name in names if name.startswith('players/')]
35+
puzzles = [ name for name in names if name.startswith('library/')]
36+
37+
for player in players:
38+
player_name = player.split('/')[1].split('.')[0]
39+
print(f'Player {player_name}')
40+
41+
for puzzle in puzzles:
42+
lines = zip.read(puzzle).decode('utf-8').strip().split("\n")
43+
author = lines[0]
44+
width, height = map(int, lines[1].split(' '))
45+
solution = "\n".join(lines[2:])
46+
print(f'Puzzle ({width}x{height}) by {author}')
47+
if args.show_solution:
48+
print(solution)
49+
print()
50+
51+
52+
def _create(args):
53+
path = args.filename
54+
overwrite = args.force
55+
56+
if os.path.exists(path):
57+
if overwrite:
58+
os.remove(path)
59+
else:
60+
print(f'Error: {path} already exists; use -f to force')
61+
sys.exit(-1)
62+
63+
with ZipFile(path, 'w') as zip:
64+
pass
65+
66+
67+
def _add_player(args):
68+
print(f'Adding player {args.name}')
69+
70+
71+
def _extract_puzzle_index(filename):
72+
match = re.search(r'entry(\d+)\.txt', filename)
73+
return int(match.group(1))
74+
75+
76+
def _add_puzzle(args):
77+
archive = args.archive
78+
author = args.author
79+
row_constraints = _parse_constraints(vars(args)['row-constraints'])
80+
column_constraints = _parse_constraints(vars(args)['column-constraints'])
81+
solution = solve_puzzle(row_constraints=row_constraints, column_constraints=column_constraints)
82+
width = solution.width
83+
height = solution.height
84+
85+
with ZipFile(archive, 'r') as zip:
86+
names = zip.namelist()
87+
88+
next_index = max([-1] + [ _extract_puzzle_index(name) for name in names if name.startswith('library/') ]) + 1
89+
filename = f'library/entry{str(next_index).rjust(5, "0")}.txt'
90+
data = f'{author}\n{width} {height}\n{show(solution)}'
91+
92+
with ZipFile(archive, 'w') as zip:
93+
zip.writestr(filename, data)
94+
95+
96+
97+
def _process_command_line_arguments():
98+
def create_archive_parsers(subparsers):
99+
subparser = subparsers.add_parser('create', help='create empty archive')
100+
subparser.add_argument('filename', help='archive', action='store')
101+
subparser.add_argument('-f', '--force', help='overwrite existing archive', action='store_true')
102+
subparser.set_defaults(func=_create)
103+
104+
subparser = subparsers.add_parser('show', help='show contents of archive')
105+
subparser.add_argument('filename', help='archive', action='store')
106+
subparser.add_argument('--show-solution', help='show solution', action='store_true')
107+
subparser.set_defaults(func=_show)
108+
109+
subparser = subparsers.add_parser('add-puzzle', help='adds puzzle to archive')
110+
subparser.add_argument('archive', help='archive', action='store')
111+
subparser.add_argument('author', help='author', action='store')
112+
subparser.add_argument('row-constraints', help='row constraints (use ; to separate rows and , to separate values)', action='store')
113+
subparser.add_argument('column-constraints', help='column constraints (use ; to separate columns and , to separate values', action='store')
114+
subparser.set_defaults(func=_add_puzzle)
115+
116+
def create_puzzle_parsers(subparsers):
117+
subparser = subparsers.add_parser('solve', help='solves PiCross puzzle given its constraints')
118+
subparser.add_argument('row', help='row constraints (use ; to separate rows and , to separate values)', action='store')
119+
subparser.add_argument('column', help='column constraints (use ; to separate columns and , to separate values', action='store')
120+
subparser.set_defaults(func=_solve)
121+
122+
subparser = subparsers.add_parser('constraints', help='derives constraints from image')
123+
subparser.add_argument('filename', help='file containing image', action='store')
124+
subparser.set_defaults(func=_derive_constraints)
125+
126+
parser = argparse.ArgumentParser(prog='picross')
127+
parser.set_defaults(func=lambda args: parser.print_help())
128+
subparsers = parser.add_subparsers(help='sub-command help')
129+
130+
subparser = subparsers.add_parser('archive', help='archive-related functionality')
131+
create_archive_parsers(subparser.add_subparsers())
132+
133+
subparser = subparsers.add_parser('puzzle', help='puzzle-related functionality')
134+
create_puzzle_parsers(subparser.add_subparsers())
135+
136+
137+
args = parser.parse_args()
138+
args.func(args)
139+
140+
141+
_process_command_line_arguments()

python/picross.py

Lines changed: 0 additions & 247 deletions
This file was deleted.

python/picross/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)