Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- import re
- import sys
- import argparse
- def fail(msg):
- sys.stderr.write(sys.argv[0] + ": " + msg + "\n")
- sys.exit(1)
- parser = argparse.ArgumentParser(description="Generate LLS input grid for finding spaceships and oscillators.")
- parser.add_argument('width', type=int, help="width of pattern bounding box")
- parser.add_argument('height', type=int, help="height of pattern bounding box")
- parser.add_argument('-f', '--first-gen', type=int, nargs=2, help="width and height of first generation")
- parser.add_argument('-p', '--period', type=int, default=2, help="period until first generation reappears")
- parser.add_argument('-x', '--shift-x', type=int, default=0, help="horizontal shift over period")
- parser.add_argument('-y', '--shift-y', type=int, default=0, help="vertical shift over period")
- parser.add_argument('-m', '--mirror', choices=("", "none", "x", "y", "d", "diag", "a", "antidiag", "9", "90", "1", "180", "2", "270"), help="mirror or rotate pattern after period")
- parser.add_argument('--fixed', nargs='+', default=[], help="fixed cell values in initial pattern (e.g. \"a1=1 a2=b1=b2=0\"")
- parser.add_argument('--wide', action='store_true', help="allow intermediate generations to use the whole (width+x)*(height+y) cell area")
- parser.add_argument('--strobe', action='store_true', help="use a strobing background for B0 rules")
- parser.add_argument('--partial-x', action='store_true', help="search for a partial travelling in -ve x-direction, use with --fixed \"aY=1\" for 0<Y<height")
- args = parser.parse_args()
- # validate pattern dimensions
- if args.period < 1:
- fail("Period must be positive.")
- if args.strobe and args.period % 2:
- fail("Period must be even for strobing rules.")
- if args.width < 1 or args.height < 1:
- fail("Width and height must be at least 1.")
- if args.partial_x:
- if args.shift_x > 0:
- fail("Horizantal shift must be <= 0 to use partial_x")
- if args.fixed is None:
- fail("Must set an On cell in the front row when searching for partials (e.g. --fixed \"a4=1\")")
- if args.first_gen is None:
- gen0_width = args.width
- gen0_height = args.height
- else:
- gen0_width, gen0_height = args.first_gen
- if gen0_width < 1 or gen0_height < 1:
- fail("First generation width and height must be at least 1.")
- if gen0_width > args.width or gen0_height > args.height:
- fail("First generation width and height may not be greater than for later generations.")
- total_width = args.width + 4 + abs(args.shift_x)
- total_height = args.height + 4 + abs(args.shift_y)
- # glide / rotate symmetry handling
- mirror_x = args.mirror in ("x", "a", "antidiag", "9", "90", "1", "180")
- mirror_y = args.mirror in ("y", "a", "antidiag", "1", "180", "2", "270")
- transpose = args.mirror in ("d", "diag", "a", "antidiag", "9", "90", "2", "270")
- if transpose and gen0_width != gen0_height:
- fail("Only square grids are supported for --mirror=%s." % args.mirror)
- # build dictionary of fixed cells
- fixed = {}
- for eqn in args.fixed:
- eqn = eqn.strip()
- if eqn == "": continue
- terms = eqn.split("=")
- if len(terms) < 2:
- fail("Invalid equation \"%s\" in --fixed=\"%s\"." % (eqn, args.fixed))
- val = terms.pop().strip()
- if val != "0" and val != "1":
- fail("Invalid constant \"%s\" in equation \"%s\"." % (val, eqn))
- for term in terms:
- term = term.strip()
- if not re.match(r'^[a-z]+[1-9][0-9]*$', term):
- fail("Invalid term \"%s\" in equation \"%s\"." % (term, eqn))
- if term in fixed and fixed[term] != val:
- fail("Inconsistent assignment for \"%s\" in --fixed=\"%s\"." % (term, args.fixed))
- fixed[term] = val
- # helper function for generating cell symbol names
- def cellname(row, col):
- name = ""
- while col >= 0:
- col, letter = divmod(col, 26)
- name = chr(ord('a') + letter) + name
- col -= 1
- return name + str(row+1)
- maxlen = max(2, len(cellname(args.width-1, args.height-1)))
- cellfmt = "%%-%ss" % maxlen
- for gen in range(args.period + 1):
- # initialize grid to all empty cells
- void = str(gen % 2) if args.strobe else "0"
- grid = [[void] * total_width for i in range(total_height)]
- # set fixed borders
- for row in range(total_height):
- grid[row][0] = grid[row][total_width-1] = void + "'"
- for col in range(1, total_width-1):
- grid[0][col] = grid[total_height-1][col] = void + "'"
- # draw in the search area
- if gen in (0, args.period):
- dy = 2 + (args.height - gen0_height) // 2 + max(0, args.shift_y if gen else -args.shift_y)
- dx = 2 + (args.width - gen0_width) // 2 + max(0, args.shift_x if gen else -args.shift_x)
- for y in range(gen0_height):
- for x in range(gen0_width):
- row = y
- col = x
- if gen and mirror_y: row = gen0_height - row - 1
- if gen and mirror_x: col = gen0_width - col - 1
- if gen and transpose: row, col = col, row
- name = cellname(row, col)
- grid[y + dy][x + dx] = fixed[name] if name in fixed else name
- if args.partial_x:
- for x in range(x + dx + 1, total_width):
- grid[y + dy][x] = "*'"
- else:
- if args.wide:
- top = left = 2
- bottom = total_height - 2
- right = total_width - 2
- else:
- dy = int(round(args.shift_y * gen * 1.0 / args.period))
- top = 2 + (dy if args.shift_y >= 0 else dy - args.shift_y)
- dx = int(round(args.shift_x * gen * 1.0 / args.period))
- left = 2 + (dx if args.shift_x >= 0 else dx - args.shift_x)
- bottom = top + args.height
- right = left + args.width
- for row in range(top, bottom):
- for col in range(left, right):
- grid[row][col] = '*'
- if args.partial_x:
- for col in range(right, total_width):
- grid[row][col] = "*'"
- # print output
- if gen > 0:
- print("")
- for line in grid:
- print(" ".join(cellfmt % cell for cell in line))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement