Implementieren Sie das Spiel des Lebens auf etwas anderem als einem regulären Raster


114

Conways Spiel des Lebens wird (fast) immer auf einem regelmäßigen quadratischen Raster gespielt, muss es aber nicht sein.

Schreiben Sie ein Programm, das die Standardregeln für benachbarte Zellen aus Conways Spiel des Lebens auf einer zweidimensionalen Kachelung der euklidischen Ebene implementiert, bei der es sich nicht um eine regelmäßige Kachelung von Quadraten, Dreiecken oder Sechsecken handelt .

Insbesondere die Kacheln, die Sie wählen ...

  1. Muss mindestens 2 (aber endlich viele) unterschiedlich geformte Prototile enthalten .
    • Die verschiedenen Formen können skaliert oder gedreht sein.
    • Sie müssen in der Lage sein, das gesamte Flugzeug zu kacheln, ohne Löcher zu hinterlassen.
    • Sie müssen einfache Polygone mit endlichem Umfang sein. (Sie können nicht schwach einfach sein.)
  2. Muss sich isomorph von den quadratischen, dreieckigen und sechseckigen Gittern unterscheiden.
    • Jede Kachelung, die sich zu einem regelmäßigen quadratischen, dreieckigen oder sechseckigen Raster zusammensetzt, ist nicht zulässig. (Sie können weiterhin Quadrate / Dreiecke / Sechsecke für andere Fliesen verwenden.)
    • Die Grenze zwischen zwei Prototilen kann mehrere Kanten und Scheitelpunkte enthalten, muss jedoch durchgehend sein.

Ihre Kacheln können periodisch oder aperiodisch sein, aber wenn sie erweitert werden, um die gesamte Ebene abzudecken, muss jedes Prototil unendlich oft erscheinen. (Also keine "Hardcodierung" bestimmter Teile Ihrer Kacheln, um die folgenden zusätzlichen Punkte zu erzielen.)

Jedes Ihrer Prototile repräsentiert eine Game of Life-Zelle, die an andere Zellen angrenzt:

  • Zellen, die Kanten oder Scheitelpunkte gemeinsam haben, werden als Nachbarn betrachtet.
  • Zellen, die mehrere Kanten oder Scheitelpunkte gemeinsam haben, werden nach wie vor nur einmal an den Nachbarn gezählt.
  • Zellen können sich nicht selbst benachbart machen.

Kacheln von Inspirationslinks:

Ausgabe

Ihr Programm sollte eine Art grafische Darstellung Ihrer Kacheln mit dem darin gespielten Game of Life ausgeben, die Sie natürlich im Format image / gif / jsfiddle veröffentlichen sollten.

Bitte zeichnen Sie die Kantenlinien der Kacheln und verwenden Sie eine helle Farbe für tote Zellen und eine dunkle Farbe für lebende Zellen.

Wertung

Ihre Einreichungspunktzahl ist die Anzahl der Upvotes minus Downvotes plus Extrapunkte für das Erkennen gemeinsamer Game of Life-Muster in Ihren Kacheln:

  • Finden Sie ein Stillleben - ein Muster, das sich von Generation zu Generation nicht ändert. (+2)
  • Finden Sie Oszillatoren mit den Perioden 2 bis 29. (+3 für jede Periode, die Sie finden, bis zu insgesamt 5 Perioden oder maximal +15 Punkte)
  • Suchen Sie einen Oszillator mit einer Periode von 30 oder mehr. (+7)
  • Finden Sie ein Raumschiff - etwas, das beliebig weit von seinem Startort entfernt werden kann, ohne irgendwelche Trümmer zu hinterlassen. (Es muss nicht unbedingt ein sich bewegender Oszillator sein.) (+10)
  • Suchen Sie ein anderes Raumschiff, das sich deutlich anders bewegt (und keine gespiegelte Version des ersten Raumschiffes ist), z. B. Segelflugzeug und LWSS . (+10)
  • Finde ein Muster des unendlichen Wachstums . Sie müssen nicht beweisen, dass das Wachstum unendlich ist. Zeigen Sie uns nur genügend Beweise für das Muster, dass es praktisch sicher ist. (+25)
  • Finden Sie eine Waffe - etwas, das Raumschiffe für immer erzeugt (dies gilt auch als unendliches Wachstum). (+50)

Die unendlichen Wachstumsmuster müssen mit einer endlichen Anzahl lebender Zellen beginnen und die anderen Muster müssen immer eine begrenzte Anzahl lebender Zellen enthalten (z. B. sollte ein Raumschiff mit der Zeit nicht beliebig groß werden).

Aufgrund der Natur von aperiodischen Kacheln ist es wahrscheinlich, dass viele dieser Muster nicht in ihnen implementiert werden können. So erhält jedes nachweislich aperiodische Kacheln automatisch +40 Punkte. Ein Muster, das an einer Stelle in einer aperiodischen Kachelung funktioniert, muss an keiner anderen Stelle funktionieren.

Jeder der Boni kann nur einmal angewendet werden. Natürlich müssen wir die Ausgabe sehen, um sie zu verifizieren. Die höchste Punktzahl gewinnt.

Anmerkungen

  • Für jede Antwort können nur Boni für ein bestimmtes Tiling vergeben werden. (Sie können jedoch auch verwandte Fliesen einbeziehen.)
  • Die Spielregeln lauten wie folgt:
    1. Jede lebende Zelle mit weniger als 2 oder mehr als 3 lebenden Nachbarn stirbt.
    2. Jede tote Zelle mit genau 3 lebenden Nachbarn wird lebendig.
    3. Andere Zellen ändern sich nicht.
  • Muster für die zusätzlichen Punkte sollten unabhängig von den Randbedingungen möglich sein, ansonsten können Sie beliebige Randbedingungen auswählen.
  • Standardmäßig sollte der Hintergrund alle toten Kacheln sein.

Vielen Dank an Peter Taylor, Jan Dvorak und Githubphagocyte, die geholfen haben, Lücken in die Fliesen zu schlagen, die erlaubt sein sollten.

(Falls jemand neugierig ist, ist dies definitiv mein Favorit unter meinen eigenen Herausforderungen .)


7
Es gibt ein starkes Argument dafür, dass es nicht Conways Leben ist, sondern ein Leben-ähnlicher Automat, wenn es nicht auf einem regelmäßigen quadratischen Gitter liegt. Sicherlich, wenn Sie über "die Standardregeln von Conways Spiel des Lebens" sprechen und Kacheln ausschließen möchten, in denen jede Zelle genau 8 Nachbarn hat, fragen Sie nach einem Oxymoron.
Peter Taylor

2
@PeterTaylor Das ist ein ziemlich semantischer Unterschied, den ich mir in diesem Zusammenhang nicht vorstellen kann, aber nur um sicherzugehen, dass ich ihn geändert habe (zusammen mit Martins Vorschlägen).
Calvins Hobbys

4
Muss ich die euklidische Ebene kacheln ?
John Dvorak

3
Ihr " topologisch unterscheidbarer " Zustand hinterlässt auch eine massive Lücke, die die direkte Implantation des Standard-Lebens durch ein Gitter von Quadraten ermöglicht, von denen jeder einen dreieckigen Keil von seiner Oberkante entfernt hat. Das Ergebnis ist eine Aneinanderreihung von Dreiecken und Quadrat-Minus-Dreiecken, wobei jedes Dreieck zwei Quadrate für Nachbarn hat, jedes Quadrat zwei Dreiecke und acht Quadrate hat und die Dreiecke einfach ignoriert werden können. Das ist eine billige Basisnote von 10230 Punkten.
Peter Taylor

4
Die Unfähigkeit, es sofort auszusortieren, ist genau der Grund für das Schließen. Es verhindert, dass Antworten gepostet werden und dass sie repariert werden.
Peter Taylor

Antworten:


82

Penrose rhombii in Python, +97 Punkte

Ich entschied mich für eine Penrose-Kachel, die aus zwei unterschiedlich geformten Rauten bestand und 3 bis 8 pro Scheitel traf. Diese Penrose-Fliesen haben sich an anderer Stelle als aperiodisch erwiesen. Die Simulation ist grafisch (via Pygame) und interaktiv. Kommentare geben zwei Stellen im Code an, an denen die Implementierung des Algorithmus aus einer anderen Quelle stammt.

Animation des Penrose-Lebens, das mit dem Oszillator p12 endet

Es gibt viele kleine Nachbarschaftsstillleben:

Stillleben im Penrose-Leben Stillleben im Penrose-Leben Stillleben im Penrose-Leben

Jeder Eckpunkt mit vier "Ein" -Nachbarn ist ein Stillleben:

Schmetterlingsstillleben im Penrose-Leben stacheliges Stillleben im Penrose-Leben Pacman Stillleben in Penrose Leben

Jede Schleife, in der keine toten inneren Zellen drei Zellen der Schleife berühren, ist auch ein Stillleben:

Schleifenstillleben im Penrose-Leben Schleifenstillleben im Penrose-Leben

Es gibt Oszillatoren mit verschiedenen Frequenzen:

p2: (viele variationen)

Periode 2 Oszillator im Penrose-Leben

p3:

Periode 3 Oszillator im Penrose-Leben

p4:

Periode 4 Oszillator im Penrose-Leben Periode 4 Oszillator im Penrose-Leben Periode 4 Oszillator im Penrose-Leben

p5:

Periode 5 Oszillator im Penrose-Leben

p6:

Periode 6 Oszillator im Penrose-Leben

p7:

Periode 7 Oszillator im Penrose-Leben Periode 7 Oszillator im Penrose-Leben

S12:

Periode 12 Oszillator im Penrose-Leben

S20:

Periode 20 Oszillator im Penrose-Leben

Die Regeln und Erläuterungen, wie sie in den meisten Fällen geschrieben wurden, lassen keine Segelflugzeuge oder Kanonen in einer nicht geplanten aperiodischen Kachelung zu. Das lässt ein unendliches Wachstum, von dem ich behaupten würde, dass es unwahrscheinlich ist, und einen p30 + Oszillator, der mit ziemlicher Sicherheit existiert, aber eine Weile braucht, um zu finden.

python penrose-life.pyerzeugt eine einzelne zufällig gefärbte periodische Kachelung python -O penrose-life.pyoder ./penrose-life.pyführt die Simulation nur tatsächlich aus. Während des Betriebs wird versucht, Oszillatoren zu identifizieren, und wenn ein Oszillator gefunden wird (p> 2), wird ein Screenshot erstellt. Nach dem Aufzeichnen eines Oszillators oder einer blockierten Karte wird die Karte zufällig ausgewählt.

Durch Klicken auf eine Zelle in der Simulation wird diese umgeschaltet.

Die folgenden Tastenkombinationen sind in der Simulation vorhanden:

  • Escape - Programm beenden
  • Mit Space kannst du das gesamte Board zufällig sortieren
  • P - Simulation anhalten
  • S - Einzelschritt der Simulation
  • F - Schaltet den "schnellen" Modus um und rendert nur jeden 25. Frame

Der ursprüngliche Keim des Penrose-Kachelalgorithmus ist ein Kreis aus zehn schmalen Dreiecken. Dies kann in ein einzelnes Dreieck oder eine andere Anordnung von Dreiecken, symmetrisch oder nicht, geändert werden.

Quelle:

#!/usr/bin/env python -O

# tiling generation code originally from http://preshing.com/files/penrose.py

import sys
import math
import time
import cairo
import cmath
import random
import pygame

#TODO: command line parameters
#------ Configuration --------
IMAGE_SIZE = (1200, 1200)
OFFX = 600
OFFY = 600
RADIUS = 600
if __debug__: NUM_SUBDIVISIONS = 5
else: NUM_SUBDIVISIONS = 7
#-----------------------------

goldenRatio = (1 + math.sqrt(5)) / 2

class Triangle():
    def __init__(self, parent = None, color = 0, corners = []):
        self.parent = parent
        self.other_half = None
        # immediate neighbor 0 is on BA side, 1 is on AC side
        self.neighbors = [None, None]
        # all_neighbors includes diagonal neighbors
        self.all_neighbors = set()
        # child 0 is first on BA side, 1 is second, 2 is on AC side
        self.children = []
        self.color = color
        if __debug__: self.debug_color = (random.random(),random.random(),random.random())
        self.state = random.randint(0,1)
        self.new_state = 0
        self.corners = corners
        self.quad = None
    def __repr__(self):
        return "Triangle: state=" + str(self.state) + \
            " color=" + str(self.color) + \
            " parent=" + ("yes" if self.parent else "no") + \
            " corners=" + str(self.corners)
    # break one triangle up into 2-3 smaller triangles
    def subdivide(self):
        result = []
        A,B,C = self.corners
        if self.color == 0:
            # Subdivide red triangle
            P = A + (B - A) / goldenRatio
            result = [Triangle(self, 0, (C, P, B)), Triangle(self, 1, (P, C, A))]
        else:
            # Subdivide blue triangle
            Q = B + (A - B) / goldenRatio
            R = B + (C - B) / goldenRatio
            result = [Triangle(self, 1, (Q, R, B)), Triangle(self, 0, (R, Q, A)), Triangle(self, 1, (R, C, A))]
        self.children.extend(result)
        return result;
    # identify the left and right neighbors of a triangle
    def connect_immediate(self):
        o = None
        n = self.neighbors
        if self.parent:
            if self.color == 0: # red child
                if self.parent.color == 0: # red parent
                    if self.parent.neighbors[0]:
                        if self.parent.neighbors[0].color == 0: # red left neighbor
                            o = self.parent.neighbors[0].children[0]
                        else: # blue left neighbor
                            o = self.parent.neighbors[0].children[1]
                    n[0] = self.parent.children[1]
                    if self.parent.other_half:
                        n[1] = self.parent.other_half.children[0]
                else: # blue parent
                    if self.parent.neighbors[0]:
                        if self.parent.neighbors[0].color == 0: # red left neighbor
                            o = self.parent.neighbors[0].children[0]
                        else: # blue left neighbor
                            o = self.parent.neighbors[0].children[1]
                    n[0] = self.parent.children[0]
                    n[1] = self.parent.children[2]
            else: # blue child
                if self.parent.color == 0: # red parent
                    if self.parent.neighbors[1]:
                        if self.parent.neighbors[1].color == 0: # red right neighbor
                            o = self.parent.neighbors[1].children[1]
                        else: # blue right neighbor
                            o = self.parent.neighbors[1].children[2]
                    n[0] = self.parent.children[0]
                    if self.parent.neighbors[0]:
                        if self.parent.neighbors[0].color == 0: # red left neighbor
                            n[1] = self.parent.neighbors[0].children[1]
                        else: # blue left neighbor
                            n[1] = self.parent.neighbors[0].children[0]
                else: # blue child of blue parent
                    if self.corners[2] == self.parent.corners[1]: # first blue child
                        if self.parent.other_half:
                            o = self.parent.other_half.children[0]
                        n[0] = self.parent.children[1]
                        if self.parent.neighbors[0]:
                            if self.parent.neighbors[0].color == 0: # red left neighbor
                                n[1] = self.parent.neighbors[0].children[1]
                            else: #blue left neighbor
                                n[1] = self.parent.neighbors[0].children[0]
                    else: # second blue child
                        if self.parent.neighbors[1]:
                            if self.parent.neighbors[1].color == 0: # red right neighbor
                                o = self.parent.neighbors[1].children[1]
                            else: # blue right neighbor
                                o = self.parent.neighbors[1].children[2]
                        if self.parent.other_half:
                            n[0] = self.parent.other_half.children[2]
                        n[1] = self.parent.children[1]
        self.other_half = o
        if o:
            self.state = self.other_half.state
            if __debug__: self.debug_color = self.other_half.debug_color

#TODO: different seed triangle configurations
# Create wheel of red triangles around the origin
triangles = [[]]
for i in xrange(10):
    B = cmath.rect(RADIUS, (2*i - 1) * math.pi / 10)+OFFX+OFFY*1j
    C = cmath.rect(RADIUS, (2*i + 1) * math.pi / 10)+OFFX+OFFY*1j
    if i % 2 == 0:
        B, C = C, B  # Make sure to mirror every second triangle
    triangles[0].append(Triangle(None, 0, (OFFX+OFFY*1j, B, C)))

# identify the neighbors of the starting triangles
for i in xrange(10):
    if i%2:
        triangles[0][i].neighbors[0] = triangles[0][(i+9)%10]
        triangles[0][i].neighbors[1] = triangles[0][(i+1)%10]
    else:
        triangles[0][i].neighbors[1] = triangles[0][(i+9)%10]
        triangles[0][i].neighbors[0] = triangles[0][(i+1)%10]

# Perform subdivisions
for i in xrange(NUM_SUBDIVISIONS):
    triangles.append([])
    for t in triangles[i]:
        triangles[i+1].extend(t.subdivide())
    for t in triangles[i+1]:
        t.connect_immediate()

# from here on, we only deal with the most-subdivided triangles
tris = triangles[NUM_SUBDIVISIONS]

# make a dict of every vertex, containing a list of every triangle sharing that vertex
vertices = {}
for t in tris:
    for c in t.corners:
        if c not in vertices:
            vertices[c] = []
        vertices[c].append(t)

# every triangle sharing a vertex are neighbors of each other
for v,triset in vertices.iteritems():
    for t in triset:
        t.all_neighbors.update(triset)

# combine mirrored triangles into quadrilateral cells
quads = []
total_neighbors = 0
for t in tris:
    if t.quad == None and t.other_half != None:
        quads.append(t)
        q = t
        q.corners = (q.corners[0], q.corners[1], q.other_half.corners[0], q.corners[2])
        q.quad = q
        q.other_half.quad = q
        q.all_neighbors.update(q.other_half.all_neighbors)
        q.all_neighbors.remove(q.other_half)
        q.all_neighbors.remove(q)
        total_neighbors += len(q.all_neighbors)

# clean up quads who still think they have triangles for neighbors
for q in quads:
    new_neighbors = set()
    for n in q.all_neighbors:
        if len(n.corners)==3:
            if n.other_half:
                if len(n.other_half.corners)==4:
                    new_neighbors.add(n.other_half)
        else:
            new_neighbors.add(n)
    q.all_neighbors = new_neighbors


# # adopt your other half's neighbors, minus them and yourself. mark other half as dead.
# for t in tris:
#     if t.other_half:
#         t.all_neighbors.update(t.other_half.all_neighbors)
#     t.all_neighbors.remove(t)
#     if t.other_half and t.other_half in t.all_neighbors:
#         t.all_neighbors.remove(t.other_half)
#     if t.other_half and not t.dead_half:
#         t.other_half.dead_half = True

pygame.init()
screen = pygame.display.set_mode(IMAGE_SIZE, 0, 32)
pygame.display.set_caption("Penrose Life")
pygame.display.flip()

paused = False
fast = False
randomize = True
found_oscillator = 0
randomized_tick = 0
tick = 0
timed_tick = 0
timed_tick_time = time.clock()
render_countdown = 0

history_length = 45
quad_history = [[0]*len(quads)]*history_length
quad_pointer = 0

myfont = pygame.font.SysFont("monospace", 15)
guidish = random.randint(0,99999999)

while True:

    tick += 1
    if tick - randomized_tick > 1000 and render_countdown == 0:
        randomize = True
    edited = False
    step = False
    if found_oscillator > 0 and render_countdown == 0:
        print "Potential p" + str(found_oscillator) + " osillator"
        render_countdown = found_oscillator
    if render_countdown == 0: # don't handle input while rendering an oscillator
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit(0)
            elif event.type == pygame.KEYDOWN:
                # print event
                if event.scancode == 53: # escape
                    sys.exit(0)
                elif event.unicode == " ": # randomize
                    randomize = True
                    edited = True
                elif event.unicode == "p": # pause
                    paused = not paused
                elif event.unicode == "f": # fast
                    fast = not fast
                elif event.unicode == "s": # step
                    paused = True
                    step = True
            elif event.type == pygame.MOUSEBUTTONDOWN:
            # click to toggle a cell
                x = event.pos[0]
                y = event.pos[1]
                for q in quads:
                    poly = [(c.real,c.imag) for c in q.corners]
                    # http://www.ariel.com.au/a/python-point-int-poly.html
                    n = len(poly)
                    inside = False
                    p1x,p1y = poly[0]
                    for i in range(n+1):
                        p2x,p2y = poly[i % n]
                        if y > min(p1y,p2y):
                            if y <= max(p1y,p2y):
                                if x <= max(p1x,p2x):
                                    if p1y != p2y:
                                        xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                                    if p1x == p2x or x <= xinters:
                                        inside = not inside
                        p1x,p1y = p2x,p2y
                    if inside:
                        edited = True
                        q.state = 0 if q.state==1 else 1

    if randomize and render_countdown == 0:
        randomized_tick = tick
        randomize = False
        for q in quads:
            q.state = random.randint(0,1)
            edited = True

    if (not fast) or (tick%25==0) or edited or render_countdown > 0:
        # draw filled quads
        for q in quads:
            cs = [(c.real,c.imag) for c in q.corners]
            if __debug__:
                color = q.debug_color
                color = (int(color[0]*256)<<24)+(int(color[1]*256)<<16)+(int(color[2]*256)<<8)+0xFF
            else:
                if q.state == 0:
                    color = 0xFFFFFFFF
                else:
                    color = 0x000000FF
            pygame.draw.polygon(screen, color, cs, 0)
        # draw edges
        for q in quads:
            if len(q.corners)==3:
                exit(1)
            cs = [(c.real,c.imag) for c in q.corners]
            width = 3
            pygame.draw.lines(screen, 0x7F7F7FFF, 1, cs, int(width))
        now = time.clock()
        speed = (tick-timed_tick)/(now-timed_tick_time)
        timed_tick_time = now
        timed_tick = tick
        screen.blit(screen, (0, 0))
        label = myfont.render("%4.2f/s"%speed, 1, (255,255,255))
        screen.fill(pygame.Color("black"), (0, 0, 110, 15))
        screen.blit(label, (0, 0))        
        pygame.display.update()

    if __debug__:
        break

    if paused and not step and render_countdown == 0:
        time.sleep(0.05)
        continue

    # screenshot
    if render_countdown > 0:
        filename = "oscillator_p%03d_%08d_%03d.png" % (found_oscillator, guidish, found_oscillator - render_countdown)
        pygame.image.save(screen,filename)
        render_countdown -= 1
        if render_countdown == 0:
            guidish = random.randint(0,99999999)
            found_oscillator = 0
            randomize = True
            continue


    # calculate new cell states based on the Game of Life rules
    for q in quads:
        a = sum([n.state for n in q.all_neighbors])
        q.new_state = q.state
        # dead cells with three neighbors spawn
        if q.state == 0 and a == 3:
            q.new_state = 1
        # live cells only survive with two or three neighbors
        elif a < 2 or a > 3:
            q.new_state = 0

    # update cell states
    for q in quads:
        q.state = q.new_state

    this_state = [q.state for q in quads]

    # don't bother checking
    if render_countdown == 0:
        # compare this board state to the last N-1 states
        for i in range(1,history_length):
            if quad_history[(quad_pointer-i)%history_length] == this_state:
                if i == 1 or i == 2: # stalled board or p2 oscillator (boring)
                    randomize = True
                    break
                #TODO: give up if the "oscillator" includes border cells
                #TODO: identify cases of two oprime oscillators overlapping
                elif i > 2:
                    found_oscillator = i
                    break # don't keep looking

        # remember this board state
        quad_history[quad_pointer] = this_state
        quad_pointer = (quad_pointer+1)%history_length

if __debug__:
    filename = "penrose.png"
    pygame.image.save(screen,filename)
    time.sleep(1)

2
Ich habe sofort darüber nachgedacht, weil ich diesen Beitrag gelesen habe: newscientist.com/article/… mit dem ich ohne weiteres 50 Punkte bekommen kann. Können Sie von dieser Idee ausgehen? EDIT: Ahh, ich habe gerade festgestellt, dass wir die ursprünglichen Game of Life-Regeln anwenden müssen.
Nur die Hälfte des

49

C ++ mit OpenGL (+17)

Also habe ich 3-Isohedral Convex Pentagon Grid ausprobiert. Funktioniert für mich;) Es gelten die Standard-Spielregeln für das Leben, außer dass das Raster nicht unendlich ist - es gibt Randzellen außerhalb des Bildes. 30% der Zellen sind anfänglich am Leben.

So sieht das Raster aus:

Bildbeschreibung hier eingeben

Die Live-Version:

Blaue Zellen leben, weiße sind tot. Rote Blutkörperchen starben gerade, grüne waren gerade geboren. Beachten Sie, dass die Artefakte im Bild das Ergebnis der GIF-Komprimierung sind. SO mag keine 10-MB-GIFs :(.

Bildbeschreibung hier eingeben

Stillleben: (+2)

Bildbeschreibung hier eingeben

Oszillatoren T = 2, T = 3, T = 12: (+9)

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Oszillatoren T = 6, T = 7: (+6)

Bildbeschreibung hier eingeben

Es gibt viel mehr verschiedene Oszillatoren ... Aber es scheint, dass das Gitter nicht regelmäßig genug für ein Schiff ist ...

Das ist nichts (keine Punkte), aber ich mag es:

Bildbeschreibung hier eingeben

Der Code ist ein Durcheinander :) Verwendet einige alte feste OpenGL. Andernfalls werden GLEW, GLFW, GLM und ImageMagick für den GIF-Export verwendet.

/**
 * Tile pattern generation is inspired by the code 
 * on http://www.jaapsch.net/tilings/
 * It saved me a lot of thinkink (and debugging) - thank you, sir!
 */

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <FTGL/ftgl.h>  //debug only
#include <ImageMagick-6/Magick++.h> //gif export
#include "glm/glm.hpp" 

#include <iostream>
#include <array>
#include <vector>
#include <set>
#include <algorithm>
#include <unistd.h>

typedef glm::vec2 Point;
typedef glm::vec3 Color;

struct Tile {
    enum State {ALIVE=0, DEAD, BORN, DIED, SIZE};

    static const int VERTICES = 5;
    static constexpr float SCALE = 0.13f;
    static constexpr std::array<std::array<int, 7>, 18> DESC 
    {{
        {{1, 0,0, 0,0,0, 0}},
        {{0, 1,2, 0,2,1, 0}},
        {{2, 2,3, 0,2,3, 1}},
        {{1, 0,4, 0,0,1, 0}},
        {{0, 1,2, 3,2,1, 0}},
        {{2, 2,3, 3,2,3, 1}},
        {{1, 0,4, 3,0,1, 0}},
        {{0, 1,2, 6,2,1, 0}},
        {{2, 2,3, 6,2,3, 1}},
        {{1, 0,4, 6,0,1, 0}},
        {{0, 1,2, 9,2,1, 0}},
        {{2, 2,3, 9,2,3, 1}},
        {{1, 0,4, 9,0,1, 0}},
        {{0, 1,2,12,2,1, 0}},
        {{2, 2,3,12,2,3, 1}},
        {{1, 0,4,12,0,1, 0}},
        {{0, 1,2,15,2,1, 0}},
        {{2, 2,3,15,2,3, 1}}
    }};

    const int ID;
    std::vector<Point> coords;
    std::set<Tile*> neighbours;
    State state;
    State nextState;
    Color color;

    Tile() : ID(-1), state(DEAD), nextState(DEAD), color(1, 1, 1) {
        const float ln = 0.6f;
        const float h = ln * sqrt(3) / 2.f;
        coords = {
            Point(0.f,      0.f), 
            Point(ln,       0.f), 
            Point(ln*3/2.f,h), 
            Point(ln,       h*4/3.f), 
            Point(ln/2.f,   h)
        };
        for(auto &c : coords) {
            c *= SCALE;
        }
    }

    Tile(const int id, const std::vector<Point> coords_) : 
        ID(id), coords(coords_), state(DEAD), nextState(DEAD), color(1, 1, 1) {}

    bool operator== (const Tile &other) const {
        return ID == other.ID;
    }

    const Point & operator[] (const int i) const {
        return coords[i];
    }
    void updateState() {
        state = nextState;
    }
    /// returns "old" state
    bool isDead() const {
        return state == DEAD || state == DIED;
    }
    /// returns "old" state
    bool isAlive() const {
        return state == ALIVE || state == BORN;
    }

    void translate(const Point &p) {
       for(auto &c : coords) {
           c += p;
       }
    }

    void rotate(const Point &p, const float angle) {
        const float si = sin(angle);
        const float co = cos(angle);
        for(auto &c : coords) {
            Point tmp = c - p;
            c.x = tmp.x * co - tmp.y * si + p.x;
            c.y = tmp.y * co + tmp.x * si + p.y;
        }      
    }

    void mirror(const float y2) {
       for(auto &c : coords) {
          c.y = y2 - (c.y - y2);
       }
    }

};
std::array<std::array<int, 7>, 18> constexpr Tile::DESC;
constexpr float Tile::SCALE;

class Game {
    static const int    CHANCE_TO_LIVE  = 30;       //% of cells initially alive
    static const int    dim             = 4;        //evil grid param

    FTGLPixmapFont &font;
    std::vector<Tile> tiles;
    bool animate; //animate death/birth
    bool debug; //show cell numbers (very slow)
    bool exportGif;     //save gif
    bool run;

public: 
    Game(FTGLPixmapFont& font) : font(font), animate(false), debug(false), exportGif(false), run(false) {
        //create the initial pattern
        std::vector<Tile> init(18);
        for(int i = 0; i < Tile::DESC.size(); ++i) {
            auto &desc = Tile::DESC[i];
            Tile &tile = init[i];
            switch(desc[0]) {   //just to check the grid
                case 0: tile.color = Color(1, 1, 1);break;
                case 1: tile.color = Color(1, 0.7, 0.7);break;
                case 2: tile.color = Color(0.7, 0.7, 1);break;
            }

            if(desc[3] != i) {
                const Tile &tile2 = init[desc[3]];
                tile.translate(tile2[desc[4]] - tile[desc[1]]);
                if(desc[6] != 0) {
                   float angleRad = getAngle(tile[desc[1]], tile[desc[2]]);
                   tile.rotate(tile[desc[1]], -angleRad);
                   tile.mirror(tile[desc[1]].y);
                   angleRad = getAngle(tile[desc[1]], tile2[desc[5]]);
                   tile.rotate(tile[desc[1]], angleRad);
                }
                else {
                   float angleRad = getAngle(tile[desc[1]], tile[desc[2]], tile2[desc[5]]);
                   tile.rotate(tile[desc[1]], angleRad);
                }
            }
        }

        const float offsets[4] {
            init[2][8].x - init[8][9].x,
            init[2][10].y - init[8][11].y,
            init[8][12].x - init[14][13].x,
            init[8][14].y - init[14][15].y 
        };

        // create all the tiles
        for(int dx = -dim; dx <= dim; ++dx) { //fuck bounding box, let's hardcode it
            for(int dy = -dim; dy <= dim; ++dy) {

                for(auto &tile : init) {
                    std::vector<Point> vert;
                    for(auto &p : tile.coords) {
                        float ax = dx * offsets[0] + dy * offsets[2];
                        float ay = dx * offsets[1] + dy * offsets[3];
                        vert.push_back(Point(p.x + ax, p.y + ay));
                    }
                    tiles.push_back(Tile(tiles.size(), vert));
                    tiles.back().color = tile.color;
                    tiles.back().state = tile.state;
                }
            }
        }

        //stupid bruteforce solution, but who's got time to think..
        for(Tile &tile : tiles) { //find neighbours for each cell 
            for(Tile &t : tiles) {
                if(tile == t) continue;
                for(Point &p : t.coords) {
                    for(Point &pt : tile.coords) {
                        if(glm::distance(p, pt) < 0.01 ) {
                            tile.neighbours.insert(&t);
                            break;
                        }
                    }
                }
            }
            assert(tile.neighbours.size() <= 9);
        }   
    }

    void init() {
        for(auto &t : tiles) {
            if(rand() % 100 < CHANCE_TO_LIVE) {
                t.state = Tile::BORN;
            }
            else {
                t.state = Tile::DEAD;           
            }
        }
    }

    void update() {
        for(auto &tile: tiles) {
            //check colors
            switch(tile.state) {
                case Tile::BORN:    //animate birth
                    tile.color.g -= 0.05;
                    tile.color.b += 0.05;
                    if(tile.color.b > 0.9) {
                        tile.state = Tile::ALIVE;
                    }
                    break;
                case Tile::DIED:    //animate death
                    tile.color += 0.05;
                    if(tile.color.g > 0.9) {
                        tile.state = Tile::DEAD;
                    }
                    break;
            }
            //fix colors after animation
            switch(tile.state) {
                case Tile::ALIVE:
                    tile.color = Color(0, 0, 1);
                    break;
                case Tile::DEAD:
                    tile.color = Color(1, 1, 1);
                    break;
            }

            //draw polygons
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            glBegin(GL_POLYGON);
            glColor3f(tile.color.r, tile.color.g, tile.color.b);
            for(auto &pt : tile.coords) {
                glVertex2f(pt.x, pt.y); //haha so oldschool!
            }
            glEnd();
        }

        //draw grid
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glColor3f(0, 0, 0);
        for(auto &tile : tiles) {
            glBegin(GL_POLYGON);
            Point c;    //centroid of tile
            for(auto &pt : tile.coords) {
                glVertex2f(pt.x, pt.y);
                c += pt;
            }
            glEnd();
            if(debug) {
                c /= (float) Tile::VERTICES;
                glRasterPos2f(c.x - 0.025, c.y - 0.01);
                font.Render(std::to_string(tile.ID).c_str()); // 
            }
        }

        if(!run) {
            return;
        }

        //compute new generation
        for(Tile &tile: tiles) {

            tile.nextState = tile.state; //initialize next state
            int c = 0;
            for(auto *n : tile.neighbours) {
                if(n->isAlive()) c++;
            }
            switch(c) {
                case 2:
                    break;
                case 3:
                    if(tile.isDead()) {
                        tile.nextState = animate ? Tile::BORN : Tile::ALIVE;
                        tile.color = Color(0, 1, 0);
                    }
                    break;
                default:
                    if(tile.isAlive()) {
                        tile.nextState = animate ? Tile::DIED : Tile::DEAD;
                        tile.color = Color(1, 0, 0);
                    }
                    break;
            }
        }
        //switch state to new
        for(Tile &tile: tiles) {
            tile.updateState();
        }
    }

    void stop() {run = false;}
    void switchRun() {run = !run;}
    bool isRun() {return run;}
    void switchAnim() {animate = !animate;}
    bool isAnim() {return animate;}
    void switchExportGif() {exportGif = !exportGif;}
    bool isExportGif() {return exportGif;}
    void switchDebug() {debug = !debug;}
    bool isDebug() const {return debug;}
 private:
    static float getAngle(const Point &p0, const Point &p1, Point const &p2) {
       return atan2(p2.y - p0.y, p2.x - p0.x) - atan2(p1.y - p0.y, p1.x - p0.x);
    }

    static float getAngle(const Point &p0, const Point &p1) {
       return atan2(p1.y - p0.y, p1.x - p0.x);
    }
};

class Controlls {
    Game *game;
    std::vector<Magick::Image> *gif;
    Controlls() : game(nullptr), gif(nullptr) {}
public:
    static Controlls& getInstance() {
        static Controlls instance;
        return instance;
    }

    static void keyboardAction(GLFWwindow* window, int key, int scancode, int action, int mods) {
        getInstance().keyboardActionImpl(key, action);
    }

    void setGame(Game *game) {
        this->game = game;
    }
    void setGif(std::vector<Magick::Image> *gif) {
        this->gif = gif;
    }
private:    
    void keyboardActionImpl(int key, int action) {
        if(!game || action == GLFW_RELEASE) {
            return;
        }
        switch (key) {
            case 'R':
                game->stop();
                game->init();
                if(gif) gif->clear();
                break;
            case GLFW_KEY_SPACE:
                game->switchRun();
                break;
            case 'A':
                game->switchAnim();
                break;
            case 'D':
                game->switchDebug();
                break;
                break;
            case 'G':
                game->switchExportGif();
                break;
        };
    }
};

int main(int argc, char** argv) {
    const int width         = 620;      //window size
    const int height        = 620;
    const std::string window_title  ("Game of life!");
    const std::string font_file     ("/usr/share/fonts/truetype/arial.ttf");
    const std::string gif_file      ("./gol.gif");

    if(!glfwInit()) return 1;

    GLFWwindow* window = glfwCreateWindow(width, height, window_title.c_str(), NULL, NULL);
    glfwSetWindowPos(window, 100, 100);
    glfwMakeContextCurrent(window);

    GLuint err = glewInit();
    if (err != GLEW_OK) return 2;

    FTGLPixmapFont font(font_file.c_str());
    if(font.Error()) return 3;
    font.FaceSize(8);

    std::vector<Magick::Image> gif; //gif export
    std::vector<GLfloat> pixels(3 * width * height);

    Game gol(font);
    gol.init();
    Controlls &controlls = Controlls::getInstance();
    controlls.setGame(&gol);
    controlls.setGif(&gif);

    glfwSetKeyCallback(window, Controlls::keyboardAction);

    glClearColor(1.f, 1.f, 1.f, 0);
    while(!glfwWindowShouldClose(window) && !glfwGetKey(window, GLFW_KEY_ESCAPE)) {
        glClear(GL_COLOR_BUFFER_BIT);

        gol.update();

        //add layer to gif
        if(gol.isExportGif()) {
            glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, &pixels[0]);
            Magick::Image image(width, height, "RGB", Magick::FloatPixel, &pixels[0]);
            image.animationDelay(50);
            gif.push_back(image);
        }

        std::string info = "ANIMATE (A): ";
        info += gol.isAnim() ? "ON " : "OFF";
        info += " | DEBUG (D): ";
        info += gol.isDebug() ? "ON " : "OFF";
        info += " | EXPORT GIF (G): ";
        info += gol.isExportGif() ? "ON " : "OFF";
        info += gol.isRun() ? " | STOP (SPACE)" : " | START (SPACE)";
        font.FaceSize(10);
        glRasterPos2f(-.95f, -.99f);
        font.Render(info.c_str());

        if(gol.isDebug()) font.FaceSize(8);
        if(!gol.isDebug()) usleep(50000); //not so fast please!

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    //save gif to file
    if(gol.isExportGif()) {
        std::cout << "saving " << gif.size() << " frames to gol.gif\n";
        gif.back().write("./last.png");
        Magick::writeImages(gif.begin(), gif.end(), gif_file);
    }

    glfwTerminate();
    return 0;
}

1
Sehr cool! Aber was meinten Sie damit, dass 23% der Zellen anfänglich am Leben sind? Es tut mir leid, wenn ich Sie nur missverstehe, aber eine der Regeln lautet: By default the background should be all dead tiles.(Sie können das Raster nicht mit einer unbegrenzten Anzahl von Live-Kacheln ausstatten.)
Calvins Hobbys

1
@ Calvin'sHobbies: Ich bin nicht sicher, ob ich folge .. Sie müssen eine Art Anfangskonfiguration festlegen ... Wenn alle Zellen am Anfang tot sind, würde nie etwas passieren.
Jaa-c

1
Na sicher. Ich beziehe mich nur auf einen Fall, in dem zum Beispiel ein Raumschiff davon abhängt, dass eine vorinitialisierte unendliche Reihe von Kacheln daneben funktioniert. Ich sehe jetzt, dass Sie gerade 23% Ihrer Kacheln für Ihre zufällige Animation initialisieren, also keine Sorge, hier gibt es kein Problem.
Calvins Hobbys

2
Ihr großer Oszillator ist jetzt Punkte wert :)
Calvins Hobbys

1
@ Calvin'sHobbies: Leider habe ich gerade einen Fehler in meinem Code gefunden (ich habe Zustände einer neuen und einer alten Generation gemischt), sodass der Oszillator nicht mehr gültig ist: / Jetzt behoben.
Jaa-c

38

Gehen, ? Punkte

Anstatt mich auf eine bestimmte Kachel festzulegen, habe ich ein Programm geschrieben, das ein GIF oder PNG einer Kachel annimmt und das Leben darauf laufen lässt. Das GIF / PNG muss für alle Kacheln eine einzige Farbe haben.

package main

import (
    "flag"
    "image"
    "image/color"
    "image/gif"
    "image/png"
    "math/rand"
    "os"
    "strings"
)

func main() {
    flag.Parse()
    filename := flag.Args()[0]
    r, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    var i image.Image
    if strings.HasSuffix(filename, ".gif") {
        i, err = gif.Decode(r)
        if err != nil {
            panic(err)
        }
    }
    if strings.HasSuffix(filename, ".png") {
        i, err = png.Decode(r)
        if err != nil {
            panic(err)
        }
    }

    // find background color
    back := background(i)

    // find connected regions
    n, m := regions(i, back)

    // find edges between regions
    edges := graph(i, m)

    // run life on the tiling
    life(i, n, m, edges)
}

// Find the most-common occurring color.
// This is the "background" color.
func background(i image.Image) color.Color {
    hist := map[color.Color]int{}
    b := i.Bounds()
    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            hist[i.At(x, y)]++
        }
    }
    maxn := 0
    var maxc color.Color
    for c, n := range hist {
        if n > maxn {
            maxn = n
            maxc = c
        }
    }
    return maxc
}

// find connected regions.  Returns # of regions and a map from pixels to their region numbers.
func regions(i image.Image, back color.Color) (int, map[image.Point]int) {

    // m maps each background point to a region #
    m := map[image.Point]int{}

    // number regions consecutively
    id := 0

    b := i.Bounds()
    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            if i.At(x, y) != back {
                continue
            }
            p := image.Point{x, y}
            if _, ok := m[p]; ok {
                continue // already in a region
            }
            q := []image.Point{p}
            m[p] = id
            k := 0
            for k < len(q) {
                z := q[k]
                k++
                for _, n := range [4]image.Point{{z.X - 1, z.Y}, {z.X + 1, z.Y}, {z.X, z.Y - 1}, {z.X, z.Y + 1}} {
                    if !n.In(b) || i.At(n.X, n.Y) != back {
                        continue
                    }
                    if _, ok := m[n]; ok {
                        continue
                    }
                    m[n] = id
                    q = append(q, n)

                }
            }
            if len(q) < 10 {
                // really tiny region - probably junk in input data
                for _, n := range q {
                    delete(m, n)
                }
                continue
            }
            id++
        }
    }
    return id, m
}

// edge between two regions.  r < s.
type edge struct {
    r, s int
}

// returns a set of edges between regions.
func graph(i image.Image, m map[image.Point]int) map[edge]struct{} {
    // delta = max allowed spacing between adjacent regions
    const delta = 6
    e := map[edge]struct{}{}
    for p, r := range m {
        for dx := -delta; dx <= delta; dx++ {
            for dy := -delta; dy <= delta; dy++ {
                n := image.Point{p.X + dx, p.Y + dy}
                if _, ok := m[n]; !ok {
                    continue
                }
                if m[n] > r {
                    e[edge{r, m[n]}] = struct{}{}
                }
            }
        }
    }
    return e
}

// run life engine
// i = image
// n = # of regions
// m = map from points to their region #
// edges = set of edges between regions
func life(i image.Image, n int, m map[image.Point]int, edges map[edge]struct{}) {
    b := i.Bounds()
    live := make([]bool, n)
    nextlive := make([]bool, n)
    palette := []color.Color{color.RGBA{0, 0, 0, 255}, color.RGBA{128, 0, 0, 255}, color.RGBA{255, 255, 128, 255}} // lines, on, off
    var frames []*image.Paletted
    var delays []int

    // pick random starting lives
    for j := 0; j < n; j++ {
        if rand.Int()%2 == 0 {
            live[j] = true
            nextlive[j] = true
        }
    }
    for round := 0; round < 100; round++ {
        // count live neighbors
        neighbors := make([]int, n)
        for e := range edges {
            if live[e.r] {
                neighbors[e.s]++
            }
            if live[e.s] {
                neighbors[e.r]++
            }
        }

        for j := 0; j < n; j++ {
            nextlive[j] = neighbors[j] == 3 || (live[j] && neighbors[j] == 2)
        }

        // add a frame
        frame := image.NewPaletted(b, palette)
        for y := b.Min.Y; y < b.Max.Y; y++ {
            for x := b.Min.X; x < b.Max.X; x++ {
                frame.SetColorIndex(x, y, 0)
            }
        }
        for p, r := range m {
            if live[r] {
                frame.SetColorIndex(p.X, p.Y, 1)
            } else {
                frame.SetColorIndex(p.X, p.Y, 2)
            }
        }
        frames = append(frames, frame)
        delays = append(delays, 30)

        live, nextlive = nextlive, live
    }

    // write animated gif of result
    w, err := os.Create("animated.gif")
    if err != nil {
        panic(err)
    }
    gif.EncodeAll(w, &gif.GIF{Image: frames, Delay: delays, LoopCount: 100})
    w.Close()
}

Dann bin ich einfach ins Internet gegangen, habe ein paar lustige Bilder zusammengestellt und das Programm darauf ausgeführt.

go run life.go penrose1.go

Es wird eine Datei mit dem Namen "animated.gif" erstellt, die eine 100-stufige Lebensdauersimulation der angegebenen Kacheln enthält.

Standardleben:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Penrose Fliesen:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Oben hat man einen Oszillator der Periode 12.

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Oben hat man einen Oszillator der Periode 3.


7
Sehr, sehr coole Idee, aber ich denke, Ihr Algorithmus handhabt Ecknachbarn nicht richtig, zumindest in Ihrem letzten Beispiel. Wenn der Oszillator der Periode 3 3 Kacheln hat, die nahe beieinander liegen, sollten die anderen 9 Kacheln an diesem Scheitelpunkt lebendig werden, da sie alle den 3 lebenden Kacheln benachbart sind. Die blauen Kacheln finden Sie unter i.stack.imgur.com/veUA1.png .
Calvins Hobbys

33

Java - 11 (ish) Punkte

Kommt mit voll (meistens) funktionierender interaktiver Umgebung!

BEARBEITEN

Tödlicher Fehler entdeckt :(

Der Weg der lebendigen Regionen wird durch die Fläche begrenzt, in der sie ursprünglich geformt wurden. Um die quadratische Doppel-Fünfeck-Barriere zu passieren, muss auf der anderen Seite eine vorab schattierte Region vorhanden sein. Dies liegt daran, dass jede Form darunter nur 2 der darüber liegenden Bereiche berührt. Das heißt, keine Raumschiffe oder Erweiterungen, die die Möglichkeiten einschränken. Ich werde es mit einem anderen Muster versuchen.

ABER!!! wenn du es noch probieren willst ... probiere es hier .

Oszillator

Bildbeschreibung hier eingeben

Ich weiß nicht, wie ich das nennen soll - ein weiterer Oszillator

Bildbeschreibung hier eingeben

Dieser sieht ein bisschen aus wie ein Ninja-Star - Stillleben

Bildbeschreibung hier eingeben

Dieser sieht aus wie ein Fliegenstillleben

Bildbeschreibung hier eingeben

ein weiterer Oszillator

Bildbeschreibung hier eingeben

BEARBEITEN

ein weiterer Oszillator gefunden. Ich nenne diesen den Adler.

Bildbeschreibung hier eingeben

Hallo! ein weiterer Oszillator! (Periode 4) Die Windmühle.

Bildbeschreibung hier eingeben

A 2 Punkt eins.

Bildbeschreibung hier eingeben

Es scheint eine Struktur zu geben, die das Äußere vom Inneren isoliert. Dieses (und das vorherige Beispiel) verwendet es. Das Einzige, was die Box zerbrechen kann, ist, wenn eines der Randfelder am Anfang (bis jetzt) ​​noch am Leben ist. Dies ist übrigens die Blinker - Periode 2.

Bildbeschreibung hier eingeben

Ich habe dies in Eclipse eingebaut und es gibt mehrere Dateien. Hier sind sie.

Hauptklasse -

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    Canvas canvas = new Canvas();
    JFrame frame = new JFrame();
    Timer timer;
    ShapeInfo info;
    int[][][] history;
    public Main() {
        JPanel panel = new JPanel();
        panel.setMinimumSize(new Dimension(500,500));
        panel.setLayout(new GridBagLayout());

        frame.setMinimumSize(new Dimension(500,500));
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //frame.setResizable(false);
        canvas.setMinimumSize(new Dimension(200,200));
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 2;
        c.fill = GridBagConstraints.BOTH;
        panel.add(canvas,c);

        JButton startButton = new JButton();
        startButton.setText("click to start");
        startButton.setMaximumSize(new Dimension(100,50));
        GridBagConstraints g = new GridBagConstraints();
        g.gridx =0;
        g.gridy = 0;
        g.weightx = 1;
        panel.add(startButton,g);

        JButton restartButton = new JButton();
        restartButton.setText("revert");
        GridBagConstraints b = new GridBagConstraints();
        b.gridx = 0;
        b.gridy = 9;
        panel.add(restartButton,b);

        JButton clearButton = new JButton();
        clearButton.setText("Clear");
        GridBagConstraints grid = new GridBagConstraints();
        grid.gridx = 1;
        grid.gridy = 0;
        panel.add(clearButton,grid);

        clearButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                info = new ShapeInfo(canvas.squaresWide,canvas.squaresHigh);
                restart();
            }
        });

        final JTextField scaleFactor = new JTextField();
        scaleFactor.setText("5");
        GridBagConstraints gh = new GridBagConstraints();
        gh.gridx  = 0;
        gh.gridy = 1;
        panel.add(scaleFactor,gh);
        scaleFactor.getDocument().addDocumentListener(new DocumentListener(){

            @Override
            public void changedUpdate(DocumentEvent arg0) {
                doSomething();
            }

            @Override
            public void insertUpdate(DocumentEvent arg0) {
                doSomething();
            }

            @Override
            public void removeUpdate(DocumentEvent arg0) {
                doSomething();
            }
            public void doSomething(){
                try{
                canvas.size = Integer.valueOf(scaleFactor.getText());
                canvas.draw(info.allShapes);
                }
                catch(Exception e){}
            }

        });
        timer = new Timer(1000, listener);
        frame.pack();
        frame.setVisible(true);
        info = new ShapeInfo(canvas.squaresWide, canvas.squaresHigh);
        info.width = canvas.squaresWide;
        info.height = canvas.squaresHigh;
        history = cloneArray(info.allShapes);
        //history[8][11][1] = 1;
        canvas.draw(info.allShapes);
        restartButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if(timer.isRunning() == true){
                    info.allShapes = cloneArray(history);
                    restart();
                }
            }
        });
        canvas.addMouseListener(new MouseListener(){
            @Override
            public void mouseClicked(MouseEvent e) {
                int x = e.getLocationOnScreen().x - canvas.getLocationOnScreen().x;
                int y = e.getLocationOnScreen().y - canvas.getLocationOnScreen().y;
                Point location = new Point(x,y);
                for(PolygonInfo p:canvas.polygons){
                    if(p.polygon.contains(location)){
                        if(info.allShapes[p.x][p.y][p.position-1] == 1){
                            info.allShapes[p.x][p.y][p.position-1] = 0;
                        }
                        else{
                            info.allShapes[p.x][p.y][p.position-1] = 1;
                        }
                    }
                }
                canvas.draw(info.allShapes);
                history = cloneArray(info.allShapes);
            }
            @Override
            public void mouseEntered(MouseEvent arg0) {
            }
            @Override
            public void mouseExited(MouseEvent arg0) {
            }
            @Override
            public void mousePressed(MouseEvent arg0) { 
            }
            @Override
            public void mouseReleased(MouseEvent arg0) {    
            }
        });
        startButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                timer.start();
            }
        });
    }
    public int[][][] cloneArray(int[][][] array){
        int[][][] newArray = new int[array.length][array[0].length][array[0][0].length];
        for(int x = 0;x<array.length;x++){
            int[][] subArray = array[x];
            for(int y = 0; y < subArray.length;y++){
                int subSubArray[] = subArray[y];
                newArray[x][y] = subSubArray.clone();
            }
        }
        return newArray;
    }
    public void restart(){
        timer.stop();
        canvas.draw(info.allShapes);
    }
    public void setUp(){
        int[] boxes = new int[]{2,3,4,6,7,8};
        for(int box:boxes){
            info.allShapes[8][12][box-1] = 1;
            info.allShapes[9][13][box-1] = 1;
            info.allShapes[8][14][box-1] = 1;
            info.allShapes[9][15][box-1] = 1;
        }
    }
    public void update() {
        ArrayList<Coordinate> dieList = new ArrayList<Coordinate>();
        ArrayList<Coordinate> appearList = new ArrayList<Coordinate>();
        for (int x = 0; x < canvas.squaresWide; x++) {
            for (int y = 0; y < canvas.squaresHigh; y++) {
                for(int position = 0;position <9;position++){
                    int alive = info.allShapes[x][y][position];
                    int touching = info.shapesTouching(x, y, position+1);
                    if(touching!=0){
                    }
                    if(alive == 1){
                        if(touching < 2 || touching > 3){
                            //cell dies
                            dieList.add(new Coordinate(x,y,position));
                        }
                    }
                    else{
                        if(touching == 3){
                            //cell appears
                            appearList.add(new Coordinate(x,y,position));
                        }
                    }
                }
            }
        }
        for(Coordinate die:dieList){
            info.allShapes[die.x][die.y][die.position] = 0;
        }
        for(Coordinate live:appearList){
            info.allShapes[live.x][live.y][live.position] = 1;
        }
    }
    boolean firstDraw = true;
    int ticks = 0;
    ActionListener listener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            canvas.draw(info.allShapes);
            if(ticks !=0){
            update();
            }
            ticks++;
        }
    };
}

Leinwandklasse -

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.util.ArrayList;

import javax.swing.JPanel;

public class Canvas extends JPanel {
    private static final long serialVersionUID = 1L;

    public int squaresWide = 30;
    public int squaresHigh = 30;
    public int size = 4;
    ArrayList<PolygonInfo> polygons = new ArrayList<PolygonInfo>();
    boolean drawTessalationOnly = true;
    private int[][][] shapes;

    public void draw(int[][][] shapes2) {
        shapes = shapes2;
        drawTessalationOnly = false;
        this.repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        //System.out.println("drawing");
        polygons.clear();
        super.paintComponent(g);
        g.setColor(Color.black);
        // draw tessellation
        for (int x = 0; x < squaresWide; x++) {
            for (int y = 0; y < squaresHigh; y++) {
                for (int position = 1; position <= 9; position++) {
                    // System.out.println("position = " + position);
                    Polygon p = new Polygon();
                    int points = 0;
                    int[] xc = new int[] {};
                    int[] yc = new int[] {};
                    if (position == 1) {
                        xc = new int[] { 0, -2, 0, 2 };
                        yc = new int[] { 2, 0, -2, 0 };
                        points = 4;
                    }
                    if (position == 2) {
                        xc = new int[] { 2, 6, 7, 4, 1 };
                        yc = new int[] { 0, 0, 1, 2, 1 };
                        points = 5;
                    }
                    if (position == 3) {
                        xc = new int[] { 1, 4, 4, 2 };
                        yc = new int[] { 1, 2, 4, 4 };
                        points = 4;
                    }
                    if (position == 4) {
                        xc = new int[] { 4, 4, 7, 6 };
                        yc = new int[] { 4, 2, 1, 4 };
                        points = 4;
                    }
                    if (position == 5) {
                        xc = new int[] { 1, 2, 1, 0, 0 };
                        yc = new int[] { 1, 4, 7, 6, 2 };
                        points = 5;
                    }
                    if (position == 6) {
                        xc = new int[] { 7, 8, 8, 7, 6 };
                        yc = new int[] { 1, 2, 6, 7, 4 };
                        points = 5;
                    }
                    if (position == 7) {
                        xc = new int[] { 4, 2, 1, 4 };
                        yc = new int[] { 4, 4, 7, 6 };
                        points = 4;
                    }
                    if (position == 8) {
                        xc = new int[] { 4, 6, 7, 4 };
                        yc = new int[] { 4, 4, 7, 6 };
                        points = 4;
                    }
                    if (position == 9) {
                        xc = new int[] { 4, 7, 6, 2, 1 };
                        yc = new int[] { 6, 7, 8, 8, 7 };
                        points = 5;
                    }
                    int[] finalX = new int[xc.length];
                    int[] finalY = new int[yc.length];
                    for (int i = 0; i < xc.length; i++) {
                        int xCoord = xc[i];
                        xCoord = (xCoord + (8 * x)) * size;
                        finalX[i] = xCoord;
                    }
                    for (int i = 0; i < yc.length; i++) {
                        int yCoord = yc[i];
                        yCoord = (yCoord + (8 * y)) * size;
                        finalY[i] = yCoord;
                    }
                    p.xpoints = finalX;
                    p.ypoints = finalY;
                    p.npoints = points;
                    polygons.add(new PolygonInfo(p,x,y,position));
                    // for(int i = 0;i<p.npoints;i++){
                    // / System.out.println("(" + p.xpoints[i] + "," +
                    // p.ypoints[i] + ")");
                    // }
                    if (drawTessalationOnly == false) {
                        if (shapes[x][y][position - 1] == 1) {
                            g.fillPolygon(p);
                        } else {
                            g.drawPolygon(p);
                        }
                    } else {
                        g.drawPolygon(p);
                    }
                }

            }
        }
    }
}

ShapeInfo-Klasse -

public class ShapeInfo {
    int[][][] allShapes; //first 2 dimensions are coordinates of large square, last is boolean - if shaded
    int width = 20;
    int height = 20;
    public ShapeInfo(int width,int height){
        allShapes = new int[width][height][16];
        for(int[][] i:allShapes){
            for(int[] h:i){
                for(int g:h){
                    g=0;
                }
            }
        }
    }
    public int shapesTouching(int x,int y,int position){
        int t = 0;
        if(x>0 && y >0 && x < width-1 && y < height-1){
        if(position == 1){
            if(allShapes[x][y][2-1] == 1){t++;}
            if(allShapes[x][y][5-1] == 1){t++;}
            if(allShapes[x-1][y][6-1] == 1){t++;}
            if(allShapes[x-1][y][2-1] == 1){t++;}
            if(allShapes[x][y-1][5-1] == 1){t++;}
            if(allShapes[x][y-1][9-1] == 1){t++;}
            if(allShapes[x-1][y-1][9-1] == 1){t++;}
            if(allShapes[x-1][y-1][6-1] == 1){t++;}
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x-1][y][4-1] == 1){t++;}
            if(allShapes[x][y-1][7-1] == 1){t++;}
            if(allShapes[x-1][y-1][8-1] == 1){t++;}
        }
        if(position == 2){
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x][y][4-1] == 1){t++;}
            if(allShapes[x][y][1-1] == 1){t++;}
            if(allShapes[x][y-1][9-1] == 1){t++;}
            if(allShapes[x+1][y][1-1] == 1){t++;}
            if(allShapes[x][y][6-1] == 1){t++;}
            if(allShapes[x][y][5-1] == 1){t++;}
        }
        if(position == 3){
            if(allShapes[x][y][2-1] == 1){t++;}
            if(allShapes[x][y][5-1] == 1){t++;}
            if(allShapes[x][y][4-1] == 1){t++;}
            if(allShapes[x][y][7-1] == 1){t++;}
            if(allShapes[x][y][1-1] == 1){t++;}
            if(allShapes[x][y][8-1] == 1){t++;}
        }
        if(position == 4){
            if(allShapes[x][y][2-1] == 1){t++;}
            if(allShapes[x][y][6-1] == 1){t++;}
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x][y][8-1] == 1){t++;}
            if(allShapes[x][y][7-1] == 1){t++;}
            if(allShapes[x+1][y][1-1] == 1){t++;}
        }
        if(position == 5){
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x][y][7-1] == 1){t++;}
            if(allShapes[x][y][1-1] == 1){t++;}
            if(allShapes[x][y+1][1-1] == 1){t++;}
            if(allShapes[x-1][y][6-1] == 1){t++;}
            if(allShapes[x][y][2-1] == 1){t++;}
            if(allShapes[x][y][9-1] == 1){t++;}
        }
        if(position == 6){
            if(allShapes[x][y][4-1] == 1){t++;}
            if(allShapes[x][y][8-1] == 1){t++;}
            if(allShapes[x+1][y][1-1] == 1){t++;}
            if(allShapes[x+1][y][5-1] == 1){t++;}
            if(allShapes[x+1][y+1][1-1] == 1){t++;}
            if(allShapes[x][y][2-1] == 1){t++;}
            if(allShapes[x][y][9-1] == 1){t++;}
        }
        if(position == 7){
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x][y][8-1] == 1){t++;}
            if(allShapes[x][y][5-1] == 1){t++;}
            if(allShapes[x][y][9-1] == 1){t++;}
            if(allShapes[x][y][4-1] == 1){t++;}
            if(allShapes[x][y+1][1-1] == 1){t++;}
        }
        if(position == 8){
            if(allShapes[x][y][9-1] == 1){t++;}
            if(allShapes[x][y][6-1] == 1){t++;}
            if(allShapes[x][y][7-1] == 1){t++;}
            if(allShapes[x][y][4-1] == 1){t++;}
            if(allShapes[x][y][3-1] == 1){t++;}
            if(allShapes[x+1][y+1][1-1] == 1){t++;}
        }
        if(position == 9){
            if(allShapes[x][y][7-1] == 1){t++;}
            if(allShapes[x][y][8-1] == 1){t++;}
            if(allShapes[x+1][y+1][1-1] == 1){t++;}
            if(allShapes[x][y+1][2-1] == 1){t++;}
            if(allShapes[x][y+1][1-1] == 1){t++;}
            if(allShapes[x][y][6-1] == 1){t++;}
            if(allShapes[x][y][5-1] == 1){t++;}
        }
        }
        return t;
    }
}

PolygonInfo-Klasse -

import java.awt.Polygon;

public class PolygonInfo {
    public Polygon polygon;
    public int x;
    public int y;
    public int position;
    public PolygonInfo(Polygon p,int X,int Y,int Position){
        x = X;
        y = Y;
        polygon = p;
        position = Position;
    }
}

und schließlich ... Koordinatenklasse

public class Coordinate {
    int x;
    int y;
    int position;
    public Coordinate(int X,int Y, int Position){
        x=X;
        y=Y;
        position = Position;
    }
}

4
Das zweite ist definitiv ein fröhliches kleines Siegel.
Martin Ender

Weiß jemand, wie ich eine JAR-Datei posten würde, damit die Leute (leicht) mit meinem Design experimentieren können?
Stretch Maniac

3
Ich mag den Cursor in der Windmühle.
cjfaure

10
"Windmühle" ist eher wie marschierende Nazi-Ameisen
bebe

1
Der Cursor ist auch im Adler. Das hat mich zuerst verwirrt.
mbomb007

25

Python

Ich platziere mehrere Punkte auf einem Metatil, das dann in regelmäßigen Abständen in einer rechteckigen oder sechseckigen Kachel kopiert wird (die Metatile dürfen sich überlappen). Aus der Menge aller Punkte berechne ich dann das Voronoi-Diagramm, aus dem sich mein Gitter zusammensetzt.

Einige ältere Beispiele

Zufällige Grafik, die Delaunay-Trinagulation wird angezeigt, die auch intern zum Auffinden der Nachbarn verwendet wird

Diagramm des Lebens

Ein periodisches Fliesen, das buchstabiert GoL

Bildbeschreibung hier eingeben

Einige weitere Gitter mit Stillleben

Bildbeschreibung hier eingeben

Für ein solches Gitter gibt es eine große Anzahl von Stillleben mit einer Vielzahl von Größen und einigen kleinen 2-, 3- oder 5-Takt-Oszillatoren, aber ich habe wahrscheinlich aufgrund der Unregelmäßigkeiten des Gitters keine Segelflugzeuge gefunden . Ich denke darüber nach, die Suche nach Lebensformen zu automatisieren, indem ich Zellen auf periodische Schwingungen überprüfe.

import networkx as nx
from scipy.spatial import Delaunay, Voronoi
from scipy.spatial._plotutils import _held_figure, _adjust_bounds
from numpy import *
import matplotlib.pyplot as plt

# copied from scipy.spatial._plotutils
@_held_figure
def voronoi_plot_2d(vor, ax=None):
    for simplex in vor.ridge_vertices:
        simplex = asarray(simplex)
        if all(simplex >= 0):
            ax.plot(vor.vertices[simplex,0], vor.vertices[simplex,1], 'k-')
    center = vor.points.mean(axis=0)  
    _adjust_bounds(ax, vor.points)
    return ax.figure

def maketilegraph(tile, offsetx, offsety, numx, numy, hexa=0):
    # tile: list of (x,y) coordinates
    # hexa=0: rectangular tiling
    # hexa=1: hexagonal tiling
    R = array([offsetx,0])
    U = array([0,offsety]) - hexa*R/2
    points = concatenate( [tile+n*R for n in range(numx)])
    points = concatenate( [points+n*U for n in range(numy)])

    pos = dict(enumerate(points))
    D = Delaunay(points)

    graph = nx.Graph()
    for tri in D.vertices:
        graph.add_cycle(tri)    
    return graph, pos, Voronoi(points)

def rule(old_state, Nalive):
    if Nalive<2: old_state = 0
    if Nalive==3: old_state = 1
    if Nalive>3: old_state = 0
    return old_state

def propagate(graph):
    for n in graph: # compute the new state
        Nalive = sum([graph.node[m]['alive'] for m in graph.neighbors(n)])
        graph.node[n]['alive_temp'] = rule(graph.node[n]['alive'], Nalive)
    for n in graph: # apply the new state
        graph.node[n]['alive'] = graph.node[n]['alive_temp']

def drawgraph(graph):
    nx.draw_networkx_nodes(graph,pos,
                        nodelist=[n for n in graph if graph.node[n]['alive']],
                        node_color='k', node_size=150)
    # nx.draw_networkx_nodes(graph,pos,
                        # nodelist=[n for n in graph if not graph.node[n]['alive']],
                        # node_color='y', node_size=25, alpha=0.5)
    # nx.draw_networkx_edges(graph,pos, width=1, alpha=0.2, edge_color='b')

##################
# Lets get started
p_alive = 0.4   # initial fill ratio

#tile = random.random((6,2))
a = [.3*exp(2j*pi*n/5) for n in range(5)] +[.5+.5j, 0]
tile = array(zip(real(a), imag(a)))
grid, pos, vor = maketilegraph(tile, 1.,1.,8,8, hexa=1)

for n in grid: # initial fill
    grid.node[n]['alive'] = random.random() < p_alive #random fill
    # grid.node[n]['alive'] = n%5==0 or n%3==0    # periodic fill

for i in range(45):propagate(grid) # run until convergence

for i in range(7):
    print i
    voronoi_plot_2d(vor)
    drawgraph(grid)
    plt.axis('off')
    plt.savefig('GoL %.3d.png'%i, bbox_inches='tight')
    plt.close()
    propagate(grid)

3
Interessante Idee, aber eine zufällige Kachelung hätte nicht endlich viele Prototile. Für Ihre periodischen Kacheln müssen Sie eine Anordnung auswählen und explizit zeigen, wie alle Oszillatoren und Materialien hergestellt werden können.
Calvins Hobbys

Es wäre cool, wenn die Grafik auf der Weltkarte basiert (z. B. Städte)
Ming-Tang

@SHiNKiROU Tolle Idee, ich erinnere mich, dass ich ein Python-Paket für die Arbeit mit geografischen Karten gesehen habe, also mache ich das, vor allem, weil ich mich nicht auf ein einziges Raster festlegen kann.
DenDenDo

Ich denke, Sie behandeln Zellen nur dann als benachbart, wenn sie sich eine Kante teilen, während ein gemeinsamer Scheitelpunkt ausreichen sollte, obwohl der Verbindungsgraph in solchen Fällen möglicherweise nicht planar ist. Z.B. 5 Zellen, die sich einen Scheitelpunkt teilen, bilden einen K_5 im Verbindungsgraphen.
Beispiel

In der Tat, manchmal sind sie durch Scheitelpunkte verbunden, manchmal sind sie keine Zellen + Verknüpfungen. Als ich das Diagramm der Verknüpfungen zum ersten Mal konstruierte, wollte ich sicherstellen, dass es eben ist, dh dass es keine Kreuzungen gibt ein Scheitelpunkt. Glücklicherweise kann dies leicht vermieden werden, indem die Zellen leicht asymmetrisch gemacht werden.
DenDenDo

21

Javascript [25+?]

http://jsfiddle.net/Therm/dqb2h2oc/

Bildbeschreibung hier eingeben

Haus Tessellations! Es gibt zwei Formen: "House" und "Upsidedown House" mit jeweils 7 Nachbarn.

Derzeit habe ich eine Punktzahl von 25.

still life                  : +2
2-stage oscillator "beacon" : +3  (Credit to isaacg)
Spaceship "Toad"            : +10 (Credit to isaacg)
Glider                      : +10 (Credit to Martin Büttner)

Benennen von Rechten für Muster, wenn Sie sie finden: p

Stillleben - Stern
Star

2 Stage Oszillator - "Beacon": Gefunden von isaacg
2stagOscillator

Raumschiff - "Kröte": Gefunden von isaacg
Bildbeschreibung hier eingeben

Segelflugzeug - Unbenannte: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben

Die Geige ist derzeit so eingestellt, dass sie die Welt als Ausgangszustand zufällig bevölkert.

Code:

// An animation similar to Conway's Game of Life, using house-tessellations.
// B2/S23

var world;
var worldnp1;
var intervalTime = 2000;

var canvas = document.getElementById('c');
var context = canvas.getContext('2d');

var x = 32;
var y = 32;

var width = 20; // width of house
var height = 15; // height of house base
var theight = 5; // height of house roof
var deadC = '#3300FF';
var aliveC = '#00CCFF';

function initWorld() {
    world = new Array(x * y);

    /* Still life - box
        world[x/2 * y + y/2 + 1] = 1;
        world[x/2 * y + y/2] = 1;
        world[x/2 * y + y/2 + y] = 1;
        world[x/2 * y + y/2 + y + 1] = 1;
    */

    /* Still life - House
        world[x/2 * y + y/2 - y] = 1;
        world[x/2 * y + y/2 + 1] = 1;
        world[x/2 * y + y/2 - 1] = 1;
        world[x/2 * y + y/2 + y] = 1;
        world[x/2 * y + y/2 + y+1] = 1;
    */

    /* Oscillator on an infinite plane :(
    for(var i=0; i<y; i++) {
        world[y/2 * y + i] = 1 ^ (i%2);
        world[y/2 * y + y + i] = 1 ^ (i%2);
    } */

    // Random state 
    for(var i=0; i<x*y; i++) {
        world[i] = Math.round(Math.random());
    }

    drawGrid();
}

animateWorld = function () {
    computeNP1();
    drawGrid();
};

function computeNP1() {
    worldnp1 = new Array(x * y);
    var buddies;
    for (var i = 0; i < x * y; i++) {
        buddies = getNeighbors(i);
        var aliveBuddies = 0;
        for (var j = 0; j < buddies.length; j++) {
            if (world[buddies[j]]) {
                aliveBuddies++;
            }
        }
        if (world[i]) {
            if (aliveBuddies === 2 || aliveBuddies === 3) {
                worldnp1[i] = 1;
            }
        }
        else {
            if (aliveBuddies === 3) {
                worldnp1[i] = 1;
            }
        }
    }
    world = worldnp1.slice(0);
}

function drawGrid() {
    var dx = 0;
    var dy = 0;
    var shiftLeft = 0;
    var pointDown = 0;
    for (var i = 0; i < y; i++) {
        // yay XOR
        shiftLeft ^= pointDown;
        pointDown ^= 1;
        if (shiftLeft) {
            dx -= width / 2;
        }
        for (var j = 0; j < x; j++) {
            var c = world[i * y + j] ? aliveC : deadC ;
            draw5gon(dx, dy, pointDown, c);
            outline5gon(dx, dy, pointDown);
            dx += width;
        }
        dx = 0;
        if (pointDown) {
            dy += 2 * height + theight;
        }
    }
}

function getNeighbors(i) {
    neighbors = [];

    // Everybody has a L/R neighbor
    if (i % x !== 0) {
        neighbors.push(i - 1);
    }
    if (i % x != x - 1) {
        neighbors.push(i + 1);
    }

    // Everybody has "U/D" neighbor
    neighbors.push(i - x);
    neighbors.push(i + x);

    // Down facers (R1)
    if (Math.floor(i / x) % 4 === 0) {
        if (i % x !== 0) {
            neighbors.push(i - x - 1);
        }
        if (i % x != x - 1) {
            neighbors.push(i - x + 1);
            neighbors.push(i + x + 1);
        }
    }

    // Up facers (R2)
    else if (Math.floor(i / x) % 4 === 1) {
        if (i % x !== 0) {
            neighbors.push(i - x - 1);
            neighbors.push(i + x - 1);
        }
        if (i % x != x - 1) {
            neighbors.push(i + x + 1);
        }
    }

    // Down facers (R3)
    else if (Math.floor(i / x) % 4 === 2) {
        if (i % x !== 0) {
            neighbors.push(i - x - 1);
            neighbors.push(i + x - 1);
        }
        if (i % x != x - 1) {
            neighbors.push(i - x + 1);
        }
    }

    // Up facers (R4)
    // else if ( Math.floor(i/x) % 4 === 3 )
    else {
        if (i % x !== 0) {
            neighbors.push(i + x - 1);
        }
        if (i % x != x - 1) {
            neighbors.push(i - x + 1);
            neighbors.push(i + x + 1);
        }
    }

    return neighbors.filter(function (val, ind, arr) {
        return (0 <= val && val < x * y);
    });
}

// If pointdown, x,y refer to top left corner
// If not pointdown, x,y refers to lower left corner
function draw5gon(x, y, pointDown, c) {
    if (pointDown) {
        drawRect(x, y, width, height, c);
        drawTriangle(x, y + height, x + width, y + height, x + width / 2, y + height + theight);
    } else {
        drawRect(x, y - height, width, height, c);
        drawTriangle(x, y - height, x + width / 2, y - height - theight, x + width, y - height);
    }
}

function outline5gon(x, y, pointDown) {
    context.beginPath();
    context.moveTo(x, y);
    if (pointDown) {
        context.lineTo(x + width, y);
        context.lineTo(x + width, y + height);
        context.lineTo(x + width / 2, y + height + theight);
        context.lineTo(x, y + height);
    } else {
        context.lineTo(x, y - height);
        context.lineTo(x + width / 2, y - height - theight);
        context.lineTo(x + width, y - height);
        context.lineTo(x + width, y);
    }
    context.lineWidth = 3;
    context.strokeStyle = '#000000';
    context.stroke();
}

function drawRect(x, y, w, h, c) {
    context.fillStyle = c;
    context.fillRect(x, y, w, h);
}

function drawTriangle(x1, y1, x2, y2, x3, y3, c) {
    context.beginPath();
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
    context.lineTo(x3, y3);
    context.fillStyle = c;
    context.fill();
}

$(document).ready(function () {
    initWorld();
    intervalID = window.setInterval(animateWorld, intervalTime);
});

2
Ich habe einen Oszillator gefunden, der auf dem GoL-Beacon basiert. world[x/2 * y + y/2 + 1] = 1; world[x/2 * y + y/2] = 1; world[x/2 * y + y/2 - y] = 1; world[x/2 * y + y/2 - y + 1] = 1; world[x/2 * y + y/2 + 1*y + 2] = 1; world[x/2 * y + y/2 + 1*y + 3] = 1; world[x/2 * y + y/2 + 2*y + 2] = 1; world[x/2 * y + y/2 + 2*y + 3] = 1;
Fügen Sie

@isaacg Bild hinzugefügt und in der Geige enthalten. Willst du es benennen?
Kevin L

Ich würde es das Leuchtfeuer nennen. Es ist dem GoL-Beacon einfach zu ähnlich, um es anders zu nennen.
Isaacg

5
Ich habe ein Segelflugzeug gefunden! Ich würde es gerne die Kröte nennen, weil es in einer Phase wie der Körper einer Kröte aussieht. world[x / 2 * y - y / 2 -1] = 1; world[x / 2 * y - y / 2] = 1; world[x / 2 * y + y / 2] = 1; world[x / 2 * y + y / 2 + 1] = 1; world[x / 2 * y + y / 2 + 1 * y] = 1; world[x / 2 * y + y / 2 + 1 * y + 1] = 1; world[x / 2 * y + y / 2 + 2 * y] = 1; world[x / 2 * y + y / 2 + 2 * y + 1] = 1; world[x / 2 * y + y / 2 + 3 * y] = 1; world[x / 2 * y + y / 2 + 3 * y + 1] = 1; world[x / 2 * y + y / 2 + 4 * y] = 1; world[x / 2 * y + y / 2 + 4 * y-1] = 1;
Isaacg

3
@isaacg wieder gefunden! Und dieses Mal habe ich es gefangen;). Es ist wirklich nur eine Variante von dir, obwohl es zwei weitere nachgestellte lebende Zellen gibt: world[x/2*y - y/2 -1] = 1;world[x/2*y - y/2] = 1;world[x/2*y + y/2 -2] = 1;world[x/2*y + y/2] = 1;world[x/2*y + y/2 +1] = 1;world[x/2*y + y/2 + 1*y] = 1;world[x/2*y + y/2 + 1*y +1] = 1;world[x/2*y + y/2 + 2*y] = 1;world[x/2*y + y/2 + 2*y +1] = 1;world[x/2*y + y/2 + 3*y -2] = 1;world[x/2*y + y/2 + 3*y] = 1;world[x/2*y + y/2 + 3*y +1] = 1;world[x/2*y + y/2 + 4*y] = 1;world[x/2*y + y/2 + 4*y -1] = 1;Ich denke, nach den Regeln ist es dennoch ein eigenständiges Raumschiff.
Martin Ender

20

Javascript [27+?]

http://jsfiddle.net/Therm/5n53auja/

Runde 2! Jetzt mit Sechsecken, Quadraten und Dreiecken. Und Interaktivität

Diese Version unterstützt das Klicken auf Kacheln, um deren Status zu ändern. Hinweis: Ein Teil der Klickverarbeitung ist möglicherweise etwas kompliziert, insbesondere bei niedrigen Werten von s, da Klickereignisse als Ganzzahlen nachverfolgt werden, Berechnungen jedoch mit Gleitkommawerten durchgeführt werden

Bildbeschreibung hier eingeben

Aktuelle Punktzahl - 24

Still life           : +2
Period 2 oscillator  : +3
Period 4 oscillator  : +3
Period 6 oscillator  : +3
Period 10 oscillator : +3
Period 12 oscillator : +3
Spaceship            : +10

Periode 4 Oszillator: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben

Periode 6 Oszillator: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben

Periode 10 Oszillator: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben

Periode 12 Oszillator: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben

Zeitraum 20 Raumschiff: Gefunden von Martin Büttner
Bildbeschreibung hier eingeben


6
Ich habe ein Segelflugzeug / Raumschiff mit der Periode 20 gefunden:world[36].e = 1; world[37].d = 1; world[37].e = 1; world[52].a = 1; world[52].e = 1; world[53].c = 1; world[53].e = 1;
Martin Ender

Eine weitere interessante Ausgangsform für dasselbe Raumschiff ist, world[36].d=1; world[52].a=1; world[52].c=1; world[69].b=1; world[69].a=1; world[70].a=1; world[68].d=1; world[84].a=1; world[84].c=1;dass es nur aus 3 Perioden-2-Oszillatoren besteht.
Martin Ender

Zeit 4 Oszillator, falls es Hilfe gibt:world[53].e=1; world[54].e=1; world[54].c=1; world[54].d=1; world[54].e=1; world[71].e=1; world[71].b=1; world[71].c=1;
Martin Ender

Und am nächsten bin ich zu etwas gekommen, das wie unbegrenztes Wachstum oder ein vertikales Raumschiff aussieht world[87].d=1; world[102].b=1; world[103].a=1; world[103].b=1; world[103].c=1; world[118].b=1; world[119].a=1; world[119].b=1; world[119].c=1; world[119].d=1;. Vielleicht hilft das jemandem, eine Variante zu finden, die funktioniert. Genug fürs Erste ...
Martin Ender

Periode 6 Oszillator: world[68].e=1; world[100].e=1; world[99].b=1; world[100].a=1; world[99].e=1; world[70].e=1; world[102].e=1; world[103].a=1; world[103].b=1; world[103].e=1;Es funktioniert auch mit der halben Größe, wenn es an der Grenze ist.
Martin Ender

16

Kairo fünfeckige Fliesen (+ generisches Gerüst), 17+ Punkte

Diese Kacheln sind überraschend einfach zu zeichnen: Der Schlüssel ist, dass die einzige irrationale Zahl, die für das Zeichnen wichtig sqrt(3)ist, sehr nahe an der rationalen Zahl liegt 7/4, die den zusätzlichen Bonus hat, dass Sie, wenn Sie 1vom Zähler und Nenner subtrahieren 6/3 = 2, dies tun dass die nicht achsgerichteten Linien schön symmetrisch sind.

Wenn Sie Rasterpapier möchten, habe ich eine PostScript-Liste für A4 erstellt. Sie können es auch für andere Papierformate verwenden.

Der Code ist generisch genug, um andere Kacheln zu unterstützen. Die Schnittstelle, die implementiert werden muss, ist:

import java.util.Set;

interface Tiling<Cell> {
    /** Calculates the neighbourhood, which should not include the cell itself. */
    public Set<Cell> neighbours(Cell cell);
    /** Gets an array {xs, ys} of polygon vertices. */
    public int[][] bounds(Cell cell);
    /** Starting cell for random generation. This doesn't need to be consistent. */
    public Cell initialCell();
    /** Allows exclusion of common oscillations in random generation. */
    public boolean isInterestingOscillationPeriod(int period);
    /** Parse command-line input. */
    public Set<Cell> parseCells(String[] data);
}

Dann lautet die Kairo-Kachelung:

import java.awt.Point;
import java.util.*;

/**
 * http://en.wikipedia.org/wiki/Cairo_pentagonal_tiling
 */
class CairoTiling implements Tiling<Point> {
    private static final int[][] SHAPES_X = new int[][] {
        { 0, 4, 11, 11, 4 },
        { 11, 4, 8, 14, 18 },
        { 11, 18, 14, 8, 4 },
        { 22, 18, 11, 11, 18 }
    };
    private static final int[][] SHAPES_Y = new int[][] {
        { 0, 7, 3, -3, -7 },
        { 3, 7, 14, 14, 7 },
        { -3, -7, -14, -14, -7 },
        { 0, -7, -3, 3, 7 }
    };

    public Set<Point> neighbours(Point cell) {
        Set<Point> neighbours = new HashSet<Point>();
        int exclx = (cell.y & 1) == 0 ? -1 : 1;
        int excly = (cell.x & 1) == 0 ? -1 : 1;
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                if (dx == 0 && dy == 0) continue;
                if (dx == exclx && dy == excly) continue;
                neighbours.add(new Point(cell.x + dx, cell.y + dy));
            }
        }

        return neighbours;
    }

    public int[][] bounds(Point cell) {
        int x = cell.x, y = cell.y;

        int[] xs = SHAPES_X[(x & 1) + 2 * (y & 1)].clone();
        int[] ys = SHAPES_Y[(x & 1) + 2 * (y & 1)].clone();
        int xoff = 7 * (x & ~1) + 7 * (y & ~1);
        int yoff = 7 * (x & ~1) - 7 * (y & ~1);

        for (int i = 0; i < 5; i++) {
            xs[i] += xoff;
            ys[i] += yoff;
        }

        return new int[][] { xs, ys };
    }

    public Point initialCell() { return new Point(0, 0); }

    public boolean isInterestingOscillationPeriod(int period) {
        // Period 6 oscillators are extremely common, and period 2 fairly common.
        return period != 2 && period != 6;
    }

    public Set<Point> parseCells(String[] data) {
        if ((data.length & 1) == 1) throw new IllegalArgumentException("Expect pairs of integers");

        Set<Point> cells = new HashSet<Point>();
        for (int i = 0; i < data.length; i += 2) {
            cells.add(new Point(Integer.parseInt(data[i]), Integer.parseInt(data[i + 1])));
        }

        return cells;
    }
}

und der Steuercode ist

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import org.w3c.dom.Node;

/**
 * Implements a Life-like cellular automaton on a generic grid.
 * http://codegolf.stackexchange.com/q/35827/194
 *
 * TODOs:
 *  - Allow a special output format for gliders which moves the bounds at an appropriate speed and doesn't extend the last frame
 *  - Allow option to control number of generations
 */
public class GenericLife {
    private static final Color GRIDCOL = new Color(0x808080);
    private static final Color DEADCOL = new Color(0xffffff);
    private static final Color LIVECOL = new Color(0x0000ff);

    private static final int MARGIN = 15;

    private static void usage() {
        System.out.println("Usage: java GenericLife <tiling> [<output.gif> <cell-data>]");
        System.out.println("For CairoTiling, cell data is pairs of integers");
        System.out.println("For random search, supply just the tiling name");
        System.exit(1);
    }

    // Unchecked warnings due to using reflection to instantation tiling over unknown cell type
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
        if (args.length == 0 || args[0].equals("--help")) usage();

        Tiling tiling = (Tiling)Class.forName(args[0]).newInstance();
        if (args.length > 1) {
            String[] cellData = new String[args.length - 2];
            System.arraycopy(args, 2, cellData, 0, cellData.length);
            Set alive;
            try { alive = tiling.parseCells(cellData); }
            catch (Exception ex) { usage(); return; }

            createAnimatedGif(args[1], tiling, evolve(tiling, alive, 100));
        }
        else search(tiling);
    }

    private static <Cell> void search(Tiling<Cell> tiling) throws IOException {
        while (true) {
            // Build a starting generation within a certain radius of the initial cell.
            // This is a good place to tweak.
            Set<Cell> alive = new HashSet<Cell>();
            double density = Math.random();
            Set<Cell> visited = new HashSet<Cell>();
            Set<Cell> boundary = new HashSet<Cell>();
            boundary.add(tiling.initialCell());
            for (int r = 0; r < 10; r++) {
                visited.addAll(boundary);
                Set<Cell> nextBoundary = new HashSet<Cell>();
                for (Cell cell : boundary) {
                    if (Math.random() < density) alive.add(cell);
                    for (Cell neighbour : tiling.neighbours(cell)) {
                        if (!visited.contains(neighbour)) nextBoundary.add(neighbour);
                    }
                }

                boundary = nextBoundary;
            }

            final int MAX = 1000;
            List<Set<Cell>> gens = evolve(tiling, alive, MAX);
            // Long-lived starting conditions might mean a glider, so are interesting.
            boolean interesting = gens.size() == MAX;
            String desc = "gens-" + MAX;
            if (!interesting) {
                // We hit some oscillator - but was it an interesting one?
                int lastGen = gens.size() - 1;
                gens = evolve(tiling, gens.get(lastGen), gens.size());
                if (gens.size() > 1) {
                    int period = gens.size() - 1;
                    desc = "oscillator-" + period;
                    interesting = tiling.isInterestingOscillationPeriod(period);
                    System.out.println("Oscillation of period " + period);
                }
                else {
                    String result = gens.get(0).isEmpty() ? "Extinction" : "Still life";
                    System.out.println(result + " at gen " + lastGen);
                }
            }

            if (interesting) {
                String filename = System.getProperty("java.io.tmpdir") + "/" + tiling.getClass().getSimpleName() + "-" + System.nanoTime() + "-" + desc + ".gif";
                createAnimatedGif(filename, tiling, gens);
                System.out.println("Wrote " + gens.size() + " generations to " + filename);
            }
        }
    }

    private static <Cell> List<Set<Cell>> evolve(Tiling<Cell> tiling, Set<Cell> gen0, int numGens) {
        Map<Set<Cell>, Integer> firstSeen = new HashMap<Set<Cell>, Integer>();
        List<Set<Cell>> gens = new ArrayList<Set<Cell>>();
        gens.add(gen0);
        firstSeen.put(gen0, 0);

        Set<Cell> alive = gen0;
        for (int gen = 1; gen < numGens; gen++) {
            if (alive.size() == 0) break;

            Set<Cell> nextGen = nextGeneration(tiling, alive);
            Integer prevSeen = firstSeen.get(nextGen);
            if (prevSeen != null) {
                if (gen - prevSeen > 1) gens.add(nextGen); // Finish the loop.
                break;
            }

            alive = nextGen;
            gens.add(alive);
            firstSeen.put(alive, gen);
        }

        return gens;
    }

    private static <Cell> void createAnimatedGif(String filename, Tiling<Cell> tiling, List<Set<Cell>> gens) throws IOException {
        OutputStream out = new FileOutputStream(filename);
        ImageWriter imgWriter = ImageIO.getImageWritersByFormatName("gif").next();
        ImageOutputStream imgOut = ImageIO.createImageOutputStream(out);
        imgWriter.setOutput(imgOut);
        imgWriter.prepareWriteSequence(null);

        Rectangle bounds = bbox(tiling, gens);
        Set<Cell> gen0 = gens.get(0);
        int numGens = gens.size();

        for (int gen = 0; gen < numGens; gen++) {
            Set<Cell> alive = gens.get(gen);

            // If we have an oscillator which loops cleanly back to the start, skip the last frame.
            if (gen > 0 && alive.equals(gen0)) break;

            writeGifFrame(imgWriter, render(tiling, bounds, alive), gen == 0, gen == numGens - 1);
        }

        imgWriter.endWriteSequence();
        imgOut.close();
        out.close();
    }

    private static <Cell> Rectangle bbox(Tiling<Cell> tiling, Collection<? extends Collection<Cell>> gens) {
        Rectangle bounds = new Rectangle(-1, -1);
        Set<Cell> allGens = new HashSet<Cell>();
        for (Collection<Cell> gen : gens) allGens.addAll(gen);
        for (Cell cell : allGens) {
            int[][] cellBounds = tiling.bounds(cell);
            int[] xs = cellBounds[0], ys = cellBounds[1];
            for (int i = 0; i < xs.length; i++) bounds.add(xs[i], ys[i]);
        }

        bounds.grow(MARGIN, MARGIN);
        return bounds;
    }

    private static void writeGifFrame(ImageWriter imgWriter, BufferedImage img, boolean isFirstFrame, boolean isLastFrame) throws IOException {
        IIOMetadata metadata = imgWriter.getDefaultImageMetadata(new ImageTypeSpecifier(img), null);

        String metaFormat = metadata.getNativeMetadataFormatName();
        Node root = metadata.getAsTree(metaFormat);

        IIOMetadataNode grCtlExt = findOrCreateNode(root, "GraphicControlExtension");
        grCtlExt.setAttribute("delayTime", isLastFrame ? "1000" : "30"); // Extra delay for last frame
        grCtlExt.setAttribute("disposalMethod", "doNotDispose");

        if (isFirstFrame) {
            // Configure infinite looping.
            IIOMetadataNode appExts = findOrCreateNode(root, "ApplicationExtensions");
            IIOMetadataNode appExt = findOrCreateNode(appExts, "ApplicationExtension");
            appExt.setAttribute("applicationID", "NETSCAPE");
            appExt.setAttribute("authenticationCode", "2.0");
            appExt.setUserObject(new byte[] { 1, 0, 0 });
        }

        metadata.setFromTree(metaFormat, root);
        imgWriter.writeToSequence(new IIOImage(img, null, metadata), null);
    }

    private static IIOMetadataNode findOrCreateNode(Node parent, String nodeName) {
        for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
            if (child.getNodeName().equals(nodeName)) return (IIOMetadataNode)child;
        }

        IIOMetadataNode node = new IIOMetadataNode(nodeName);
        parent.appendChild(node);
        return node ;
    }

    private static <Cell> Set<Cell> nextGeneration(Tiling<Cell> tiling, Set<Cell> gen) {
        Map<Cell, Integer> neighbourCount = new HashMap<Cell, Integer>();
        for (Cell cell : gen) {
            for (Cell neighbour : tiling.neighbours(cell)) {
                Integer curr = neighbourCount.get(neighbour);
                neighbourCount.put(neighbour, 1 + (curr == null ? 0 : curr.intValue()));
            }
        }

        Set<Cell> nextGen = new HashSet<Cell>();
        for (Map.Entry<Cell, Integer> e : neighbourCount.entrySet()) {
            if (e.getValue() == 3 || (e.getValue() == 2 && gen.contains(e.getKey()))) {
                nextGen.add(e.getKey());
            }
        }

        return nextGen;
    }

    private static <Cell> BufferedImage render(Tiling<Cell> tiling, Rectangle bounds, Collection<Cell> alive) {
        // Create a suitable paletted image
        int width = bounds.width;
        int height = bounds.height;
        byte[] data = new byte[width * height];
        int[] pal = new int[]{ GRIDCOL.getRGB(), DEADCOL.getRGB(), LIVECOL.getRGB() };
        ColorModel colourModel = new IndexColorModel(8, pal.length, pal, 0, false, -1, DataBuffer.TYPE_BYTE);
        DataBufferByte dbb = new DataBufferByte(data, width * height);
        WritableRaster raster = Raster.createPackedRaster(dbb, width, height, width, new int[]{0xff}, new Point(0, 0));
        BufferedImage img = new BufferedImage(colourModel, raster, true, null);
        Graphics g = img.createGraphics();

        // Render the tiling.
        // We assume that either one of the live cells or the "initial cell" is in bounds.
        Set<Cell> visited = new HashSet<Cell>();
        Set<Cell> unvisited = new HashSet<Cell>(alive);
        unvisited.add(tiling.initialCell());
        while (!unvisited.isEmpty()) {
            Iterator<Cell> it = unvisited.iterator();
            Cell current = it.next();
            it.remove();
            visited.add(current);

            Rectangle cellBounds = new Rectangle(-1, -1);
            int[][] cellVertices = tiling.bounds(current);
            int[] xs = cellVertices[0], ys = cellVertices[1];
            for (int i = 0; i < xs.length; i++) {
                cellBounds.add(xs[i], ys[i]);
                xs[i] -= bounds.x;
                ys[i] -= bounds.y;
            }

            if (!bounds.intersects(cellBounds)) continue;

            g.setColor(alive.contains(current) ? LIVECOL : DEADCOL);
            g.fillPolygon(xs, ys, xs.length);
            g.setColor(GRIDCOL);
            g.drawPolygon(xs, ys, xs.length);

            for (Cell neighbour : tiling.neighbours(current)) {
                if (!visited.contains(neighbour)) unvisited.add(neighbour);
            }
        }

        return img;
    }
}

Jeder Scheitelpunkt erzeugt ein Stillleben (2 Punkte):

java GenericLife CairoTiling stilllife.gif 0 0 0 1 1 1 3 2 3 3 4 2 4 3

Stillleben

Oszillatoren (15 Punkte): Im Uhrzeigersinn von links oben haben wir die Befehle 2, 3, 4, 6, 11, 12.

Assortierte Oszillatoren


Ich kann die Schildkröte nicht sehen.
Quentin

@Quentin, mein Spitzname für den P3-Oszillator ist Ebola. Du hast den verwirrten Kopf und den Schwanz.
Peter Taylor

Ich habe über den P2 nachgedacht. Sieht aus wie eine ständig flippende Schildkröte.
Quentin

Der p4 sieht auch aus wie eine schwimmende Schildkröte.
Ross Presser

16

Rhombille (30+ Punkte)

Dieses Gitter hat eine ziemlich hohe Konnektivität (jede Zelle hat 10 Nachbarn), und seltsamerweise scheint dies effektiver zur Geburt als zum Tod beizutragen. Die meisten zufälligen Gitter scheinen ein unendliches Wachstum auszulösen (25 Punkte); zB diese 5-Zellen-Startposition:

Startposition

entwickelt über 300 Generationen zu etwas Riesigem:

Entwicklung dieser Ausgangsposition

und die Bevölkerung wächst quadratisch mit der Generation seit mindestens 3000 Generationen.

Vielleicht habe ich deshalb nur einen Oszillator der Periode 2 (3 Punkte) gefunden:

3-Zellen-Oszillator

Wie für Stillleben (2 Punkte): Nehmen Sie alle 4 - Zellen um einen einzigen Kontrollpunkt.

Der Code (Verwendung mit dem generischen Framework und AbstractLatticeKlassen, die ich in früheren Antworten gepostet habe):

public class Rhombille extends AbstractLattice {
    public Rhombille() {
        super(14, 0, 7, 12, new int[][] {
                {0, 7, 14, 7},
                {0, 7, 7, 0},
                {7, 14, 14, 7}
            }, new int[][] {
                {0, 4, 0, -4},
                {0, -4, -12, -8},
                {-4, 0, -8, -12}
            });
    }

    @Override
    public boolean isInterestingOscillationPeriod(int period) {
        return period != 2;
    }
}

14

Rhombitrihexagonal Fliesen , 17+ Punkte

Auf Wunsch von Martin Büttner.

Stillleben (2 Punkte):

Eine Kette mit zwei Schlaufen

Oszillatoren von Perioden (im Uhrzeigersinn von oben links) 2, 4, 5, 6, 11 (15 Punkte):

Diverse Oszillatoren

Im Allgemeinen hat ein Oszillator eine Reihe von Zellen, die sich ändern (den Kern ), eine Reihe von Zellen, die sich neben dem Kern befinden (den Mantel ), und eine Reihe von Zellen, die verhindern, dass sich der Mantel ändert (den Träger ). Bei dieser Kachelung kann sich die Unterstützung der Oszillatoren manchmal überlappen: z

4-Oszillator und 5-Oszillator mit überlappender Unterstützung

Wenn der 4-Oszillator entfernt würde, würde die Unterstützung des 5-Oszillators versagen und sich schließlich zu einem 2-Oszillator entwickeln. Wenn jedoch der 5-Oszillator entfernt würde, würde die Unterstützung des 4-Oszillators einfach ein Sechseck hinzufügen und stabilisieren, so dass dies nicht wirklich ein 20-Oszillator ist.


Der Code, der diese Kacheln implementiert, ist äußerst allgemein: Auf der Grundlage meiner Erfahrung mit aperiodischen Kacheln erkannte ich, dass das Erweitern einer bekannten Grenze und das Durchsuchen eines Scheitelpunkts eine sehr flexible Technik ist, auch wenn sie für einfache Gitter möglicherweise nicht effizient ist. Da wir jedoch an komplexeren Gittern interessiert sind, habe ich diesen Ansatz hier gewählt.

Jede periodische Kachelung ist ein Gitter, und es ist möglich, eine Grundeinheit zu identifizieren (bei dieser Kachelung handelt es sich um ein Sechseck, zwei Dreiecke und drei Quadrate), die sich entlang zweier Achsen wiederholt. Dann geben Sie einfach die Achsenversätze und die Koordinaten der primitiven Zellen einer Grundeinheit ein und schon sind Sie fertig.

Der gesamte Code kann als zip unter https://gist.github.com/pjt33/becd56784480ddd751bf heruntergeladen werden. Dies schließt auch einen Code ein, GenericLifeGuiden ich nicht auf dieser Seite gepostet habe.

public class Rhombitrihexagonal extends AbstractLattice {
    public Rhombitrihexagonal() {
        super(22, 0, 11, 19, new int[][] {
                {-7, 0, 7, 7, 0, -7},
                {0, 4, 11, 7},
                {7, 11, 15},
                {7, 15, 15, 7},
                {7, 15, 11},
                {7, 11, 4, 0},
            }, new int[][] {
                {4, 8, 4, -4, -8, -4},
                {8, 15, 11, 4},
                {4, 11, 4},
                {4, 4, -4, -4},
                {-4, -4, -11},
                {-4, -11, -15, -8},
            });
    }

    @Override
    public boolean isInterestingOscillationPeriod(int period) {
        return period != 2 && period != 4 && period != 5 && period != 6 && period != 10 && period != 12 && period != 15 && period != 30;
    }
}

Die Unterstützung hierfür ist mein zuvor veröffentlichtes generisches Framework plus die AbstractLatticeKlasse:

import java.awt.Point;
import java.util.*;

public abstract class AbstractLattice implements Tiling<AbstractLattice.LatticeCell> {
    // Use the idea of expansion and vertex mapping from my earlier aperiod tiling implementation.
    private Map<Point, Set<LatticeCell>> vertexNeighbourhood = new HashMap<Point, Set<LatticeCell>>();
    private int scale = -1;

    // Geometry
    private final int dx0, dy0, dx1, dy1;
    private final int[][] xs;
    private final int[][] ys;

    protected AbstractLattice(int dx0, int dy0, int dx1, int dy1, int[][] xs, int[][] ys) {
        this.dx0 = dx0;
        this.dy0 = dy0;
        this.dx1 = dx1;
        this.dy1 = dy1;
        // Assume sensible subclasses, so no need to clone the arrays to prevent modification.
        this.xs = xs;
        this.ys = ys;
    }

    private void expand() {
        scale++;
        // We want to enumerate all lattice cells whose extreme coordinate is +/- scale.
        // Corners:
        insertLatticeNeighbourhood(-scale, -scale);
        insertLatticeNeighbourhood(-scale, scale);
        insertLatticeNeighbourhood(scale, -scale);
        insertLatticeNeighbourhood(scale, scale);

        // Edges:
        for (int i = -scale + 1; i < scale; i++) {
            insertLatticeNeighbourhood(-scale, i);
            insertLatticeNeighbourhood(scale, i);
            insertLatticeNeighbourhood(i, -scale);
            insertLatticeNeighbourhood(i, scale);
        }
    }

    private void insertLatticeNeighbourhood(int x, int y) {
        for (int sub = 0; sub < xs.length; sub++) {
            LatticeCell cell = new LatticeCell(x, y, sub);
            int[][] bounds = bounds(cell);
            for (int i = 0; i < bounds[0].length; i++) {
                Point p = new Point(bounds[0][i], bounds[1][i]);

                Set<LatticeCell> adj = vertexNeighbourhood.get(p);
                if (adj == null) vertexNeighbourhood.put(p,  adj = new HashSet<LatticeCell>());
                adj.add(cell);
            }
        }
    }

    public Set<LatticeCell> neighbours(LatticeCell cell) {
        Set<LatticeCell> rv = new HashSet<LatticeCell>();

        // +1 because we will border cells from the next scale.
        int requiredScale = Math.max(Math.abs(cell.x), Math.abs(cell.y)) + 1;
        while (scale < requiredScale) expand();

        int[][] bounds = bounds(cell);
        for (int i = 0; i < bounds[0].length; i++) {
            Point p = new Point(bounds[0][i], bounds[1][i]);
            Set<LatticeCell> adj = vertexNeighbourhood.get(p);
            rv.addAll(adj);
        }

        rv.remove(cell);
        return rv;
    }

    public int[][] bounds(LatticeCell cell) {
        int[][] bounds = new int[2][];
        bounds[0] = xs[cell.sub].clone();
        bounds[1] = ys[cell.sub].clone();
        for (int i = 0; i < bounds[0].length; i++) {
            bounds[0][i] += cell.x * dx0 + cell.y * dx1;
            bounds[1][i] += cell.x * dy0 + cell.y * dy1;
        }

        return bounds;
    }

    public LatticeCell initialCell() {
        return new LatticeCell(0, 0, 0);
    }

    public abstract boolean isInterestingOscillationPeriod(int period);

    public Set<LatticeCell> parseCells(String[] data) {
        Set<LatticeCell> rv = new HashSet<LatticeCell>();
        if (data.length % 3 != 0) throw new IllegalArgumentException("Data should come in triples");
        for (int i = 0; i < data.length; i += 3) {
            if (data[i + 2].length() != 1) throw new IllegalArgumentException("Third data item should be a single letter");
            rv.add(new LatticeCell(Integer.parseInt(data[i]), Integer.parseInt(data[i + 1]), data[i + 2].charAt(0) - 'A'));
        }
        return rv;
    }

    public String format(Set<LatticeCell> cells) {
        StringBuilder sb = new StringBuilder();
        for (LatticeCell cell : cells) {
            if (sb.length() > 0) sb.append(' ');
            sb.append(cell.x).append(' ').append(cell.y).append(' ').append((char)(cell.sub + 'A'));
        }

        return sb.toString();
    }

    static class LatticeCell {
        public final int x, y, sub;

        LatticeCell(int x, int y, int sub) {
            this.x = x;
            this.y = y;
            this.sub = sub;
        }

        @Override
        public int hashCode() {
            return (x * 0x100025) + (y * 0x959) + sub;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof LatticeCell)) return false;
            LatticeCell other = (LatticeCell)obj;
            return x == other.x && y == other.y && sub == other.sub;
        }

        @Override
        public String toString() {
            return x + " " + y + " " + (char)('A' + sub);
        }
    }
}

Nach ein paar Stunden CPU-Zeit habe ich einen 7-Oszillator und einen 15-Oszillator sowie einige interessante Oszillatorpaare hinzugefügt, in denen sie sich einige der Zellen teilen, die sie stabil halten.
Peter Taylor

Und durch manuelles Ändern des 7-Oszillators habe ich versehentlich einen 3-Oszillator erstellt, der Ihnen Aufschluss darüber gibt, wie effektiv die Zufallssuche ist.
Peter Taylor

13

Aperiodische Labyrinthfliesen (45+ Punkte)

Dies verwendet den allgemeinen Rahmen meiner früheren Antwort.

Stillleben (2 Punkte):

Labyrinthstillleben: Vier Dreiecke treffen auf einen Eckpunkt der Ordnung 12

Oszillator (3 Punkte):

Oszillatorbild

Dieser Oszillator ist sehr verbreitet und taucht aufgrund der meisten zufälligen Startpunkte auf.

Code:

import java.awt.Point;
import java.util.*;

public class LabyrinthTiling implements Tiling<String> {
    private Map<Point, Point> internedPoints = new HashMap<Point, Point>();
    private Map<String, Set<Point>> vertices = new HashMap<String, Set<Point>>();
    private Map<Point, Set<String>> tris = new HashMap<Point, Set<String>>();

    private int level = 0;
    // 3^level
    private int scale = 1;

    public LabyrinthTiling() {
        linkSymmetric("", new Point(-8, 0));
        linkSymmetric("", new Point(8, 0));
        linkSymmetric("", new Point(0, 14));
    }

    private void linkSymmetric(String suffix, Point p) {
        int ay = Math.abs(p.y);
        link("+" + suffix, new Point(p.x, ay));
        link("-" + suffix, new Point(p.x, -ay));
    }

    private void link(String tri, Point p) {
        Point p2 = internedPoints.get(p);
        if (p2 == null) internedPoints.put(p, p);
        else p = p2;

        Set<Point> ps = vertices.get(tri);
        if (ps == null) vertices.put(tri, ps = new HashSet<Point>());

        Set<String> ts = tris.get(p);
        if (ts == null) tris.put(p, ts = new HashSet<String>());

        ps.add(p);
        ts.add(tri);
    }

    private void expand() {
        level++;
        scale *= 3;
        subdivideEq("", new Point(-8 * scale, 0), new Point(8 * scale, 0), new Point(0, 14 * scale), level, true);
    }

    private static Point avg(Point p0, Point p1, Point p2) {
        return new Point((p0.x + p1.x + p2.x) / 3, (p0.y + p1.y + p2.y) / 3);
    }

    private void subdivideEq(String suffix, Point p0, Point p1, Point p2, int level, boolean skip0) {
        if (level == 0) {
            linkSymmetric(suffix, p0);
            linkSymmetric(suffix, p1);
            linkSymmetric(suffix, p2);
            return;
        }

        Point p01 = avg(p0, p0, p1), p10 = avg(p0, p1, p1);
        Point p02 = avg(p0, p0, p2), p20 = avg(p0, p2, p2);
        Point p12 = avg(p1, p1, p2), p21 = avg(p1, p2, p2);
        Point c = avg(p0, p1, p2);
        level--;

        if (!skip0) subdivideEq(suffix + "0", p01, p10, c, level, false);
        subdivideIso(suffix + "1", p0, c, p01, level);
        subdivideIso(suffix + "2", p0, c, p02, level);
        subdivideEq(suffix + "3", p02, c, p20, level, false);
        subdivideIso(suffix + "4", p2, c, p20, level);
        subdivideIso(suffix + "5", p2, c, p21, level);
        subdivideEq(suffix + "6", c, p12, p21, level, false);
        subdivideIso(suffix + "7", p1, c, p12, level);
        subdivideIso(suffix + "8", p1, c, p10, level);
    }

    private void subdivideIso(String suffix, Point p0, Point p1, Point p2, int level) {
        if (level == 0) {
            linkSymmetric(suffix, p0);
            linkSymmetric(suffix, p1);
            linkSymmetric(suffix, p2);
            return;
        }

        Point p01 = avg(p0, p0, p1), p10 = avg(p0, p1, p1);
        Point p02 = avg(p0, p0, p2), p20 = avg(p0, p2, p2);
        Point p12 = avg(p1, p1, p2), p21 = avg(p1, p2, p2);
        Point c = avg(p0, p1, p2);
        level--;

        subdivideIso(suffix + "0", p0, p01, p02, level);
        subdivideEq(suffix + "1", p01, p02, p20, level, false);
        subdivideIso(suffix + "2", p01, p2, p20, level);
        subdivideIso(suffix + "3", p01, p2, c, level);
        subdivideIso(suffix + "4", p01, p10, c, level);
        subdivideIso(suffix + "5", p10, p2, c, level);
        subdivideIso(suffix + "6", p10, p2, p21, level);
        subdivideEq(suffix + "7", p10, p12, p21, level, false);
        subdivideIso(suffix + "8", p1, p10, p12, level);
    }

    public Set<String> neighbours(String cell) {
        Set<String> rv = new HashSet<String>();

        Set<Point> cellVertices;
        while ((cellVertices = vertices.get(cell)) == null) expand();
        for (Point p : cellVertices) {
            // If the point is on the edge of the current level, we need to expand once more.
            if (Math.abs(p.x) / 8 + Math.abs(p.y) / 14 == scale) expand();

            Set<String> adj = tris.get(p);
            rv.addAll(adj);
        }

        rv.remove(cell);
        return rv;
    }

    public int[][] bounds(String cell) {
        Set<Point> cellVertices;
        while ((cellVertices = vertices.get(cell)) == null) expand();

        int[][] bounds = new int[2][3];
        int off = 0;
        for (Point p : cellVertices) {
            bounds[0][off] = p.x;
            bounds[1][off] = p.y;
            off++;
        }

        return bounds;
    }

    public String initialCell() {
        return "+";
    }

    public boolean isInterestingOscillationPeriod(int period) {
        return period != 4;
    }

    public Set<String> parseCells(String[] data) {
        Set<String> rv = new HashSet<String>();
        for (String cell : data) rv.add(cell);
        return rv;
    }

    public String format(Set<String> cells) {
        StringBuilder sb = new StringBuilder();
        for (String cell : cells) {
            if (sb.length() > 0) sb.append(' ');
            sb.append(cell);
        }

        return sb.toString();
    }
}

13

Penrose-ähnliche Projektion eines 7-dimensionalen Gitters (64+ Punkte)

Dies ist vergleichbar mit der Penrose-Parkettierung (um eine Penrose-Parkettierung ersetzen N = 7mit N = 5) und qualifiziert mich für die aperiodischen Bonus (40 Punkte).

Stillleben (2 Punkte): Trivial, da die Protozellen konvex sind, sodass jeder Scheitelpunkt in der Größenordnung von 3 oder mehr ausreicht. (Wählen Sie alle Gesichter aus, wenn es sich um die 3. oder eine der 4 anderen handelt.)

Kurzzeitoszillatoren (15 Punkte):

Diese Kacheln sind reich an Oszillatoren. Der kleinste Zeitraum, für den ich nur einen Oszillator gefunden habe, ist 11, und der kleinste Zeitraum, für den ich keinen gefunden habe, ist 13.

p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12

Langzeitoszillator (7 Punkte):

Ich habe mich bewusst für eine der Varianten dieser rotationssymmetrischen Fliesen entschieden, die sich für den Langzeitoszillator als nützlich erwiesen hat. Es dreht sich alle 28 Generationen um ein Siebtel um den Mittelpunkt, was es zu einem p196 macht.

p196

Der Code verwendet das Framework, das ich in früheren Antworten zusammen mit der folgenden Kachelklasse gepostet habe:

import java.awt.geom.Point2D;
import java.util.*;

public class Penrose7Tiling implements Tiling<Penrose7Tiling.Rhomb> {
    private Map<String, Rhomb> rhombs = new HashMap<String, Rhomb>();

    private static final int N = 7;
    private double scale = 16;
    private double[] gamma;
    // Nth roots of unity.
    private Point2D.Double[] zeta;

    public Penrose7Tiling() {
        gamma = new double[N];
        zeta = new Point2D.Double[N];
        for (int i = 0; i < N; i++) {
            gamma[i] = 1.0 / N; // for global rotational symmetry
            zeta[i] = new Point2D.Double(Math.cos(2 * i * Math.PI / N), Math.sin(2 * i * Math.PI / N));
        }
    }

    private Rhomb getRhomb(int r, int s, int k_r, int k_s) {
        String key = String.format("%d,%d,%d,%d", r, s, k_r, k_s);
        Rhomb rhomb = rhombs.get(key);
        if (rhomb == null) rhombs.put(key, rhomb = new Rhomb(r, s, k_r, k_s));
        return rhomb;
    }

    private int round(double val) {
        return (int)Math.round(scale * val);
    }

    public class Rhomb {
        public int[] k;
        public int r, s;

        private int[] xs = new int[4];
        private int[] ys = new int[4];
        private Set<Rhomb> neighbours;

        public Rhomb(int r, int s, int k_r, int k_s) {
            assert 0 <= r && r < s && s < N;

            this.r = r;
            this.s = s;

            // z_0 satisfies z_0 * zeta_{r,s} + gamma_{r,s} = k_{r,s}
            Point2D.Double z_0 = solveLinear(zeta[r].x, -zeta[r].y, gamma[r] - k_r, zeta[s].x, -zeta[s].y, gamma[s] - k_s);

            // Find base lattice point.
            Point2D.Double p = new Point2D.Double();
            k = new int[N];
            for (int i = 0; i < N; i++) {
                int k_i;
                if (i == r) k_i = k_r;
                else if (i == s) k_i = k_s;
                else k_i = (int)Math.ceil(z_0.x * zeta[i].x - z_0.y * zeta[i].y + gamma[i]);

                k[i] = k_i;
                p.x += zeta[i].x * (k_i + gamma[i]);
                p.y += zeta[i].y * (k_i + gamma[i]);
            }

            xs[0] = round(p.x);
            ys[0] = round(p.y);
            xs[1] = round(p.x + zeta[r].x);
            ys[1] = round(p.y + zeta[r].y);
            xs[2] = round(p.x + zeta[r].x + zeta[s].x);
            ys[2] = round(p.y + zeta[r].y + zeta[s].y);
            xs[3] = round(p.x + zeta[s].x);
            ys[3] = round(p.y + zeta[s].y);
        }

        public Set<Rhomb> neighbours() {
            if (neighbours == null) {
                neighbours = new HashSet<Rhomb>();

                // There are quite a few candidates, but we have to check them...
                for (int nr = 0; nr < N - 1; nr++) {
                    for (int ns = nr + 1; ns < N; ns++) {
                        if (nr == r && ns == s) continue; // Can't happen.
                        for (int nk_r = k[nr] - 1; nk_r <= k[nr]; nk_r++) {
                            for (int nk_s = k[ns] - 1; nk_s <= k[ns]; nk_s++) {
                                Rhomb candidate = getRhomb(nr, ns, nk_r, nk_s);

                                // Our lattice points are (k) plus one or both of vec[r] and vec[s]
                                // where vec[0] = (1, 0, 0, ...), vec[1] = (0, 1, 0, ...), etc.
                                // Candidate has a similar set of 4 lattice points. Is there any agreement?
                                boolean isNeighbour = true;
                                for (int i = 0; i < N; i++) {
                                    int myMin = k[i], myMax = k[i] + ((i == r || i == s) ? 1 : 0);
                                    int cMin = candidate.k[i], cMax = candidate.k[i] + ((i == nr || i == ns) ? 1 : 0);
                                    if (myMin > cMax || cMin > myMax) isNeighbour = false;
                                }
                                if (isNeighbour) neighbours.add(candidate);
                            }
                        }
                    }
                }
            }

            return neighbours;
        }

        @Override
        public String toString() {
            return String.format("%d,%d,%d,%d", r, s, k[r], k[s]);
        }
    }

    // Solves ax + by + c = dx + ey + f = 0
    private Point2D.Double solveLinear(double a, double b, double c, double d, double e, double f) {
        double det = a*e - b*d;
        double x = (b*f - c*e) / det;
        double y = (c*d - a*f) / det;
        return new Point2D.Double(x, y);
    }

    public Set<Rhomb> neighbours(Rhomb cell) {
        return cell.neighbours();
    }

    public int[][] bounds(Rhomb cell) {
        // Will be modified. Copy-clone for safety.
        return new int[][]{ cell.xs.clone(), cell.ys.clone() };
    }

    public Rhomb initialCell() {
        return getRhomb(0, 1, 0, 0);
    }

    public boolean isInterestingOscillationPeriod(int period) {
        return period == 11 || period == 13 || (period > 14 && period != 26);
    }

    public Set<Rhomb> parseCells(String[] data) {
        Set<Rhomb> rv = new HashSet<Rhomb>();
        for (String key : data) {
            String[] parts = key.split(",");
            int r = Integer.parseInt(parts[0]);
            int s = Integer.parseInt(parts[1]);
            int k_r = Integer.parseInt(parts[2]);
            int k_s = Integer.parseInt(parts[3]);
            rv.add(getRhomb(r, s, k_r, k_s));
        }
        return rv;
    }

    public String format(Set<Rhomb> cells) {
        StringBuilder sb = new StringBuilder();
        for (Rhomb cell : cells) {
            if (sb.length() > 0) sb.append(' ');
            sb.append(cell);
        }

        return sb.toString();
    }
}

10

java, punkte-aktuell 11

Dies ist die neue und verbesserte Version der obigen, außer ohne einen schwerwiegenden Fehler!

Probieren Sie es hier , jetzt mit zufälliger Taste! (mehrmals drücken, um mehr Füllung zu erhalten).

Erster, Periode 4 Oszillator, 3 Punkte

Bildbeschreibung hier eingeben

Als nächstes 2 3 Perioden 2 Oszillatoren - 3 Punkte

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

2 weitere 2 Perioden-Oszillatoren, mit freundlicher Genehmigung von Martin Büttner (oooohhhhhhh ... Farbe)

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Ich habe ein Programm erstellt, um es zufällig und kontinuierlich auszuführen und nach Oszillationen zu suchen. Es hat diesen gefunden. Zeitraum 5 +3 Punkte

Bildbeschreibung hier eingeben

Und noch eine Periode 5, die vom Randomizer gefunden wurde.

Bildbeschreibung hier eingeben

Und natürlich ein Stillleben (zum Beispiel gibt es viele) 2 Punkte

Bildbeschreibung hier eingeben

Code-Hauptklasse

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class Main{
    public static void main(String[] args) {
        new Main();
    }

    Canvas canvas = new Canvas();
    JFrame frame = new JFrame();
    Timer timer;
    ShapeInfo info;
    int[][][] history;
    public Main() {
        JPanel panel = new JPanel();
        panel.setMinimumSize(new Dimension(500,500));
        panel.setLayout(new GridBagLayout());

        frame.setMinimumSize(new Dimension(500,500));
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //frame.setResizable(false);
        canvas.setMinimumSize(new Dimension(200,200));
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.fill = GridBagConstraints.BOTH;
        panel.add(canvas,c);

        JButton startButton = new JButton();
        startButton.setText("click to start");
        startButton.setMaximumSize(new Dimension(100,50));
        GridBagConstraints g = new GridBagConstraints();
        g.gridx =0;
        g.gridy = 0;
        g.weightx = 1;
        panel.add(startButton,g);

        JButton restartButton = new JButton();
        restartButton.setText("revert");
        GridBagConstraints b = new GridBagConstraints();
        b.gridx = 0;
        b.gridy = 9;
        panel.add(restartButton,b);

        JButton clearButton = new JButton();
        clearButton.setText("Clear");
        GridBagConstraints grid = new GridBagConstraints();
        grid.gridx = 1;
        grid.gridy = 0;
        panel.add(clearButton,grid);

        JButton randomButton = new JButton();
        randomButton.setText("fill randomly");
        GridBagConstraints rt = new GridBagConstraints();
        rt.gridx = 2;
        rt.gridy = 0;
        panel.add(randomButton,rt);

        JLabel speedLabel = new JLabel();
        speedLabel.setText("speed");
        GridBagConstraints rt2 = new GridBagConstraints();
        rt2.gridx = 3;
        rt2.gridy = 0;
        panel.add(speedLabel,rt2);

        final JTextField speed = new JTextField();
        speed.setText("300");
        GridBagConstraints rt21 = new GridBagConstraints();
        rt21.gridx = 4;
        rt21.gridy = 0;
        panel.add(speed,rt21);

        speed.getDocument().addDocumentListener(new DocumentListener(){

            @Override
            public void changedUpdate(DocumentEvent arg0) {
                doSomething();

            }
            @Override
            public void insertUpdate(DocumentEvent arg0) {
                doSomething();

            }
            @Override
            public void removeUpdate(DocumentEvent arg0) {
                doSomething();

            }   
            public void doSomething(){
                try{int s = Integer.valueOf(speed.getText());
                timer.setDelay(s);}
                catch(Exception e){}
            }
        });

        randomButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) { 
                for(int i = 0; i< canvas.squaresHigh*canvas.squaresWide/2;i++){
                    double rx = Math.random();
                    double ry = Math.random();
                    int position = (int) Math.floor(Math.random() * 13);
                    int x = (int)(rx * canvas.squaresWide);
                    int y = (int)(ry * canvas.squaresHigh);
                    if(x!=0&&x!=canvas.squaresWide-1&&y!=0&&y!=canvas.squaresHigh-1){
                        info.allShapes[x][y][position] = 1;
                    }
                }
                history = cloneArray(info.allShapes);
                canvas.draw(info.allShapes);
            }
        });

        clearButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                info = new ShapeInfo(canvas.squaresWide,canvas.squaresHigh);
                restart();
            }
        });

        final JTextField scaleFactor = new JTextField();
        scaleFactor.setText("5");
        GridBagConstraints gh = new GridBagConstraints();
        gh.gridx  = 0;
        gh.gridy = 1;
        panel.add(scaleFactor,gh);
        scaleFactor.getDocument().addDocumentListener(new DocumentListener(){

            @Override
            public void changedUpdate(DocumentEvent arg0) {
                doSomething();
            }

            @Override
            public void insertUpdate(DocumentEvent arg0) {
                doSomething();
            }

            @Override
            public void removeUpdate(DocumentEvent arg0) {
                doSomething();
            }
            public void doSomething(){
                try{
                canvas.size = Integer.valueOf(scaleFactor.getText());
                canvas.draw(info.allShapes);
                }
                catch(Exception e){}
            }

        });
        timer = new Timer(300, listener);
        frame.pack();
        frame.setVisible(true);
        info = new ShapeInfo(canvas.squaresWide, canvas.squaresHigh);
        info.width = canvas.squaresWide;
        info.height = canvas.squaresHigh;
        history = cloneArray(info.allShapes);
        //history[8][11][1] = 1;
        canvas.draw(info.allShapes);
        restartButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if(timer.isRunning() == true){
                    info.allShapes = cloneArray(history);
                    restart();
                }
            }
        });
        canvas.addMouseListener(new MouseListener(){
            @Override
            public void mouseClicked(MouseEvent e) {
                int x = e.getLocationOnScreen().x - canvas.getLocationOnScreen().x;
                int y = e.getLocationOnScreen().y - canvas.getLocationOnScreen().y;
                Point location = new Point(x,y);
                for(PolygonInfo p:canvas.polygons){
                    if(p.polygon.contains(location)){
                        if(info.allShapes[p.x][p.y][p.position] == 1){
                            info.allShapes[p.x][p.y][p.position] = 0;
                        }
                        else{
                            info.allShapes[p.x][p.y][p.position] = 1;
                        }
                    }
                }
                canvas.draw(info.allShapes);
                history = cloneArray(info.allShapes);
            }
            @Override
            public void mouseEntered(MouseEvent arg0) {
            }
            @Override
            public void mouseExited(MouseEvent arg0) {
            }
            @Override
            public void mousePressed(MouseEvent arg0) { 
            }
            @Override
            public void mouseReleased(MouseEvent arg0) {    
            }
        });
        startButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                timer.start();
            }
        });
    }
    public int[][][] cloneArray(int[][][] array){
        int[][][] newArray = new int[array.length][array[0].length][array[0][0].length];
        for(int x = 0;x<array.length;x++){
            int[][] subArray = array[x];
            for(int y = 0; y < subArray.length;y++){
                int subSubArray[] = subArray[y];
                newArray[x][y] = subSubArray.clone();
            }
        }
        return newArray;
    }
    public void restart(){
        timer.stop();
        canvas.draw(info.allShapes);
    }
    public void setUp(){
        int[] boxes = new int[]{2,3,4,6,7,8};
        for(int box:boxes){
            info.allShapes[8][12][box-1] = 1;
            info.allShapes[9][13][box-1] = 1;
            info.allShapes[8][14][box-1] = 1;
            info.allShapes[9][15][box-1] = 1;
        }
    }
    public void update() {
        ArrayList<Coordinate> dieList = new ArrayList<Coordinate>();
        ArrayList<Coordinate> appearList = new ArrayList<Coordinate>();
        for (int x = 0; x < canvas.squaresWide; x++) {
            for (int y = 0; y < canvas.squaresHigh; y++) {
                for(int position = 0;position <13;position++){
                    int alive = info.allShapes[x][y][position];
                    int touching = info.shapesTouching(x, y, position);
                    if(touching!=0){
                    }
                    if(alive == 1){
                        if(touching < 2 || touching > 3){
                            //cell dies
                            dieList.add(new Coordinate(x,y,position));
                        }
                    }
                    else{
                        if(touching == 3){
                            //cell appears
                            appearList.add(new Coordinate(x,y,position));
                        }
                    }
                }
            }
        }
        for(Coordinate die:dieList){
            info.allShapes[die.x][die.y][die.position] = 0;
        }
        for(Coordinate live:appearList){
            info.allShapes[live.x][live.y][live.position] = 1;
        }
    }
    boolean firstDraw = true;
    int ticks = 0;
    ActionListener listener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            canvas.draw(info.allShapes);
            if(ticks !=0){
            update();
            }
            ticks++;
        }
    };
}

Segeltuch -

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.util.ArrayList;

import javax.swing.JPanel;

public class Canvas extends JPanel {
    private static final long serialVersionUID = 1L;

    public int squaresWide = 30;
    public int squaresHigh = 30;
    public int size = 6;
    ArrayList<PolygonInfo> polygons = new ArrayList<PolygonInfo>();
    boolean drawTessalationOnly = true;
    private int[][][] shapes;

    public void draw(int[][][] shapes2) {
        shapes = shapes2;
        drawTessalationOnly = false;
        this.repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        //System.out.println("drawing");
        polygons.clear();
        super.paintComponent(g);
        g.setColor(Color.black);
        // draw tessellation
        for (int x = 0; x < squaresWide; x++) {
            for (int y = 0; y < squaresHigh; y++) {
                for (int position = 0; position < 13; position++) {
                    // System.out.println("position = " + position);
                    Polygon p = new Polygon();
                    int points = 0;
                    int[] xc = new int[] {};
                    int[] yc = new int[] {};
                    if (position == 0) {
                        xc = new int[] {-2,0,2,0};
                        yc = new int[] {0,-2,0,2};
                        points = 4;
                    }
                    if (position == 1) {
                        xc = new int[] {2,4,4,1};
                        yc = new int[] {0,0,2,1};
                        points = 4;
                    }
                    if (position == 2) {
                        xc = new int[] {4,6,7,4};
                        yc = new int[] {0,0,1,2};
                        points = 4;
                    }
                    if (position == 3) {
                        xc = new int[] {1,2,0,0};
                        yc = new int[] {1,4,4,2};
                        points = 4;
                    }
                    if (position == 4) {
                        xc = new int[] {1,4,4,2};
                        yc = new int[] {1,2,4,4};
                        points = 4;
                    }
                    if (position == 5) {
                        xc = new int[] {7,6,4,4};
                        yc = new int[] {1,4,4,2};
                        points = 4;
                    }
                    if (position == 6) {
                        xc = new int[] {7,8,8,6};
                        yc = new int[] {1,2,4,4};
                        points = 4;
                    }
                    if (position == 7) {
                        xc = new int[] {0,2,1,0};
                        yc = new int[] {4,4,7,6};
                        points = 4;
                    }
                    if (position == 8) {
                        xc = new int[] {1,2,4,4};
                        yc = new int[] {7,4,4,6};
                        points = 4;
                    }
                    if (position == 9) {
                        xc = new int[] {7,6,4,4};
                        yc = new int[] {7,4,4,6};
                        points = 4;
                    }
                    if (position == 10) {
                        xc = new int[] {8,6,7,8};
                        yc = new int[] {4,4,7,6};
                        points = 4;
                    }
                    if (position == 11) {
                        xc = new int[] {4,4,2,1};
                        yc = new int[] {6,8,8,7};
                        points = 4;
                    }
                    if (position == 12) {
                        xc = new int[] {4,4,6,7};
                        yc = new int[] {6,8,8,7};
                        points = 4;
                    }
                    int[] finalX = new int[xc.length];
                    int[] finalY = new int[yc.length];
                    for (int i = 0; i < xc.length; i++) {
                        int xCoord = xc[i];
                        xCoord = (xCoord + (8 * x)) * size;
                        finalX[i] = xCoord;
                    }
                    for (int i = 0; i < yc.length; i++) {
                        int yCoord = yc[i];
                        yCoord = (yCoord + (8 * y)) * size;
                        finalY[i] = yCoord;
                    }
                    p.xpoints = finalX;
                    p.ypoints = finalY;
                    p.npoints = points;
                    polygons.add(new PolygonInfo(p,x,y,position));
                    // for(int i = 0;i<p.npoints;i++){
                    // / System.out.println("(" + p.xpoints[i] + "," +
                    // p.ypoints[i] + ")");
                    // }
                    if (drawTessalationOnly == false) {
                        if (shapes[x][y][position] == 1) {
                            g.setColor(Color.black);
                            g.fillPolygon(p);
                        } else {
                            g.setColor(Color.black);
                            g.drawPolygon(p);
                        }
                    } else {
                        g.drawPolygon(p);
                    }
                }

            }
        }
    }
}

ShapeInfo -

public class ShapeInfo {
    int[][][] allShapes; // first 2 dimensions are coordinates of large square,
                            // last is boolean - if shaded
    int width = 30;
    int height = 30;

    public ShapeInfo(int width, int height) {
        allShapes = new int[width][height][13];
        for (int[][] i : allShapes) {
            for (int[] h : i) {
                for (int g : h) {
                    g = 0;
                }
            }
        }
    }

    public int shapesTouching(int x, int y, int position) {
        int t = 0;
        if (x > 0 && y > 0 && x < width - 1 && y < height - 1) {
            int[] inShape = new int[]{};
            int[] rightOfShape = new int[]{};
            int[] aboveShape = new int[]{};
            int[] leftOfShape = new int[]{};
            int[] belowShape = new int[]{};
            int[] aboveRightOfShape = new int[]{};
            int[] aboveLeftOfShape = new int[]{};
            int[] belowRightOfShape = new int[]{};
            int[] belowLeftOfShape = new int[]{};
            if (position == 0) {
                inShape = new int[]{1,3,4};
                aboveShape = new int[]{7,8,11};
                leftOfShape = new int[]{2,5,6};
                aboveLeftOfShape = new int[]{10,12,9};
            }
            if (position == 1) {
                inShape = new int[]{0,3,4,5,2};
                aboveShape = new int[]{11,12};
            }
            if (position == 2) {
                inShape = new int[]{1,4,5,6};
                rightOfShape = new int[]{0};
                aboveShape = new int[]{12,11};
            }
            if (position == 3) {
                inShape = new int[]{0,1,4,8,7};
                leftOfShape = new int[]{6,10};
            }
            if (position == 4) {
                inShape = new int[]{0,1,3,2,7,5,8,9};
            }
            if (position == 5) {
                inShape = new int[]{2,6,1,10,4,9,8};
                rightOfShape = new int[]{0};
            }
            if (position == 6) {
                inShape = new int[]{2,5,9,10};
                rightOfShape = new int[]{0,3,7};
            }
            if (position == 7) {
                inShape = new int[]{3,4,8,11};
                leftOfShape =new int[]{6,10};
                belowShape = new int[]{0};
            }
            if (position == 8) {
                inShape = new int[]{5,4,9,3,12,7,11};
                belowShape = new int[]{0};
            }
            if (position == 9) {
                inShape = new int[]{4,5,8,6,11,12,10};
                belowRightOfShape = new int[]{0};
            }
            if (position == 10) {
                inShape = new int[]{6,5,9,12};
                rightOfShape = new int[]{3,7};
                belowRightOfShape = new int[]{0};
            }
            if (position == 11) {
                inShape = new int[]{7,8,9,12};
                belowShape = new int[]{0,1,2};
            }
            if (position == 12) {
                inShape = new int[]{11,8,9,10};
                belowShape = new int[]{1,2};
                belowRightOfShape = new int[]{0};
            }
            for(int a:inShape){
                if(allShapes[x][y][a] == 1){t++;}
            }
            for(int a:rightOfShape){
                if(allShapes[x+1][y][a] == 1){t++;}
            }
            for(int a:leftOfShape){
                if(allShapes[x-1][y][a] == 1){t++;}
            }
            for(int a:aboveShape){
                if(allShapes[x][y-1][a] == 1){t++;}
            }
            for(int a:belowShape){
                if(allShapes[x][y+1][a] == 1){t++;}
            }
            for(int a:aboveRightOfShape){
                if(allShapes[x+1][y-1][a] == 1){t++;}
            }
            for(int a:aboveLeftOfShape){
                if(allShapes[x-1][y-1][a] == 1){t++;}
            }
            for(int a:belowRightOfShape){
                if(allShapes[x+1][y+1][a] == 1){t++;}
            }
            for(int a:belowLeftOfShape){
                if(allShapes[x-1][y+1][a] == 1){t++;}
            }
        }
        return t;
    }
}

Koordinate -

public class Coordinate {
    int x;
    int y;
    int position;
    public Coordinate(int X,int Y, int Position){
        x=X;
        y=Y;
        position = Position;
    }
}

PolygonInfo

import java.awt.Polygon;

public class PolygonInfo {
    public Polygon polygon;
    public int x;
    public int y;
    public int position;
    public PolygonInfo(Polygon p,int X,int Y,int Position){
        x = X;
        y = Y;
        polygon = p;
        position = Position;
    }
}

Wenn jemand etwas findet, wird er erwähnt. (Was mich erinnert: mein Bruder hat die ersten 2 Oszillatoren gefunden)



10

Javascript, HexagonSplit

Haftungsausschluss: Es ist aufgrund vieler Manipulationen an Doms ziemlich langsam und benötigt wahrscheinlich einen Bugfix, damit die X-Achse nicht umläuft.

Geige

http://jsfiddle.net/16bhsr52/9/

Fiddle erlaubt jetzt das Umschalten der aktiven Zellen.

Immernoch Live

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Oszillator

2 Phase 2 Phase

Raumschiff (2 Phasen, zwei Varianten)

2 Phase Variante der ersten

Raumschiff (4 Phasen)

Bildbeschreibung hier eingeben

Javascript

//--  Prepare  --
var topX = 0;
var topY = 0;
var sizeX = 40;
var sizeY = 10;
var patternSizeX = 17;
var patternSizeY = 43;
var patternElements = 3;
var neighbourTopLeft = -(sizeX + 1) * patternElements;
var neighbourTop = -(sizeX) * patternElements;
var neighbourTopRight = -(sizeX - 1) * patternElements;
var neighbourLeft = -patternElements;
var neighbourRight = +patternElements;
var neighbourBottomLeft = +(sizeX - 1) * patternElements;
var neighbourBottom = +(sizeX) * patternElements;
var neighbourBottomRight = +(sizeX + 1) * patternElements;
var patternNeighbours = [
    [neighbourTopLeft + 2, neighbourTop + 2, neighbourTopRight + 2, neighbourLeft, neighbourLeft + 1, 1, neighbourRight],
    [neighbourLeft + 1, 0, 2, neighbourRight, neighbourRight + 1, neighbourRight + 2],
    [neighbourLeft + 1, neighbourLeft + 2, 1, neighbourRight + 2, neighbourBottomLeft, neighbourBottom, neighbourBottomRight]
];

for (i = 0; i < sizeX; i++) {
    for (j = 0; j < sizeY; j++) {
        var tileId = (j * sizeX + i) * patternElements;
        $("body").append('<div id="t' + (tileId) + '" class="shapeDown" style="left:' + topX + patternSizeX * i + 'px;top:' + topY + patternSizeY * j + 'px;">');
        $("body").append('<div id="t' + (tileId + 1) + '" class="shapeHexagon" style="left:' + (8 + topX + patternSizeX * i) + 'px;top:' + (17 + topY + patternSizeY * j) + 'px;">');
        $("body").append('<div id="t' + (tileId + 2) + '" class="shapeUp" style="left:' + topX + patternSizeX * i + 'px;top:' + (34 + topY + patternSizeY * j) + 'px;">');
    }
}

//--  Populate  --
for (i = 0; i < (patternElements * sizeX * sizeY) / 5; i++) {
    $("#t" + Math.floor((Math.random() * (patternElements * sizeX * sizeY)))).addClass("shapeAlive");
};

//--  Animate  --
setInterval(progress, 1000);

function progress() {
    var dying = [];
    var rising = [];

    for (i = 0; i < sizeX; i++) {
        for (j = 0; j < sizeY; j++) {
            var tileBaseId = (j * sizeX + i) * patternElements;
            for (k = 0; k < patternElements; k++) {
                var tileSelect = "#t" + (tileBaseId + k);
                var alive = $(tileSelect).filter(".shapeAlive").length;
                var nbSelect = $.map(patternNeighbours[k], function (n, i) {
                    return ("#t" + (tileBaseId + n));
                }).join();
                var count = $(nbSelect).filter(".shapeAlive").length;
                if (alive && (count < 2 || count > 3)) {
                    dying.push(tileSelect);
                };
                if (!alive && count == 3) {
                    rising.push(tileSelect);
                };
            }
        }
    }

    $(dying.join()).removeClass("shapeAlive");
    $(rising.join()).addClass("shapeAlive");
};

CSS

.shapeHexagon {
    background-color: black;
    height: 8px;
    width: 16px;
    position: absolute;
}
.shapeUp {
    background-color: black;
    height: 8px;
    width: 16px;
    position: absolute;
}
.shapeUp:after, .shapeHexagon:before {
    content:"";
    position: absolute;
    top: -8px;
    left: 0px;
    width: 0;
    height: 0;
    border-style: solid;
    border-color: transparent transparent black;
    border-width: 0px 8px 8px 8px;
}
.shapeAlive.shapeUp {
    background-color: green;
}
.shapeAlive.shapeUp:after {
    border-color: transparent transparent green;
}
.shapeDown {
    background-color: black;
    height: 8px;
    width: 16px;
    position: absolute;
}
.shapeDown:after, .shapeHexagon:after {
    content:"";
    position: absolute;
    top: 8px;
    left: 0px;
    width: 0;
    height: 0;
    border-style: solid;
    border-color: black transparent transparent transparent;
    border-width: 8px 8px 0 8px;
}
.shapeAlive.shapeUp:after, .shapeAlive.shapeHexagon:before {
    border-color: transparent transparent green;
}
.shapeAlive.shapeDown, .shapeAlive.shapeHexagon {
    background-color: green;
}
.shapeAlive.shapeDown:after, .shapeAlive.shapeHexagon:after {
    border-color: green transparent transparent transparent;
}

10

"Hex Medley 3" (24+ Punkte *)

Inspiriert von der fünfeckigen Kachelung des Blümchens: Ein Block mit 7 Sechsecken kachelt das Flugzeug, und wir können die Sechsecke auf viele verschiedene Arten zerschneiden. Wie der Name schon sagt, ist dies die dritte Variation, die ich ausprobiert habe, aber es lohnt sich, sie zu veröffentlichen, da es die erste Kachel ist, die die 7 Punkte für einen p30 + -Oszillator beansprucht.

Die Fliesen sind:

Das Innere der 7 Sechsecke ist in 6 gleichseitige Dreiecke unterteilt.  die äußeren sechs in jeweils 3 Rhomben mit abwechselnder Parität

Da die Protozellen konvex sind, ergibt jeder Vertex der Ordnung 3 ein Stillleben (2 Punkte).

Ich habe fünf Oszillatoren mit kleiner Periode (15 Punkte) gefunden: Perioden 2, 3, 4, 6, 12.

p2 Oszillator p3 Oszillator p4 Oszillator p6 Oszillator p12 Oszillator

Und das Piece de Resistance : ein p48-Oszillator (7 Punkte), der sich alle 8 Generationen um 60 Grad dreht:

p48 Oszillator

* Aufgrund der Beschaffenheit dieser Kacheln könnte ich ein einzelnes Hexfeld auswählen, das in Rauten unterteilt ist, und es um 60 Grad drehen. Dies würde das Kacheln aperiodisch machen, ohne technisch irgendwelche Regeln zu brechen, und würde auch keinen der Oszillatoren brechen. Aber ich denke nicht, dass es im Sinne der Frage ist, also werde ich nicht versuchen, diese 40 Punkte zu beanspruchen.

Der Code basiert auf viel Code, den ich in anderen Antworten gepostet habe. Der einzigartige Teil ist

public class HexMedley3 extends AbstractLattice {
    public HexMedley3() {
        super(35, -12, 28, 24, new int[][] {
                {0, 0, 7},
                {0, 7, 7},
                {0, 7, 0},
                {0, 0, -7},
                {0, -7, -7},
                {0, -7, 0},

                {0, 0, 7, 7},
                {7, 7, 14, 14},
                {7, 14, 7, 0},

                {7, 14, 21, 14},
                {14, 21, 21, 14},
                {14, 14, 7, 7},

                {7, 14, 14, 7},
                {7, 14, 7, 0},
                {7, 0, 0, 7},

                {0, 0, -7, -7},
                {-7, -7, -14, -14},
                {-7, -14, -7, 0},

                {-7, -14, -21, -14},
                {-14, -21, -21, -14},
                {-14, -14, -7, -7},

                {-7, -14, -14, -7},
                {-7, -14, -7, 0},
                {-7, 0, 0, -7},

            }, new int[][] {
                {0, 8, 4},
                {0, 4, -4},
                {0, -4, -8},
                {0, -8, -4},
                {0, -4, 4},
                {0, 4, 8},
                {8, 16, 20, 12},
                {12, 20, 16, 8},
                {12, 8, 4, 8},
                {4, 8, 4, 0},
                {0, 4, -4, -8},
                {0, -8, -4, 4},
                {-4, -8, -16, -12},
                {-12, -16, -20, -16},
                {-12, -16, -8, -4},

                {-8, -16, -20, -12},
                {-12, -20, -16, -8},
                {-12, -8, -4, -8},
                {-4, -8, -4, 0},
                {0, -4, 4, 8},
                {0, 8, 4, -4},
                {4, 8, 16, 12},
                {12, 16, 20, 16},
                {12, 16, 8, 4},
            });
    }

    @Override
    public boolean isInterestingOscillationPeriod(int period) {
        return period != 2 && period != 4;
    }
}

0

Rechtecken mit einer Breite von 2 Zeile in Python 3, +2

Die Form dieses Gitters ist wie folgt:

 ______________
[______________]
[______][______]
[__][__][__][__]
[][][][][][][][]

Zufälligerweise hat jede Zelle in diesem Raster 8 Nachbarn, genau wie die ursprüngliche quadratische Kachel des Spiels des Lebens.

Leider hat diese Kachelung die schreckliche Eigenschaft, dass jede Zelle nur zwei nördliche Nachbarn hat. Das heißt, ein Muster kann sich niemals nach Süden ausbreiten, auch nicht nach Südosten oder Südwesten. Diese Eigenschaft führt zu einer Situation, die Oszillatoren eher unwahrscheinlich macht, obwohl es möglicherweise solche gibt, die Wände auf zwei Seiten und blinkende Zellen in der Mitte haben.

Es scheint auch die Eigenschaft zu haben (ich bin mir noch nicht zu 100% sicher), dass kein Muster wachsen kann, wenn man sich nach Norden bewegt. Eine Zeile wird niemals eine größere maximale Anzahl von Zellen aufweisen als die Zeile darunter. Ich denke das bedeutet keine Segelflugzeuge oder kompliziertere Formen.

Damit haben wir einen knappen Bonus von +2 für eine Vielzahl von Stillleben, von denen dies nur eine kleine Auswahl ist:

AA__
_BC_

AABB
_CD_

AA__BB
_CXXD_ <-- XX can be any multiple of 2 wide

____YYYY____
__AA____BB__
___CXXXXD___ <-- XX can be any multiple of 4 wide

____YYYYOOOO <-- OOOO can continue to the right and could be the bottom of a stack of this pattern
__AA____BB__
___CXXXX____ <-- XX can be any multiple of 4 wide

OOOOYYYYOOOO <-- same stackability as above
__AA____BB__
____XXXX____ <-- XX can be any multiple of 4 wide

Hier ist der Code, der beim Ausführen ein Raster mit 8 Zeilen zeichnet (1 Zelle in der oberen Zeile, 128 Zellen in der unteren Zeile). Jede Taste rückt einen Schritt vor, mit Ausnahme rder Randomisierung der Karte und dem qVerlassen des Programms.

#!/usr/bin/env python3

import random
import readchar

class board:
  def __init__(self, rows = 8):
    if rows>10:
      raise ValueError("Too many rows!")
    self.rows = rows
    self.cells = [[cell() for c in range(int(2**(r)))] for r in range(rows)]
  def __str__(self):
    out = []
    for r,row in enumerate(self.cells):
      out.append(''.join([str(row[c])*(2**(self.rows-r-1)) for c in range(len(row))]))
    return "\n".join(out)
  def randomize(self):
    for row in self.cells:
      for c,cel in enumerate(row):
        row[c].state = random.choice([True,False])
  def state_at(self,r,c):
    if r==None or c==None:
      raise TypeError()
    if r<0 or c<0:
      return False
    if r>=self.rows:
      return False
    if c>=len(self.cells[r]):
      return False
    return self.cells[r][c].state
  def tick(self):
    new_cells = [[cell() for c in range(int(2**(r)))] for r in range(self.rows)]
    for r,row in enumerate(self.cells):
      for c,cel in enumerate(row):
        # print(f"cell {r} {c}")
        cur = cel.state
        # print(cur)
        neighbors = 0
        # same row, left and right
        neighbors += self.state_at(r,c-1)
        neighbors += self.state_at(r,c+1)
        # straight up
        neighbors += self.state_at(r-1,int(c/2))
        # straight down
        neighbors += self.state_at(r+1,c*2)
        neighbors += self.state_at(r+1,c*2+1)
        # down left
        neighbors += self.state_at(r+1,c*2-1)
        # down right
        neighbors += self.state_at(r+1,c*2+2)
        if c%2==0:
          # up left
          neighbors += self.state_at(r-1,int(c/2)-1)
        else:
          # up right
          neighbors += self.state_at(r-1,int(c/2)+1)
        # print(neighbors)
        if cur:
          if neighbors<2 or neighbors>3:
            # print("turn off")
            new_cells[r][c].state = False
          else:
            new_cells[r][c].state = True
          continue
        if neighbors==3:
          # print("turn on")
          new_cells[r][c].state = True
          continue
        new_cells[r][c].state = False
        continue
    self.cells = new_cells

class cell:
  def __init__(self, state = False):
    self.state = state
  def __str__(self):
    return self.state and "X" or "_"

b = board(8)
b.randomize()
print(b)
while(1):
  i = readchar.readchar()
  if i=='q':
    break
  if i=='r':
    b.randomize()
  b.tick()
  print()
  print(b)

PS: Dieses Gitter ist das Äquivalent zu regulär in einem besonders geformten nichteuklidischen Raum :)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.