Game of Lifeを通常のグリッド以外に実装する


114

ConwayのGame of Lifeは(ほぼ)常に通常の正方形グリッドでプレイされますが、そうである必要はありません。

ConwayのGame of Lifeの標準セル隣接ルールを、正方形、三角形、六角形の規則的なタイルでないユークリッド平面の2次元タイルで実装するプログラムを作成します。

具体的には、選択したタイル...

  1. 少なくとも2つ(ただし、有限数)の異なる形状のプロトタイプが含まれている必要があります。
    • 異なる形状は、互いの縮尺または回転バージョンであり得る。
    • 穴を残さずに平面全体をタイル化できる必要があります。
    • それらは、境界が有限の単純なポリゴンでなければなりません。(それらは単純に単純ではないかもしれません。)
  2. 正方形、三角形、六角形のグリッドと同形に区別する必要があります。
    • 通常の正方形、三角形、または六角形のグリッドに簡単に沸騰するタイルは許可されません。(他のタイルでも正方形/三角形/六角形を使用できます。)
    • 2つのプロトタイル間の境界には、複数のエッジと頂点が含まれる場合がありますが、連続している必要があります。

タイルは周期的または非周期的かもしれませんが、平面全体をカバーするように拡張された場合、各プロトタイプは無限に何度も現れなければなりません。(したがって、タイルの特定の部分を「ハードコーディング」して、以下の追加のポイントを達成することはできません。)

各プロトタイプは、他のセルに隣接する1つのGame of Lifeセルを表します。

  • エッジまたは頂点を共有するセルは、隣接セルと見なされます。
  • 複数のエッジまたは頂点を共有するセルは、隣同士で1回だけカウントされます。
  • セルは隣接できません。

タイリングのインスピレーションリンク:

出力

あなたのプログラムは、ゲーム内でプレイされているGame of Lifeでのタイルのグラフィック表現を出力する必要があります。もちろん、それをimage / gif / jsfiddle形式で投稿する必要があります。

タイルの端の線を描き、死んだ細胞には明るい色を、生きた細胞には暗い色を使用してください。

得点

提出スコアは、アップ投票の数からダウン投票を引いたものに、タイルで一般的なGame of Lifeパターンを発見するための追加ポイント加えたものです。

  • 静物 -世代から世代へと変化しないパターンを見つけます。(+2)
  • 周期2〜29の発振器を見つけます(合計5周期まで、または最大+15ポイントまで、周期ごとに+3)。
  • 周期が30以上の発振器を見つけます。(+7)
  • 宇宙船を見つけてください-残骸を残さずに出発地から任意に遠く離れることができるもの。(必ずしも可動オシレーターではないかもしれません。)(+10)
  • 明らかに異なる方法で移動する別の宇宙船を見つけます(最初の宇宙船のミラーバージョンではありません)。たとえば、グライダーLWSSを参照してください。(+10)
  • 無限の成長のパターンを見つけます。成長が無限であることを証明する必要はありません。実際に確実なパターンの十分な証拠を示してください。(+25)
  • を見つける-宇宙船を永遠に生成するもの(これは無限の成長としてもカウントされます)。(+50)

無限の成長パターンは有限数の生細胞で始まり、他のパターンは常に有界の数の生細胞を含んでいる必要があります(たとえば、宇宙船は時間とともに勝手に大きくなってはいけません)。

非周期的タイルの性質により、これらのパターンの多くはそれらに実装することは不可能であると思われます。そのため、検証可能な非周期的タイルは自動的に+40ポイントを獲得します。非周期的タイルのある場所で機能するパターンは、他の場所で機能する必要はありません。

各ボーナスは1回のみ適用できます。当然、出力を確認して確認する必要があります。最高のスコアが勝ちます。

ノート

  • 各回答には、1つの特定のタイルにのみボーナスを適用できます。(関連するタイルを自由に含めることができます。)
  • ゲームオブライフのルールは次のとおりです。
    1. 2未満または3を超えるライブネイバーを持つライブセルはすべて死にます。
    2. 正確に3つのライブネイバーを持つ死んだセルが生き返ります。
    3. 他のセルは変更されません。
  • 追加のポイントのパターンは、境界条件に関係なく使用できるはずですが、それ以外の場合は、必要な境界条件を選択できます。
  • デフォルトでは、背景はすべて無効なタイルである必要があります。

Peter Taylor、Jan Dvorak、およびgithubphagocyteに、タイリングを許可する際に抜け穴を打破するのを助けてくれたことに感謝します。

(誰かが好奇心がある場合、これは間違いなく私のお気に入りの私のお気に入りです。)


7
規則正しい正方形グリッド上にない場合、ConwayのLifeではなく、Life-likeオートマトンであるという強力なケースがあります。確かに「ConwayのGame of Lifeの標準ルール」について話し、すべてのセルに8つの隣人がいるタイルを除外する場合、矛盾表現を求めています。
ピーターテイラー

2
@PeterTaylorこれは、この文脈では混乱することは想像できないが、マーティンの提案とともに変更したことを確認するために、かなり意味論的な違いです。
カルビンの趣味

4
ユークリッド平面を並べる必要がありますか?
ジョン・ドヴォルザーク

3
また、「トポロジー的に明確な」状態は、それぞれの上端から三角形のくさびが除去された正方形のグリッドによって標準的な生命の直接移植を可能にする巨大な抜け穴を残します。結果は、三角形と正方形マイナス三角形のタイリングです。各三角形には隣人用の2つの正方形があり、各正方形には2つの三角形と8つの正方形があり、三角形は単純に無視できます。それは安い10230ポイントの基本スコアです。
ピーターテイラー

4
すぐに整理できないことがまさにそれを閉じる理由です。投稿された回答を先取りして、修正を妨げます。
ピーターテイラー14

回答:


82

Pythonのペンローズロンビー、+ 97ポイント

2つの異なる形状の菱形で構成され、頂点ごとに3〜8のペンローズタイルを選択しました。このペンローズのタイルは、他の場所で非周期的であることが証明されています。シミュレーションはグラフィカル(pygame経由)でインタラクティブです。コメントは、アルゴリズムの実装が別のソースから取得されたコード内の2つの場所を示します。

p12発振器で終わるペンローズの生命のアニメーション

多くの小さな近所の静物画があります。

ペンローズ生活の静物 ペンローズ生活の静物 ペンローズ生活の静物

4つの「オン」近傍を持つ頂点は静物です:

ペンローズの生活の中で蝶のある静物 ペンローズ生活のとがった静物 ペンローズ生活のパックマン静物

死んだ内部セルがループ上の3つのセルに触れないループも静物です。

ペンローズ生活のループ静物 ペンローズ生活のループ静物

さまざまな周波数の発振器があります。

p2:(多くのバリエーション)

ペンローズ生活の期間2発振器

p3:

ペンローズ生活の期間3発振器

p4:

ペンローズ生活の期間4発振器 ペンローズ生活の期間4発振器 ペンローズ生活の期間4発振器

p5:

ペンローズ生活の期間5発振器

p6:

ペンローズ生活の期間6発振器

p7:

ペンローズ生活の期間7発振器 ペンローズ生活の期間7発振器

p12:

ペンローズ生活の期間12発振器

p20:

ペンローズ生活の期間20発振器

書かれている規則と説明のほとんどは、計画外の非周期的タイリングでのグライダーや銃の使用を許可していません。それは無限の成長を残し、私はそうは思わないだろうし、p30 +オシレーターはほぼ確実に存在するが、見つけるにはしばらく時間がかかるだろう。

python penrose-life.pyランダムに色付けされた単一の定期的なタイルを生成する python -O penrose-life.py./penrose-life.py、実際にシミュレーションを実行します。実行中にオシレーターを識別しようとし、1つ(p> 2)が見つかるとスクリーンショットを表示します。オシレーターまたはストールしたボードを記録した後、ボードはランダム化されます。

シミュレーションでセルをクリックすると、セルが切り替わります。

シミュレーションには、次のキーボードショートカットがあります。

  • エスケープ-プログラムを終了します
  • スペース-ボード全体をランダム化
  • P-シミュレーションを一時停止します
  • S-シングルステップシミュレーション
  • F-「高速」モードを切り替えて、25フレームごとにレンダリングする

ペンローズタイリングアルゴリズムの最初のシードは、10個の狭い三角形の円です。これは、単一の三角形、または対称または非三角形の異なる配置に変更できます。

ソース:

#!/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
私はすぐに私がこの記事を読んだので、これについて考えていた:newscientist.com/article/...私はすぐに50ポイントを得ることができたと。そのアイデアから拡張できますか?編集:ああ、元のGame of Lifeルールを使用する必要があることに気付きました。
ちょうど半分14

49

C ++ w / OpenGL(+17)

そこで、3面体の凸五角形グリッドを試しました。私のために動作します;)グリッドが無限ではないことを除いて、ライフルールの標準ゲームが適用されます-画像の外側に境界セルがあります。細胞の30%は最初は生きています。

これは、グリッドがどのように見えるかです:

ここに画像の説明を入力してください

ライブバージョン:

青い細胞は生きており、白い細胞は死んでいます。赤血球が死んだばかりで、緑が生まれたばかりです。画像内のアーチファクトはgif圧縮の結果であることに注意してください。SOは10MBのgif :(が好きではありません。

ここに画像の説明を入力してください

静物:(+2)

ここに画像の説明を入力してください

オシレーターT = 2、T = 3、T = 12:(+9)

ここに画像の説明を入力してください ここに画像の説明を入力してください

オシレーターT = 6、T = 7:(+6)

ここに画像の説明を入力してください

さらに多くの異なるオシレーターがあります...しかし、グリッドは船にとって十分に規則的ではないようです...

これは何も(ポイントなし)ですが、私はそれが好きです:

ここに画像の説明を入力してください

コードは混乱です:)いくつかの古い固定OpenGLを使用します。それ以外の場合は、gifエクスポートにGLEW、GLFW、GLMおよびImageMagickを使用しました。

/**
 * 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
とてもかっこいい!しかし、細胞の23%が最初は生きているとはどういう意味ですか?私があなたを誤解しているだけで申し訳ありませんが、ルールの1つがありますBy default the background should be all dead tiles.(したがって、グリッドに無限の数のライブタイルをシードすることはできません)。
カルビンの趣味14

1
@ Calvin'sHobbies:従うかどうかわかりません。何らかの初期構成を設定する必要があります...すべてのセルが最初に死んでいる場合、何も起こりません。
Jaa-c 14

1
もちろん。たとえば、宇宙船が動作するために、事前に初期化された無限のタイル列に依存している場合にのみ言及しています。ランダムアニメーション用にタイルの23%を初期化しているだけなので、心配しないでください。ここでは問題ありません。
カルビンの趣味14

2
あなたの大きな発振器はポイントに値するようになりました:)
カルビンの趣味

1
@ Calvin'sHobbies:残念ながら、コードにバグが見つかったため(新しい世代と古い世代の状態が混在していたため)、オシレーターは無効になりました。
Jaa-c 14

38

行く?ポイント

そのため、特定のタイルにピン留めするのではなく、タイルのgifまたはpngを受け取り、その上で動作するプログラムを作成しました。gif / pngは、すべてのタイルに単一の色を使用する必要があります。

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()
}

それから私はただウェブに行って、いくつかの楽しいタイリング画像をつかんで、それらの上でプログラムを走らせました。

go run life.go penrose1.go

指定されたタイルの100段階の寿命シミュレーションを含む「animated.gif」というファイルを生成します。

標準寿命:

ここに画像の説明を入力してください ここに画像の説明を入力してください

ペンローズタイル:

ここに画像の説明を入力してください ここに画像の説明を入力してください

ここに画像の説明を入力してください ここに画像の説明を入力してください

上記のものには、期間12の発振器があります。

ここに画像の説明を入力してください ここに画像の説明を入力してください

上記の期間3のオシレーターがあります。


7
非常にクールなアイデアですが、少なくとも最後の例では、アルゴリズムがコーナーネイバーを適切に処理するとは思いません。周期3のオシレーターに3つのタイルが近接している場合、それらの3つのタイルはすべて隣接しているため、その頂点の他の9つのタイルはアライブになります。i.stack.imgur.com/veUA1.pngの青いタイルを参照してください。
カルビンの趣味14

33

Java-11(イッシュ)ポイント

完全に(ほとんど)機能するインタラクティブな環境が付属しています!

編集

致命的な欠陥の発見:(

生きている領域のパスは、それが最初に形成された領域によって制限されます。正方形-二重五角形の障壁を通過するには、反対側に事前にシェーディングされた領域が必要です。これは、その下の各図形がその上の2つの領域のみに触れるためです。これは、宇宙船や何も拡大しないことを意味し、可能性を制限します。別のパターンで試してみます。

しかし!!!まだ試してみたいなら... ここで試してみてください

発振器

ここに画像の説明を入力してください

これを何と呼ぶか​​わからない-別のオシレーター

ここに画像の説明を入力してください

これは忍者の星に少し似ています-静物

ここに画像の説明を入力してください

これはハエのように見える-静物

ここに画像の説明を入力してください

別の発振器

ここに画像の説明を入力してください

編集

別の発振器が見つかりました。これをイーグルと名付けています。

ここに画像の説明を入力してください

おい!別の発振器!(期間4)風車。

ここに画像の説明を入力してください

2期間1。

ここに画像の説明を入力してください

外側を内側から絶縁する構造があるようです。これ(および前の例)はそれを使用します。ボックスを破ることができる唯一のことは、境界の正方形の1つが最初に(これまで)生きている場合です。ちなみに、これはウインカー-期間2です。

ここに画像の説明を入力してください

これをEclipseで作成しましたが、複数のファイルがあります。どうぞ。

メインクラス-

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++;
        }
    };
}

Canvasクラス-

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クラス-

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クラス-

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;
    }
}

そして最後に...座標クラス

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

4
その2番目は間違いなく幸せな小さなシールです。
マーティンエンダー14

誰もが私がjarファイルを投稿する方法を知っているので、人々は私のデザインを簡単に試すことができますか?
ストレッチマニア14

3
私は風車のカーソルが好きです。
cjfaure 14

10
「風車」はナチスの蟻の行進に似ています
ベベ14

1
カーソルもイーグルにあります。最初は混乱しました。
mbomb007

25

Python

メタタイルに複数のポイントを配置し、それを長方形または六角形のタイルで定期的にコピーします(メタタイルはオーバーラップできます)。すべての点のセットから、グリッドを構成するボロノイ図を計算します。

いくつかの古い例

ランダムグラフ、Delaunay trinagulationが表示されます。これは、近隣を見つけるためにも内部的に使用されます

生命のグラフ

綴る定期的なタイル GoL

ここに画像の説明を入力してください

静物を示すいくつかのグリッド

ここに画像の説明を入力してください

そのようなグリッドには、さまざまなサイズのいくつかの小さな静物といくつかの小さな2、3または5サイクルの発振器がありますが、おそらくグリッドの不規則性のためにグライダーは見つかりませんでした。セルの周期的な振動をチェックすることにより、生命体の検索を自動化することを考えています。

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
興味深いアイデアですが、ランダムなタイリングには、限られた数のプロトタイプがありません。定期的なタイリングでは、1つの配置を選択し、すべてのオシレーターと素材の作成方法を明示的に示す必要があります。
カルビンの趣味14

グラフは世界地図(例えば、都市)に基づいている場合、それはクールになる
明唐

@SHiNKiROU素晴らしいアイデアです。地理マップを操作するためのpythonパッケージを見たことを覚えています。特に1つのグリッドに収まらないので、これを実行します。
DenDenDo

そのような場合、接続グラフが平面ではない場合でも、共有された頂点で十分であるはずであるが、エッジを共有する場合にのみセルを隣接として処理していると思います。例えば。1つの頂点を共有する5つのセルは、接続グラフでK_5を形成します。
例14

確かに、それらは頂点で接続されている場合がありますが、セル+リンクを非接続にする場合があります。頂点。しかし幸いなことに、これはセルをわずかに非対称にすることで簡単に回避できます。
DenDenDo 14

21

Javascript [25+?]

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

ここに画像の説明を入力してください

ハウステッセレーション!「家」と「逆さまの家」の2つの形状があり、それぞれに7つの隣人がいます。

現在、私は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)

あなたがそれらを見つけた場合のグラブのためのパターンの命名権:p

静物-スター
星

2ステージオシレータ-「Beacon」:isaacgによって発見
2stagOscillator

宇宙船-「ヒキガエル」:isaacgが発見
ここに画像の説明を入力してください

グライダー-名前:MartinBüttnerにより発見
ここに画像の説明を入力してください

フィドルは現在、初期状態としてランダムに世界を埋めるように設定されています。

コード:

// 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
GoLビーコンに基づいた発振器を見つけました。あなたのフィドルに次のように貼り付けます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;
isaacg

@isaacg写真を追加し、フィドルに含めました。名前を付けますか?
ケビンL 14

私はそれをビーコンと呼びます。GoLビーコンとあまりにも似ているため、他のものと呼ぶことはできません。
isaacg 14

5
グライダーを見つけました!ヒキガエルの体のように見えるので、私はそれをヒキガエルと呼びたいです。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もう一度見つけました!そして今回、私はそれを捕まえました;)。それは実際にはあなたの変種に過ぎませんが、さらに2つの後続のライブセル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;があります。
マーティンエンダー14

20

Javascript [27+?]

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

ラウンド2!六角形、正方形、三角形が追加されました。また、

このバージョンでは、タイルをクリックして状態を切り替えることができます。注:sクリックイベントは整数として追跡されますが、計算は浮動小数点値で行われるため、一部のクリック処理は、特にの低い値の場合、少し不安定になる場合があります

ここに画像の説明を入力してください

現在のスコア-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

期間4の発振器:マーティン・ブットナーが発見
ここに画像の説明を入力してください

期間6発振器:マーティン・ブットナーが発見
ここに画像の説明を入力してください

期間10のオシレーター:MartinBüttnerにより発見
ここに画像の説明を入力してください

期間12の発振器:マーティン・ブットナーが発見
ここに画像の説明を入力してください

期間20の宇宙船:マーティン・ブットナーが発見
ここに画像の説明を入力してください


6
期間20のグライダー/宇宙船を見つけた: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;
マーティンエンダー14

同じ宇宙船のもう1つの非常に興味深い開始形状は、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;3つの周期2発振器のみで構成されているためです。
マーティンエンダー14

期間4のオシレーター(何らかの助けがある場合)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;
マーティン・エンダー14

そして私が限りない成長や垂直宇宙船のように見えるものに一番近づいたのは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;。多分それは誰かが動作するバリエーションを見つけるのに役立ちます。今のところ十分です
マーティンエンダー14

周期6オシレーター: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;境界上にある場合、サイズの半分でも動作します。
マーティンエンダー14

16

カイロ五角形タイル(+汎用フレームワーク)、17 +ポイント

このタイリングが描き驚くほど簡単です:キーはそれを描画するために重要であるだけで無理数は、ということであるsqrt(3)、合理的な数に非常に近い7/4あなたが引く場合という追加ボーナスがある、1あなたが得る分子と分母からの6/3 = 2そう、軸に沿っていない線がきれいに対称であること。

グリッドペーパーが必要な場合は、A4用のPostScript要点を作成しました。他の用紙サイズの場合は、気軽にフォークしてください。

コードは、他のタイルをサポートするのに十分な汎用性があります。実装する必要があるインターフェイスは次のとおりです。

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);
}

カイロのタイルは次のとおりです。

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;
    }
}

制御コードは

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;
    }
}

任意の頂点が静物を生成します(2ポイント):

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

静物

オシレーター(15ポイント):左上から時計回りに2、3、4、6、11、12のオーダーがあります。

各種発振器


私はカメを見ることができません。
クエンティン14

@Quentin、私のp3オシレーターのニックネームはebolaです。あなたはもつれた頭と尾を持っています。
ピーターテイラー14

私はp2について考えていました。絶え間なく反転するカメのように見えます。
クエンティン14

p4のものも泳ぐカメのように見えます。
ロスプレッサー

16

ロンビレ(30ポイント以上)

このグリッドの接続性は非常に高く(各セルには10個の隣人がいます)、奇妙なことに、これは死よりも出生により効果的に貢献しているようです。ほとんどのランダムグリッドは、無限の成長(25ポイント)をトリガーするようです。たとえば、この5セルの開始位置:

開始位置

300を超える世代が巨大なものに進化します。

その開始位置の進化

人口は少なくとも3000世代にわたって世代とともに二次的に増加します。

おそらくこれが、周期2(3ポイント)の発振器を 1つだけ見つけた理由です。

3セル発振器

静物(2点):単一頂点の周りの任意の4個の細胞を取ります。

コード(AbstractLattice以前の回答で投稿した汎用フレームワークとクラスで使用):

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

菱形六角形のタイル、17ポイント以上

MartinBüttnerの要求どおり。

静物(2ポイント):

2つのループを持つチェーン

周期の発振器(左上から時計回り)2、4、5、6、11(15ポイント):

さまざまな発振器

一般に、発振器には、変化するセルのセット(コア)、コアに隣接するセルのセット(クラッド)、およびクラッドの変化を防ぐセルのセット(サポート)があります。このタイリングでは、オシレーターのサポートが重複する場合があります。

オーバーラップをサポートする4オシレーターと5オシレーター

4オシレーターが取り外された場合、5オシレーターのサポートは失敗し、最終的に2オシレーターに進化します。しかし、5オシレーターが取り外された場合、4オシレーターのサポートは1ヘクスを追加して安定させるだけなので、これは実際には20オシレーターではありません。


このタイリングを実装するコードは非常に汎用的です:非周期的なタイリングの経験に基づいて、既知の境界に拡張し、頂点によるルックアップを行うことは、単純なラティスには効率的ではないかもしれませんが、非常に柔軟な手法であることに気付きました。しかし、より複雑なラティスに興味があるため、ここではそのアプローチを採用しました。

すべての周期的なタイルは格子であり、2つの軸に沿って繰り返される基本単位(このタイルの場合は六角形、2つの三角形、および3つの正方形)を識別することができます。次に、基本単位のプリミティブセルの軸オフセットと座標を指定するだけで完了です。

このコードはすべて、https: //gist.github.com/pjt33/becd56784480ddd751bfでzipとしてダウンロードできGenericLifeGuiます。これには、このページには掲載していないものも含まれています。

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;
    }
}

これに対するサポートは、以前に投稿した汎用フレームワークとAbstractLatticeクラスです:

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);
        }
    }
}

数時間のCPU時間の後、7オシレーターと15オシレーターに加えて、いくつかの興味深いオシレーターペアを追加し、それらが安定性を維持するいくつかのセルを共有するようにしました。
ピーターテイラー14

そして、7オシレーターを手動で微調整することで、偶然3オシレーターを作成しました。これは、ランダム検索の効果について教えてくれます。
ピーターテイラー14

13

非周期的なラビリンスタイル(45ポイント以上)

これは、以前の回答の汎用フレームワークを使用しています。

静物(2ポイント):

ラビリンスの静物:12の頂点で4つの三角形が交わる

発振器(3点):

発振器の画像

この発振器は非常に一般的であり、ほとんどのランダムな開始点の結果として現れます。

コード:

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

7次元格子のペンローズ風投影(64ポイント以上)

これは、(ペンローズ・タイルを置き換える得るためにペンローズ・タイルに似ているN = 7N = 5)との資格を非周期的ボーナス(40ポイント)。

静物(2点):プロトセルが凸状であるため、3次以上の任意の頂点で十分なので、些細なことです。(順序3の場合はすべての面を選択し、そうでない場合は4つの面を選択します)。

短周期発振器(15ポイント):

このタイルにはオシレーターが豊富です。発振器を1つしか見つけられなかった最小の期間は11であり、1つしか見つけられなかった最小の期間は13です。

p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12

長周期発振器(7ポイント):

回転対称性を持つこのタイルのバリエーションの1つを意図的に選択しました。28世代ごとに中心点の7分の1の回転を行い、p196になります。

p196

コードは、以前の回答で投稿したフレームワークと次のタイルクラスを使用します。

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、ポイント-現在11

これは、致命的な欠陥がないことを除いて、上記の新しい改良バージョンです!

ここで試してみてください、今ではランダムなボタンで!(より多くの塗りつぶしを得るために数回押します)また、スピードボタンが含まれています。

最初の、周期4の発振器、3ポイント

ここに画像の説明を入力してください

次に、2 3周期2オシレーター-3ポイント

ここに画像の説明を入力してください

ここに画像の説明を入力してください

ここに画像の説明を入力してください

MartinBüttnerの好意による2個の2周期オシレーター(oooohhhhhhh ... color)

ここに画像の説明を入力してください

ここに画像の説明を入力してください

ランダムに継続的に実行するプログラムを作成し、振動を探しました。これを見つけました。期間5 +3ポイント

ここに画像の説明を入力してください

そして、ランダマイザーによって見つかった別の期間5。

ここに画像の説明を入力してください

そしてもちろん、静物(例として、たくさんあります)2点

ここに画像の説明を入力してください

コード-メインクラス

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++;
        }
    };
}

キャンバス -

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;
    }
}

座標-

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;
    }
}

誰かが見つけたら、それらは言及されます。(これは私に思い出させます:私の兄弟は最初の2つの発振器を見つけました)



10

Javascript、HexagonSplit

免責事項:domの操作が多いため非常に遅く、おそらくラップアラウンドしないようにx軸のバグ修正が必要です。

フィドル

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

Fiddleでアクティブセルを切り替えることができるようになりました。

まだ生きている

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください

発振器

2段階 2段階

宇宙船(2フェーズ、2種類)

2段階 最初のバリアント

Spacehip(4フェーズ)

ここに画像の説明を入力してください

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ポイント以上)

小花の五角形のタイルにインスパイアされた:7つの六角形のブロックが平面にタイルを張り、さまざまな方法で六角形を切り刻むことができます。名前が示すように、これは私が試したこのような3番目のバリエーションですが、p30 +オシレーターの7ポイントを主張する最初のタイリングであるため、投稿する価値があります。

タイリングは次のとおりです。

7つの六角形の内側は、6つの正三角形に分割されます。 外側の6個をそれぞれ3つの菱形に、交互パリティ

プロトセルは凸状であるため、次数3の頂点は静物(2ポイント)を与えます。

周期2、3、4、6、12の5つの小周期発振器(15ポイント)を見つけました。

p2発振器 p3発振器 p4発振器 p6発振器 p12発振器

そして、ピエース・ド・レジスタンス:8世代ごとに60度回転するp48オシレーター(7ポイント):

p48発振器

*このタイルの性質を考えると、菱形に分割された単一のヘックスを選択し、60度回転させることができます。これにより、技術的にルールを壊すことなくタイリングが非周期的になり、オシレーターも破損しません。しかし、私はそれが質問の精神にあるとは思わないので、これらの40点を主張しようとはしません。

コードは、他の回答で私が投稿した多くのコードに依存しています。ユニークな部分は

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

Python 3 の幅2 行の長方形、+ 2

このグリッドの形状は次のとおりです。

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

偶然にも、このグリッドの各セルには、ゲームオブライフの元の正方形のタイルのように、8つの隣接セルがあります。

残念ながら、このタイリングには、各セルに2つの北隣だけがあるという恐ろしい特性があります。つまり、パターンは、南東または南西を含めて、南向きに伝播することはできません。この特性は、オシレーターをかなりありそうもない状況に導きますが、2つの側面に壁があり、中央にセルが点滅するようなものが存在する場合があります。

また、北に移動している間はパターンが大きくならないという特性があるようです(まだ100%は確信していません)。行は、その下の行よりもセルの最大範囲数が広くなることはありません。それはグライダーやより複雑なフォームがないことを意味すると思います。

これにより、さまざまな静物画に対してわずかに+2のボーナスが得られますが、これらはほんの一例です。

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

コードは次のとおりです。実行すると、8行のグリッド(一番上の行に1セル、一番下の行に128セル)が描画されます。rボードをランダム化しqてプログラムを終了する場合を除き、どのキーでも1ステップ進みます。

#!/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:このグリッドは、特別な形状の非ユークリッド空間におけるレギュラーと同等です:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.