99 lines
2.2 KiB
Python
99 lines
2.2 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
import sys
|
||
|
import argparse
|
||
|
import random
|
||
|
|
||
|
def make_puzzle(s, solvable, iterations):
|
||
|
def swap_empty(p):
|
||
|
idx = p.index(0)
|
||
|
poss = []
|
||
|
if idx % s > 0:
|
||
|
poss.append(idx - 1)
|
||
|
if idx % s < s - 1:
|
||
|
poss.append(idx + 1)
|
||
|
if idx / s > 0 and idx - s >= 0:
|
||
|
poss.append(idx - s)
|
||
|
if idx / s < s - 1:
|
||
|
poss.append(idx + s)
|
||
|
swi = random.choice(poss)
|
||
|
p[idx] = p[swi]
|
||
|
p[swi] = 0
|
||
|
|
||
|
p = make_goal(s)
|
||
|
for i in range(iterations):
|
||
|
swap_empty(p)
|
||
|
|
||
|
if not solvable:
|
||
|
if p[0] == 0 or p[1] == 0:
|
||
|
p[-1], p[-2] = p[-2], p[-1]
|
||
|
else:
|
||
|
p[0], p[1] = p[1], p[0]
|
||
|
|
||
|
return p
|
||
|
|
||
|
def make_goal(s):
|
||
|
ts = s*s
|
||
|
puzzle = [-1 for i in range(ts)]
|
||
|
cur = 1
|
||
|
x = 0
|
||
|
ix = 1
|
||
|
y = 0
|
||
|
iy = 0
|
||
|
while True:
|
||
|
puzzle[x + y*s] = cur
|
||
|
if cur == 0:
|
||
|
break
|
||
|
cur += 1
|
||
|
if x + ix == s or x + ix < 0 or (ix != 0 and puzzle[x + ix + y*s] != -1):
|
||
|
iy = ix
|
||
|
ix = 0
|
||
|
elif y + iy == s or y + iy < 0 or (iy != 0 and puzzle[x + (y+iy)*s] != -1):
|
||
|
ix = -iy
|
||
|
iy = 0
|
||
|
x += ix
|
||
|
y += iy
|
||
|
if cur == s*s:
|
||
|
cur = 0
|
||
|
|
||
|
return puzzle
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
parser = argparse.ArgumentParser()
|
||
|
|
||
|
parser.add_argument("size", type=int, help="Size of the puzzle's side. Must be >3.")
|
||
|
parser.add_argument("-s", "--solvable", action="store_true", default=False, help="Forces generation of a solvable puzzle. Overrides -u.")
|
||
|
parser.add_argument("-u", "--unsolvable", action="store_true", default=False, help="Forces generation of an unsolvable puzzle")
|
||
|
parser.add_argument("-i", "--iterations", type=int, default=10000, help="Number of passes")
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
random.seed()
|
||
|
|
||
|
if args.solvable and args.unsolvable:
|
||
|
print "Can't be both solvable AND unsolvable, dummy !"
|
||
|
sys.exit(1)
|
||
|
|
||
|
if args.size < 3:
|
||
|
print "Can't generate a puzzle with size lower than 2. It says so in the help. Dummy."
|
||
|
sys.exit(1)
|
||
|
|
||
|
if not args.solvable and not args.unsolvable:
|
||
|
solv = random.choice([True, False])
|
||
|
elif args.solvable:
|
||
|
solv = True
|
||
|
elif args.unsolvable:
|
||
|
solv = False
|
||
|
|
||
|
s = args.size
|
||
|
|
||
|
puzzle = make_puzzle(s, solvable=solv, iterations=args.iterations)
|
||
|
|
||
|
w = len(str(s*s))
|
||
|
print "# This puzzle is %s" % ("solvable" if solv else "unsolvable")
|
||
|
print "%d" % s
|
||
|
for y in range(s):
|
||
|
for x in range(s):
|
||
|
print "%s" % (str(puzzle[x + y*s]).rjust(w)),
|
||
|
print
|