This follows up on the complicated still life tiling based on octagons by observing that you can reduce the number of tiles by quite a lot if you add placement rules. Note that adjacency can be enforced by jigsaw boundaries, and actually the internal rules can be enforced by further decomposing the patterns into octagonal tiles, but the idea here is that if people are going to place physical tiles (which is my goal), it's OK if they apply some visual constraints. (Yes, taking this to an extreme, you could just lay down black and white tiles for cells while inspecting the neighbors, but I believe the additional annotation bridges the gap between Life master and novice.)
First, here are the 6 still life tiles and an example of what an eater looks like composed of them. There are 6 tiles in which the red marks ("petals") indicate live cells going two cells clockwise from each quadrant. When 4 tiles are placed incident to corners, matching colors to form a colored square, the number of petals is the number of live neighbors. So to build a still life with these tiles (using as many of each as needed and freely rotating them) we only require:
- There are either 2 or 3 petals incident to the center of a black square.
- There are never 3 petals incident to the center of a white square.
The ability to handle still lifes with 6 tiles emboldened me to move up to p2 oscillators. In this case, there are 70 possible tiles (though a few may not fit in any p2). There are 4 colors indicating always live, always dead, live on even steps, live on odd steps. Here is the complete set of tiles, and tilings for blinker, toad, and clock respectively. When 4 tiles are placed incident to corners, matching colors to form a colored square, the number of petals of each color indicates the number of live neighbors in even or odd generations. So to build a still life with these tiles (using as many of each as needed and freely rotating them) we require:
- There are 2 or 3 petals of each color incident to the center of a black (dark gray) square.
- There are never exactly 3 petals of the same color incident to the center of a white square.
- There are always 3 white petals incident to the center of any pink or blue square.
- There are never 2 or 3 black petals incident to the center of any pink or blue square.
Finally, here is a repeating pattern that illustrates the still life tiles at all orientations. It is not a still life (as can be observed; it's a p2 houndtooth pattern in fact). By printing this and cutting at the gray lines (not cyan!) you can make a set of physical tiles. Python code for generating the imagemagick command for the still life PNG.
Code: Select all
COLOR = ["white", "black"]
def petal(x, y, length, i, xf, thickness, angle):
return ("-draw 'translate %d, %d rotate %d translate %d, %d rotate %s "
"path \"M 0, 0 L %d, %d L %d %d L %d, %d Z\"'" %
(length + x, length + y, i * 90, -length, -length, angle,
xf, -thickness, xf + thickness, 0, xf, thickness))
def tile(x, y, length, quadrants):
size = length * 2 + 1
xf = length * 0.6
thickness = length * 0.13
lines = ["-fill '#70f5f6' -draw 'translate %d, %d rectangle 0, 0, %d, %d'" % (x, y, size - 1, size - 1),
"-fill black"]
for i in range(len(quadrants)):
lines.append("-fill %s" % COLOR[quadrants[i]])
lines.append("-draw 'translate %d, %d rotate %d translate %d, %d rectangle 0, 0, %d %d'" %
(length + x, length + y, i * 90, -length, -length, length - 1, length - 1))
for i in range(len(quadrants)):
if quadrants[(i + 1) % 4]:
lines.append("-fill red")
lines.append(petal(x, y, length, i, xf, thickness, 22.5))
if quadrants[(i + 2) % 4]:
lines.append("-fill red")
lines.append(petal(x, y, length, i, xf, thickness, 67.5))
return lines
def pattern(x, y, length, grid, border):
size = length * 2 + 1 + border
lines = []
for i in range(len(grid) - 1):
for j in range(len(grid[i]) - 1):
lines.extend(tile(x + j * size, y + i * size, length,
(grid[i][j], grid[i][j + 1], grid[i + 1][j + 1], grid[i + 1][j])))
return lines
size = 800
lines = []
lines.append("magick -size %dx%d canvas:none -fill lightgray" % (size, size))
lines.append("-draw 'rectangle 0, 0, %s, %s'" % (size, size))
tilecolors = [(a, b, c, d)
for a in range(2) for b in range(2) for c in range(2) for d in range(2)]
x = 60
for quadrant in tilecolors:
if quadrant == min(quadrant[-i:] + quadrant[:-i] for i in range(len(quadrant))):
lines.extend(tile(x, 80, 40, quadrant))
x += 120
eater = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
lines.extend(pattern(150, 300, 40, eater, 0))
lines.append("still.png")
print " \\\n".join(lines)
Code: Select all
COLOR = ["white", "#ff7070", "#7070ff", "#606860"]
oddcolor = "#b00000"
evencolor = "#0000b0"
oldcolor = "#000000"
newcolor = "#ffffff"
color1 = [evencolor, oldcolor, newcolor, evencolor]
color2 = [oddcolor, newcolor, oldcolor, oddcolor]
def petal(x, y, length, i, xf, thickness, angle):
return ("-draw 'translate %d, %d rotate %d translate %d, %d rotate %s "
"path \"M 0, 0 L %d, %d L %d %d L %d, %d Z\"'" %
(length + x, length + y, i * 90, -length, -length, angle,
xf, -thickness, xf + thickness, 0, xf, thickness))
def tile(x, y, length, quadrants):
size = length * 2 + 1
xf = length * 0.6
thickness = length * 0.1
lines = ["-fill '#70f5f6' -draw 'translate %d, %d rectangle 0, 0, %d, %d'" % (x, y, size - 1, size - 1),
"-fill black"]
for i in range(len(quadrants)):
lines.append("-fill '%s'" % COLOR[quadrants[i]])
lines.append("-draw 'translate %d, %d rotate %d translate %d, %d rectangle 0, 0, %d %d'" %
(length + x, length + y, i * 90, -length, -length, length - 1, length - 1))
for i in range(len(quadrants)):
state = quadrants[i]
state1 = quadrants[(i + 1) % 4]
if state1 & 1:
lines.append("-fill '%s'" % color1[state])
lines.append(petal(x, y, length, i, xf, thickness, 11.25))
if state1 & 2:
lines.append("-fill '%s'" % color2[state])
lines.append(petal(x, y, length, i, xf, thickness, 33.75))
state2 = quadrants[(i + 2) % 4]
if state2 & 1:
lines.append("-fill '%s'" % color1[state])
lines.append(petal(x, y, length, i, xf, thickness, 56.25))
if state2 & 2:
lines.append("-fill '%s'" % color2[state])
lines.append(petal(x, y, length, i, xf, thickness, 78.75))
return lines
def pattern(x, y, length, grid, border):
size = length * 2 + 1 + border
lines = []
for i in range(len(grid) - 1):
for j in range(len(grid[i]) - 1):
lines.extend(tile(x + j * size, y + i * size, length,
(grid[i][j], grid[i][j + 1], grid[i + 1][j + 1], grid[i + 1][j])))
return lines
size = 1100
lines = []
lines.append("magick -size %dx%d canvas:none -fill '#559883'" % (size, size))
lines.append("-draw 'rectangle 0, 0, %s, %s'" % (size, size))
tilecolors = [(a, b, c, d)
for a in range(4) for b in range(4) for c in range(4) for d in range(4)]
x = 0
y = 0
for quadrant in tilecolors:
if quadrant == min(quadrant[-i:] + quadrant[:-i] for i in range(len(quadrant))):
lines.extend(tile(x + 60, y + 60, 40, quadrant))
x += 100
if x > 900:
x = 0
y += 100
blinker = [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 2, 3, 2, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]]
lines.extend(pattern(150, 800, 25, blinker, 0))
toad = [
[0, 0, 0, 0, 0, 0],
[0, 0, 2, 0, 0, 0],
[0, 3, 1, 1, 2, 0],
[0, 2, 1, 1, 3, 0],
[0, 0, 0, 2, 0, 0],
[0, 0, 0, 0, 0, 0]]
lines.extend(pattern(400, 800, 25, toad, 0))
clock = [
[0, 0, 0, 0, 0, 0],
[0, 0, 2, 1, 0, 0],
[0, 1, 3, 0, 2, 0],
[0, 2, 0, 3, 1, 0],
[0, 0, 1, 2, 0, 0],
[0, 0, 0, 0, 0, 0]]
lines.extend(pattern(700, 800, 25, clock, 0))
lines.append("period2.png")
print " \\\n".join(lines)
Code: Select all
COLOR = ["white", "black"]
def petal(x, y, length, i, xf, thickness, angle):
return ("-draw 'translate %d, %d rotate %d translate %d, %d rotate %s "
"path \"M 0, 0 L %d, %d L %d %d L %d, %d Z\"'" %
(length + x, length + y, i * 90, -length, -length, angle,
xf, -thickness, xf + thickness, 0, xf, thickness))
def tile(x, y, length, quadrants):
size = length * 2 + 1
xf = length * 0.6
thickness = length * 0.13
lines = ["-fill '#70f5f6' -draw 'translate %d, %d rectangle 0, 0, %d, %d'" % (x, y, size - 1, size - 1),
"-fill black"]
for i in range(len(quadrants)):
lines.append("-fill %s" % COLOR[quadrants[i]])
lines.append("-draw 'translate %d, %d rotate %d translate %d, %d rectangle 0, 0, %d %d'" %
(length + x, length + y, i * 90, -length, -length, length - 1, length - 1))
for i in range(len(quadrants)):
if quadrants[(i + 1) % 4]:
lines.append("-fill red")
lines.append(petal(x, y, length, i, xf, thickness, 22.5))
if quadrants[(i + 2) % 4]:
lines.append("-fill red")
lines.append(petal(x, y, length, i, xf, thickness, 67.5))
return lines
def pattern(x, y, length, grid, border):
size = length * 2 + 1 + border
lines = []
for i in range(len(grid) - 1):
for j in range(len(grid[i]) - 1):
lines.extend(tile(x + j * size, y + i * size, length,
(grid[i][j], grid[i][j + 1], grid[i + 1][j + 1], grid[i + 1][j])))
return lines
size = 984
lines = []
lines.append("magick -size %dx%d canvas:none -fill lightgray" % (size, size))
lines.append("-draw 'rectangle 0, 0, %s, %s'" % (size, size))
tilecolors = [(a, b, c, d)
for a in range(2) for b in range(2) for c in range(2) for d in range(2)]
houndstooth = [
[0, 0, 1, 0] * 3 + [0],
[1, 1, 1, 0] * 3 + [1],
[0, 1, 1, 1] * 3 + [0],
[0, 1, 0, 0] * 3 + [0]]
houndstooth = houndstooth * 3 + [houndstooth[0]]
lines.extend(pattern(0, 0, 40, houndstooth, 1))
lines.append("tiling.png")
print " \\\n".join(lines)