X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/d91e1fc9b80b277905ece5467bfb90a254a25b22..1af60e1e75977b7e1de901e2ca24b36fafcff1da:/print.py diff --git a/print.py b/print.py index 172ae51..75a1f91 100755 --- a/print.py +++ b/print.py @@ -6,15 +6,15 @@ # and how many you want per page. # Supported games are those which are sensibly solvable using -# pencil and paper: Rectangles, Pattern and Solo. +# pencil and paper: Rectangles, Pattern, Solo, Net. # Command-line syntax is # # print.py # -# is one of `rect', `rectangles', `pattern', `solo'. -# is two numbers separated by an x: `2x3', for example, -# means two columns by three rows. +# is one of `rect', `rectangles', `pattern', `solo', +# `net', `dominosa'. is two numbers separated by an x: +# `2x3', for example, means two columns by three rows. # # The program will then read game IDs from stdin until it sees EOF, # and generate as many PostScript pages on stdout as it needs. @@ -92,6 +92,121 @@ def rect_format(s): ((x+0.5)*gridpitch, (h-y-0.5)*gridpitch, n)) return ret.coords, ret.s +def net_format(s): + # Parse the game ID. + ret = Holder() + ret.s = "" + params, seed = string.split(s, ":") + wrapping = 0 + if params[-1:] == "w": + wrapping = 1 + params = params[:-1] + w, h = map(string.atoi, string.split(params, "x")) + grid = [] + hbarriers = [] + vbarriers = [] + while len(seed) > 0: + n = string.atoi(seed[0], 16) + seed = seed[1:] + while len(seed) > 0 and seed[0] in 'hv': + x = len(grid) % w + y = len(grid) / w + if seed[0] == 'h': + hbarriers.append((x, y+1)) + else: + vbarriers.append((x+1, y)) + seed = seed[1:] + grid.append(n) + assert w * h == len(grid) + # I'm going to arbitrarily choose a 24pt grid pitch. + gridpitch = 24 + scale = 0.25 + bigoffset = 0.25 + smalloffset = 0.17 + squaresize = 0.25 + # Set up coordinate system. + pw = gridpitch * w + ph = gridpitch * h + ret.coords = (pw/2, pw/2, ph/2, ph/2) + psprint(ret, "%g %g translate" % (-ret.coords[0], -ret.coords[2])) + # Draw the base grid lines. + psprint(ret, "newpath 0.02 setlinewidth") + for x in xrange(1,w): + psprint(ret, "%g 0 moveto 0 %g rlineto" % (x * gridpitch, h * gridpitch)) + for y in xrange(1,h): + psprint(ret, "0 %g moveto %g 0 rlineto" % (y * gridpitch, w * gridpitch)) + psprint(ret, "stroke") + # Draw round the grid exterior. + psprint(ret, "newpath") + if not wrapping: + psprint(ret, "2 setlinewidth") + psprint(ret, "0 0 moveto 0 %g rlineto %g 0 rlineto 0 %g rlineto" % \ + (h * gridpitch, w * gridpitch, -h * gridpitch)) + psprint(ret, "closepath stroke") + # Draw any barriers. + psprint(ret, "newpath 2 setlinewidth 1 setlinecap") + for x, y in hbarriers: + psprint(ret, "%g %g moveto %g 0 rlineto" % \ + (x * gridpitch, (h - y) * gridpitch, gridpitch)) + for x, y in vbarriers: + psprint(ret, "%g %g moveto 0 -%g rlineto" % \ + (x * gridpitch, (h - y) * gridpitch, gridpitch)) + psprint(ret, "stroke") + # And draw the symbol in each box. + for i in xrange(len(grid)): + x = i % w + y = i / w + v = grid[i] + # Rotate to canonical form. + if v in (1,2,4,8): + v = 1 + elif v in (5,10): + v = 5 + elif v in (3,6,9,12): + v = 9 + elif v in (7,11,13,14): + v = 13 + # Centre on an area in the corner of the tile. + psprint(ret, "gsave") + if v & 4: + hoffset = bigoffset + else: + hoffset = smalloffset + if v & 2: + voffset = bigoffset + else: + voffset = smalloffset + psprint(ret, "%g %g translate" % \ + ((x + hoffset) * gridpitch, (h - y - voffset) * gridpitch)) + psprint(ret, "%g dup scale" % (float(gridpitch) * scale / 2)) + psprint(ret, "newpath 0.07 setlinewidth") + # Draw the radial lines. + for dx, dy, z in ((1,0,1), (0,1,2), (-1,0,4), (0,-1,8)): + if v & z: + psprint(ret, "0 0 moveto %d %d lineto" % (dx, dy)) + psprint(ret, "stroke") + # Draw additional figures if desired. + if v == 1: + # Endpoints have a little empty square at the centre. + psprint(ret, "newpath %g %g moveto 0 -%g rlineto" % \ + (squaresize, squaresize, squaresize * 2)) + psprint(ret, "-%g 0 rlineto 0 %g rlineto closepath fill" % \ + (squaresize * 2, squaresize * 2)) + # Get back out of the centre section. + psprint(ret, "grestore") + # Draw the endpoint square in large in the middle. + if v == 1: + psprint(ret, "gsave") + psprint(ret, "%g %g translate" % \ + ((x + 0.5) * gridpitch, (h - y - 0.5) * gridpitch)) + psprint(ret, "%g dup scale" % (float(gridpitch) / 2)) + psprint(ret, "newpath %g %g moveto 0 -%g rlineto" % \ + (squaresize, squaresize, squaresize * 2)) + psprint(ret, "-%g 0 rlineto 0 %g rlineto closepath fill" % \ + (squaresize * 2, squaresize * 2)) + psprint(ret, "grestore") + return ret.coords, ret.s + def pattern_format(s): ret = Holder() ret.s = "" @@ -218,11 +333,99 @@ def solo_format(s): ((x+0.5)*gridpitch, (cr-y-0.5)*gridpitch, s)) return ret.coords, ret.s +def dominosa_format(s): + ret = Holder() + ret.s = "" + params, seed = string.split(s, ":") + n = string.atoi(params) + w = n+2 + h = n+1 + grid = [] + while len(seed) > 0: + if seed[0] == '[': # XXX + d, seed = string.split(seed[1:], "]") + grid.append(string.atoi(d)) + else: + assert seed[0] in string.digits + grid.append(string.atoi(seed[0:1])) + seed = seed[1:] + assert w*h == len(grid) + # I'm going to arbitrarily choose to use 9pt text for the + # numbers, and a 16pt grid pitch. + textht = 9 + gridpitch = 16 + pw = gridpitch * w + ph = gridpitch * h + psprint(ret, "/Helvetica findfont %g scalefont setfont" % textht) + ret.coords = (pw/2, pw/2, ph/2, ph/2) + psprint(ret, "%g %g translate" % (-ret.coords[0], -ret.coords[2])) + for y in xrange(h): + for x in xrange(w): + psprint(ret, "%g %g (%d) ctshow" % \ + ((x+0.5)*gridpitch, (h-y-0.5)*gridpitch, grid[y*w+x])) + return ret.coords, ret.s + +def slant_format(s): + # Parse the game ID. + ret = Holder() + ret.s = "" + params, seed = string.split(s, ":") + w, h = map(string.atoi, string.split(params, "x")) + W = w+1 + H = h+1 + grid = [] + while len(seed) > 0: + if seed[0] in string.lowercase: + grid.extend([-1] * (ord(seed[0]) - ord('a') + 1)) + seed = seed[1:] + elif seed[0] in "01234": + grid.append(string.atoi(seed[0])) + seed = seed[1:] + assert W * H == len(grid) + # I'm going to arbitrarily choose to use 7pt text for the + # numbers, and a 14pt grid pitch. + textht = 7 + gridpitch = 14 + radius = textht * 2.0 / 3.0 + # Set up coordinate system. + pw = gridpitch * w + ph = gridpitch * h + ret.coords = (pw/2, pw/2, ph/2, ph/2) + psprint(ret, "%g %g translate" % (-ret.coords[0], -ret.coords[2])) + # Draw round the grid exterior, thickly. + psprint(ret, "newpath 1 setlinewidth") + psprint(ret, "0 0 moveto 0 %g rlineto %g 0 rlineto 0 %g rlineto" % \ + (h * gridpitch, w * gridpitch, -h * gridpitch)) + psprint(ret, "closepath stroke") + # Draw the internal grid lines, _very_ thin (the player will + # need to draw over them visibly). + psprint(ret, "newpath 0.01 setlinewidth") + for x in xrange(1,w): + psprint(ret, "%g 0 moveto 0 %g rlineto" % (x * gridpitch, h * gridpitch)) + for y in xrange(1,h): + psprint(ret, "0 %g moveto %g 0 rlineto" % (y * gridpitch, w * gridpitch)) + psprint(ret, "stroke") + # And draw the numbers. + psprint(ret, "/Helvetica findfont %g scalefont setfont" % textht) + for y in xrange(H): + for x in xrange(W): + n = grid[y*W+x] + if n >= 0: + psprint(ret, "newpath %g %g %g 0 360 arc" % \ + ((x)*gridpitch, (h-y)*gridpitch, radius), + "gsave 1 setgray fill grestore stroke") + psprint(ret, "%g %g (%d) ctshow" % \ + ((x)*gridpitch, (h-y)*gridpitch, n)) + return ret.coords, ret.s + formatters = { +"net": net_format, "rect": rect_format, "rectangles": rect_format, "pattern": pattern_format, -"solo": solo_format +"solo": solo_format, +"dominosa": dominosa_format, +"slant": slant_format } if len(sys.argv) < 3: