Tiling with placement rules for still life and p2 oscillators

For general discussion about Conway's Game of Life.
Post Reply
pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

Tiling with placement rules for still life and p2 oscillators

Post by pcallahan » January 25th, 2020, 11:11 pm

I'm starting a new thread to consolidate my posts on other threads and provide nicer pictures than before, which I made using python and imagemagick. (Scripts are added at the end.)

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.
still.png
still.png (33.39 KiB) Viewed 1528 times
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.
(An alternative coloring uses white petals on black squares and black petals on white squares; I'm not sure which is better.)

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.
period2.png
period2.png (230.79 KiB) Viewed 1528 times
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.
These are admittedly less practical for assembling oscillators by hand. However, it might make an interesting jigsaw puzzle to split up a larger oscillator into a tile set that uniquely assembles to it when following the rules (and it is relatively easy to add jigsaw boundaries to enforce the colors).

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.
tiling.png
tiling.png (82.16 KiB) Viewed 1525 times
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)
Python code for generating the imagemagick command for the period-2 PNG.

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)
Python code for generating the imagemagick command for the houndstooth 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 = 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)

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

Re: Tiling with placement rules for still life and p2 oscillators

Post by pcallahan » January 27th, 2020, 12:48 am

Bridging the gap between visual placement rules and a pure tiling, we can replace the petals with toothed octagons, like the original tiling. In this case, any black octagon in a still life must have 2 or 3 teeth, and any white octagon must not have exactly 3 dents. Like the previous example, we can use 6 square tiles and check constraints visually.
octagons.png
octagons.png (75.41 KiB) Viewed 1482 times
But in addition, the octagons can be treated as tiles, so the placement could be forced without requiring a visual inspection. There are 37 possible octagons up to rotations, fewer with flips, and some ways of decomposing them discussed in another thread.

And here is the python script to use with imagemagick.

Code: Select all

import math

COLOR = ["white", "black"]

def petal(x, y, length, i, xf, thickness, angle, hasTooth, state):
  bump = thickness if state else -thickness * 0.5
  tooth = "L %d %d L %d %d L %d %d L %d %d" % (
    xf, -thickness * 0.3, xf + bump, -thickness * 0.5, xf + bump, thickness * 0.5, xf, thickness * 0.3) if hasTooth else "" 
  return ("-draw 'translate %d, %d rotate %d translate %d, %d rotate %s "
          "path \"M 0, 0 L %d, %d %s L %d, %d Z\"'" %
          (length + x, length + y, i * 90, -length, -length, angle,
          xf, -thickness, tooth, xf, thickness))

def tile(x, y, length, quadrants):
  size = length * 2 + 1
  xf = [length * 0.7, length * 0.6]
  thickness = [t * math.tan(math.radians(22.5)) * 1.04 for t in xf]
  lines = ["-fill 'lightgray' -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 lightgray") 
      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]
      lines.append("-fill %s" % COLOR[state]) 
      lines.append(petal(x, y, length, i, xf[state], thickness[state], 22.5, quadrants[(i + 1) % 4], state))
      lines.append("-fill %s" % COLOR[state]) 
      lines.append(petal(x, y, length, i, xf[state], thickness[state], 67.5, quadrants[(i + 2) % 4], state))
  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 skyblue" % (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(10, 250, 35, eater, 1))

block_on_table = [
  [0, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 1, 0],
  [0, 0, 0, 1, 1, 0],
  [0, 0, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 1, 0],
  [0, 0, 0, 0, 0, 0]]

lines.extend(pattern(400, 250, 35, block_on_table, 1))

lines.append("octagons.png")

print " \\\n".join(lines)

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

Re: Tiling with placement rules for still life and p2 oscillators

Post by pcallahan » February 4th, 2020, 1:30 pm

This is really just a slight tweak on the first split-dot approach (update 4 in this post: https://conwaylife.com/forums/viewtopic ... 100#p87584), though somehow I was most of the way done before realizing it. I think it makes for a better visualization. The main idea is to use circles instead of squares, and have quarter and half circles at the join points. I also use just two colors, omit any line boundaries, and invert the cell colors within live cells, because I think it is easier to see them and the rules can be explained in terms of "white dots" and "black dots" without additional context.
tiles.png
tiles.png (6.46 KiB) Viewed 1411 times
The reason I think circles are better is that without being told anything about the tiles, most people will understand that they are supposed to make complete circles, whereas it might seem reasonable to make checkerboards out of the squares. I also think they are more attractive designs, though this is clearly subjective.

As noted (https://conwaylife.com/forums/viewtopic ... 100#p88535), they are not "self-checking" but you can imagine using bumps and dents and adding plastic gadgets over the top to force a still life. I prefer to state a rule, e.g. in the case of B23/S3 still lifes, "white dots must be in clusters of 2 or 3, black dots must not be in clusters of 3." But just by changing the rules, you can handle any 1-step constraint on any 2-state Moore neighborhood CA--not limited to totalistic assuming you are willing to memorize neighborhood shapes.

Here's a constellation of two phases of glider, a blinker, and a block.
patterns.png
patterns.png (54.09 KiB) Viewed 1411 times
I think it is very easy to pick out the deaths and fairly easy to pick out the births. It would need to be tested along with other designs on someone who hasn't been playing around with Life for many years.

Finally, here's a big complicated still life that came out of lifesrc.
rand9x9.png
rand9x9.png (100.5 KiB) Viewed 1411 times
I think it is quite easy for anyone to convince themselves it's a still life (aside from those who already think it's trivial). This moves the fan-out work away from your brain or visual cortex and outsources it to the tiles but leaves constraint checking in your head.

So where am I going with this? Well, I think the whole idea of doing computation with tiling constraints is intrinsically interesting (even if they're not pure geometric constraints) but I have more specific (though it's a stretch to say practical) motives:
  • Can we make this a basis of puzzles and games?
    • E.g. a simple puzzle would be to build a still life out of a particular multi-set of tiles, such as the 9 tiles in a block. 4 corner pieces, 4 edge pieces, 1 interior piece.
    • Would a 2-player Scrabble-like game be playable where the goals is to build still lifes instead of words.
  • A tool for analyzing/developing intuition about Life or other 2-state CAs offline. Sometimes it helps to get away from a screen.
  • A decorative scheme along the lines of Truchet tiles. This is a stretch. E.g. the big still life may not seem pretty, but more symmetric patterns might be, and forcing them to be still lifes gives it a recognizable motif.
Here's a script to generate imagemagick commands for the tiles at all orientations.

Code: Select all

COLOR = ["white", "black"]

def tile(quadrants, x, y, side, scale):
  lines = []
  length = side * scale
  radius = side * (scale - 0.5)
  inner = length - radius
  dots = length - 1.7 * side
 
  lines.append("-stroke none") 
  for i in range(4):
    theta = 90 * i - 180

    (a, b, c, d) = quadrants[i:] + quadrants[:i]
    if a:
      lines.append("-fill %s -draw 'translate %5.3f %5.3f rotate %5.3f path \"M %5.3f %5.3f L %5.3f %5.3f A %5.3f %5.3f 0 0 0 %5.3f %5.3f\"'" %
                 (COLOR[a], x, y, theta, length, length, length, inner, radius, radius, inner, length))
    small = side * 0.65
    lines.append("-fill %s -draw 'translate %5.3f %5.3f rotate %5.3f path \"M %5.3f %5.3f A %5.3f %5.3f 0 0 0 %5.3f %5.3f\"'" %
                (COLOR[a ^ b], x, y, theta, dots + small, length, small, small, dots - small, length))
    lines.append("-fill %s -draw 'translate %5.3f %5.3f rotate %5.3f circle %5.3f %5.3f %5.3f %5.3f'" %
                (COLOR[a ^ c], x, y, theta, dots, dots, dots, dots + small))
    lines.append("-fill %s -draw 'translate %5.3f %5.3f rotate %5.3f path \"M %5.3f %5.3f A %5.3f %5.3f 0 0 0 %5.3f %5.3f\"'" %
                (COLOR[a ^ d], x, y, theta, length, dots - small, small, small, length, dots + small))
  return lines

for a in range(2):
  for b in range(2):
    for c in range(2):
      for d in range(2):
        quadrants = (a, b, c, d)
        side = 10
        scale = 5 
        dim = side * scale * 2 + 1
        lines = ["magick -size %dx%d canvas:%s" % (dim, dim, COLOR[0])]
        lines.extend(tile(quadrants, side * scale, side * scale, side, scale))
        lines.append("block_%d%d%d%d.png" % quadrants)
        print(" \\\n").join(lines)
And another that uses imagemagick montage to piece the tiles together into patterns.

Code: Select all

import math

def pattern(grid, name):
  tiles = []
  for i in range(len(grid) - 1): 
    for j in range(len(grid[i]) - 1): 
      quadrants = (grid[i][j], grid[i][j + 1], grid[i + 1][j + 1], grid[i + 1][j])
      tiles.append("block_%d%d%d%d.png" % quadrants)
  print("montage -tile %dx%d -geometry 100x100 %s %s.png" % (len(grid[0]) - 1, len(grid) - 1, " ".join(tiles), name))

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]]
pattern(eater, "eater")

block_on_table = [
  [0, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 1, 0],
  [0, 0, 0, 1, 1, 0],
  [0, 0, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 1, 0],
  [0, 0, 0, 0, 0, 0]]
pattern(block_on_table, "b_on_t")

rand9x9 = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0],
[0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
pattern(rand9x9, "rand9x9")

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]]
pattern(houndstooth, "houndstooth")

counter_example = [
  [1, 1, 0, 0, 1, 1, 0, 0, 0] * 2,
  [0, 0, 0, 1, 1, 0, 0, 1, 1] * 2,
  [0, 1, 1, 0, 0, 0, 1, 1, 0] * 2]
counter_example = counter_example * 4
pattern(counter_example, "ce")

gliders = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
  [0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0], 
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
pattern(gliders, "gliders")

blinker = [
  [0, 0, 0, 0, 0],
  [0, 1, 1, 1, 0],
  [0, 0, 0, 0, 0]]
pattern(blinker, "blinker")

patterns = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
  [0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0], 
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
pattern(patterns, "patterns")

print("montage -tile 6x1 -geometry 101x101+20+20 -background none block_0000.png block_0001.png block_0011.png block_0101.png block_0111.png block_1111.png tiles.png")

Update: Another thing I like about this visualization is that the black dots on white remind me of punch tape. I wasn't sure if that's right. The only punch things I ever worked with (briefly) were cards, which had rectangular holes, but here's a picture and as I believed, it uses round holes. http://pollockspark.com/wp-content/uplo ... H-TAPE.jpg

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

Re: Tiling with placement rules for still life and p2 oscillators

Post by pcallahan » February 20th, 2020, 1:52 am

Here's a variation I haven't fully fleshed out. Suppose instead of half dots split across tiles, each tile or else its neighbor owns the dot. This is similar to counting the next two cells clockwise, but it can vary from tile to tile. We can indicate that a tile owns the dot by attaching the other half, creating a bulge. If its neighbor owns the dot, we leave a half circle gap instead of a dot.
Screen Shot 2020-02-19 at 9.42.00 PM.png
Screen Shot 2020-02-19 at 9.42.00 PM.png (31.45 KiB) Viewed 1284 times
Obviously, we need a lot more than 6 tiles to support all possibilities. If we stick to a single orientation like gap/bulge going clockwise around a corner then we can get by with 6 and we're back to the original counting. If we allow some variations and assign counts to corners, then we should be able to reduce the number of puzzle pieces needed to test the still-life condition.Can the total be reduced below the 27 tiles used in https://conwaylife.com/forums/viewtopic ... 100#p88361?

Update: One way to get more possibilities is simply to start with a set of 6 in which the dot is owned going clockwise and then allow the tiles to be flipped for the full set of counterclockwise tiles. I'm not sure how much flexibility there is in terms of mixing clockwise and counterclockwise tiles. Since many sides are flat, the placement of one tile does not define the orientation of all those that surround it.

Another thing, more related to the previous posting than this. I think these pictures are a little more interesting if the tiles are separated as below so they can be seen individually at the appropriate orientations.
rand9x9.png
rand9x9.png (376.48 KiB) Viewed 1252 times
This makes it clear there are just 6 distinct tiles and also shows how the dots are shared between tiles. (Note: this is not a still life, though it took me a while to notice. I think the petals are more obvious for spotting births, as much as I like the symmetry of the half dots.)

Update: after making tiles with card stock (too light but a prototype) I decided it is better to use larger dots or else the slightest slippage makes it hard to resolve paired halves. Anyone have a strong preference? I think the big dots are uglier but necessary if you are doing it with real tiles about an inch on a side. The small ones may be better for a decorative scheme with larger tiles.
rand9x9.png
rand9x9.png (448.25 KiB) Viewed 1206 times

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

Re: Tiling with placement rules for still life and p2 oscillators

Post by pcallahan » February 29th, 2020, 12:05 pm

These tiles work pretty well after making them with printable magnet sheets using an inkjet printer. It's a cheap and effective solution that doesn't require 3D printing or a custom manufacturing order. The price was $7.34 for 5 sheets from Amazon. Cutting out the squares took around fifteen minutes for me, I think, using scissors. You could probably streamline some of that and get straighter edges with a paper cutter.

Here they are on a small whiteboard we have at home, mostly arranged into common still life patterns with some left over
20200229_073729.jpg
20200229_073729.jpg (267.88 KiB) Viewed 1123 times
I was able to print them at a reasonable size in a 9x12 grid containing 108 tiles in these proportions (cell values going clockwise around corners):

Code: Select all

(0, 0, 0, 1) 48
(0, 0, 1, 1) 36
(0, 1, 0, 1) 12
(0, 1, 1, 1) 10
(1, 1, 1, 1) 2
One concern I had was how much margin to leave. I know from experience that my printer does not print all the way up to the edge. I may have left more margin than necessary (I was thinking about using the extra strips as framing, but it's not much anyway.) The PDF file with margins is shared on Google drive.

Post Reply