Thực hiện Trò chơi cuộc sống trên bất cứ điều gì ngoại trừ một lưới thông thường


114

Trò chơi Cuộc sống của Conway (hầu như) luôn được chơi trên một ô vuông thông thường, nhưng không cần thiết phải như vậy.

Viết chương trình thực hiện các quy tắc lân cận ô tiêu chuẩn từ Trò chơi cuộc sống của Conway trên một lát gạch hai chiều của mặt phẳng Euclide không phải là một lát vuông thông thường, hình tam giác hoặc hình lục giác .

Cụ thể, ốp lát bạn chọn ...

  1. Phải chứa ít nhất 2 (nhưng rất nhiều) prototile có hình dạng khác nhau .
    • Các hình dạng khác nhau có thể được thu nhỏ hoặc xoay phiên bản của nhau.
    • Họ phải có thể lát toàn bộ mặt phẳng mà không để lại lỗ.
    • Chúng phải là đa giác đơn giản với chu vi hữu hạn. (Chúng có thể không đơn giản yếu.)
  2. Phải khác biệt về mặt hình học với các lưới hình vuông, hình tam giác và hình lục giác.
    • Bất kỳ ốp lát nào tầm thường luộc xuống một hình vuông, hình tam giác hoặc hình lục giác thông thường đều không được phép. (Bạn vẫn có thể sử dụng hình vuông / hình tam giác / hình lục giác trong các góc khác.)
    • Đường viền giữa hai prototin bất kỳ có thể chứa nhiều cạnh và đỉnh, nhưng nó phải liên tục.

Ốp lát của bạn có thể là định kỳ hoặc định kỳ, nhưng khi mở rộng để bao phủ toàn bộ mặt phẳng, mỗi prototile phải xuất hiện vô hạn nhiều lần. (Vì vậy, không "mã hóa" một số phần nhất định của ốp lát của bạn để giúp đạt được các điểm bổ sung bên dưới.)

Mỗi prototiles của bạn đại diện cho một ô Trò chơi Sự sống lân cận các ô khác:

  • Các tế bào chia sẻ bất kỳ cạnh hoặc bất kỳ đỉnh được coi là hàng xóm.
  • Các ô chia sẻ nhiều cạnh hoặc đỉnh vẫn chỉ được tính ở các hàng xóm khác một lần.
  • Các tế bào không thể hàng xóm mình.

Liên kết cảm hứng ốp lát:

Đầu ra

Chương trình của bạn sẽ xuất ra một số loại biểu diễn đồ họa cho ốp lát của bạn với Trò chơi cuộc sống đang được chơi trong đó, tất nhiên bạn nên đăng ở định dạng hình ảnh / gif / jsfiddle.

Vui lòng vẽ các đường viền của gạch và sử dụng màu sáng cho các ô chết và màu tối cho các ô sống.

Chấm điểm

Điểm số gửi của bạn là số lượng upvote trừ đi downvote, cộng thêm điểm để khám phá các mẫu Game of Life phổ biến trong ốp lát của bạn:

  • Tìm một cuộc sống tĩnh lặng - một mô hình không thay đổi từ thế hệ này sang thế hệ tiếp theo. (+2)
  • Tìm các bộ dao động có chu kỳ 2 đến 29. (+3 cho mỗi khoảng thời gian bạn tìm thấy tối đa tổng cộng 5 tiết hoặc tối đa +15 điểm)
  • Tìm một bộ dao động có chu kỳ từ 30 trở lên. (+7)
  • Tìm một con tàu vũ trụ - thứ gì đó có thể tự ý đi xa khỏi vị trí bắt đầu của nó mà không để lại bất kỳ mảnh vỡ nào. (Nó có thể không nhất thiết là một bộ dao động chuyển động.) (+10)
  • Tìm một tàu vũ trụ khác di chuyển theo một cách khác biệt (và không phải là phiên bản được nhân đôi của tàu vũ trụ đầu tiên), ví dụ, xem tàu lượnLWSS . (+10)
  • Tìm một mô hình tăng trưởng vô hạn . Bạn không cần phải chứng minh rằng sự tăng trưởng là vô hạn, chỉ cần cho chúng tôi thấy đủ bằng chứng về mô hình mà nó thực sự chắc chắn. (+25)
  • Tìm một khẩu súng - thứ tạo ra tàu vũ trụ mãi mãi (điều này cũng được tính là sự tăng trưởng vô hạn). (+50)

Các mẫu tăng trưởng vô hạn phải bắt đầu bằng số lượng tế bào sống hữu hạn và các mẫu khác phải luôn chứa một số lượng tế bào sống bị ràng buộc (ví dụ, một tàu vũ trụ không nên phát triển lớn tùy ý theo thời gian).

Do tính chất của một chu kỳ nghiêng, có vẻ như nhiều mô hình trong số này sẽ không thể thực hiện được trong đó. Vì vậy, bất kỳ ốp lát định kỳ có thể tự động được +40 điểm. Một mô hình hoạt động ở một nơi trong một lát gạch định kỳ không phải làm việc ở những nơi khác.

Mỗi phần thưởng chỉ có thể được áp dụng một lần. Đương nhiên, chúng ta sẽ cần phải xem đầu ra để xác minh chúng. Điểm cao nhất sẽ thắng.

Ghi chú

  • Mỗi câu trả lời chỉ có thể có tiền thưởng áp dụng cho một ốp lát cụ thể. (Mặc dù cảm thấy tự do để bao gồm các nghiêng liên quan.)
  • Luật chơi của cuộc sống như sau:
    1. Bất kỳ tế bào sống nào có ít hơn 2 hoặc nhiều hơn 3 hàng xóm sống đều chết.
    2. Bất kỳ tế bào chết với chính xác 3 hàng xóm sống trở lại.
    3. Các tế bào khác không thay đổi.
  • Các mẫu cho các điểm bổ sung nên có thể bất kể điều kiện biên, nhưng nếu không, bạn có thể chọn bất kỳ điều kiện biên nào bạn muốn.
  • Theo mặc định, nền nên là tất cả các gạch chết.

Cảm ơn Peter Taylor, Jan Dvorak và githubphagocyte vì đã giúp ngăn chặn những sơ hở trong những gì nên được cho phép.

(Trong trường hợp bất kỳ ai tò mò, đây chắc chắn là thử thách yêu thích của riêng tôi .)


7
Có một trường hợp mạnh mẽ được đưa ra là nếu nó không nằm trên một ô vuông thông thường thì đó không phải là Cuộc sống của Conway, mà là một máy tự động giống như Cuộc sống. Chắc chắn nếu bạn muốn nói về "các quy tắc tiêu chuẩn của Trò chơi cuộc sống của Conway" và loại trừ các nghiêng trong đó mỗi ô có chính xác 8 hàng xóm bạn đang yêu cầu một oxymoron.
Peter Taylor

2
@PeterTaylor Đó là một sự khác biệt về ngữ nghĩa mà tôi không thể tưởng tượng được sẽ gây nhầm lẫn trong bối cảnh này, nhưng chỉ để chắc chắn rằng tôi đã thay đổi nó (cùng với các đề xuất của Martin).
Sở thích của Calvin

4
Tôi có cần phải lát mặt phẳng euclide không?
John Dvorak

3
Tình trạng " khác biệt về mặt tôpô " của bạn cũng để lại một lỗ hổng lớn cho phép cấy trực tiếp Cuộc sống tiêu chuẩn bằng một lưới các ô vuông mỗi cạnh có một hình nêm hình tam giác được tháo ra khỏi cạnh trên của nó. Kết quả là một lát các hình tam giác và hình vuông trừ hình tam giác trong đó mỗi hình tam giác có hai hình vuông cho hàng xóm, mỗi hình vuông có hai hình tam giác và tám hình vuông, và hình tam giác có thể được bỏ qua. Đó là điểm cơ bản 10230 điểm giá rẻ.
Peter Taylor

4
Không có khả năng sắp xếp nó ngay lập tức chính xác là lý do để đóng nó. Nó trả lời trước câu trả lời được đăng mà ngăn không cho nó được sửa.
Peter Taylor

Câu trả lời:


82

Penrose rhombii trong Python, +97 điểm

Tôi đã chọn một lát gạch penrose gồm hai hình thoi có hình dạng khác nhau, đáp ứng 3-8 mỗi đỉnh. Ốp lát penrose này được chứng minh là định kỳ ở nơi khác. Mô phỏng là đồ họa (thông qua pygame) và tương tác. Nhận xét chỉ ra hai vị trí trong mã nơi thực hiện thuật toán được lấy từ một nguồn khác.

hoạt hình của cuộc sống penrose kết thúc với bộ dao động p12

Có nhiều khu phố nhỏ vẫn còn sống:

cuộc sống tĩnh lặng trong cuộc sống Penrose cuộc sống tĩnh lặng trong cuộc sống Penrose cuộc sống tĩnh lặng trong cuộc sống Penrose

Bất kỳ đỉnh với bốn hàng xóm "trên" là một cuộc sống tĩnh lặng:

bướm vẫn sống trong cuộc sống penrose cuộc sống vẫn còn gai nhọn trong cuộc sống Penrose pacman vẫn sống trong cuộc sống Penrose

Bất kỳ vòng lặp nào không có ô bên trong chết chạm vào ba ô trên vòng lặp cũng là một cuộc sống tĩnh:

vòng lặp cuộc sống tĩnh lặng trong cuộc sống penrose vòng lặp cuộc sống tĩnh lặng trong cuộc sống penrose

Có các dao động ở các tần số khác nhau:

p2: (nhiều biến thể)

dao động giai đoạn 2 trong cuộc sống penrose

p3:

dao động giai đoạn 3 trong cuộc sống penrose

p4:

dao động giai đoạn 4 trong cuộc sống penrose dao động giai đoạn 4 trong cuộc sống penrose dao động giai đoạn 4 trong cuộc sống penrose

p5:

dao động giai đoạn 5 trong cuộc sống penrose

p6:

dao động giai đoạn 6 trong cuộc sống penrose

p7:

dao động giai đoạn 7 trong cuộc sống penrose dao động giai đoạn 7 trong cuộc sống penrose

trang 12:

dao động giai đoạn 12 trong cuộc sống penrose

p20:

dao động giai đoạn 20 trong cuộc sống penrose

Các quy tắc và làm rõ như được viết hầu hết không cho phép tàu lượn hoặc súng trong một lát gạch không theo kế hoạch. Điều đó để lại sự tăng trưởng vô hạn, mà tôi cho rằng không có khả năng, và một bộ dao động p30 +, gần như chắc chắn tồn tại nhưng sẽ mất một thời gian để tìm thấy.

python penrose-life.pysẽ tạo ra một lát gạch định kỳ có màu ngẫu nhiên python -O penrose-life.pyhoặc ./penrose-life.pythực sự sẽ chạy mô phỏng. Trong khi chạy, nó sẽ cố gắng xác định các bộ dao động và khi tìm thấy một (p> 2), nó sẽ chụp màn hình nó. Sau khi ghi lại một bộ dao động, hoặc một bảng bị đình trệ, bảng được chọn ngẫu nhiên.

Nhấp vào một ô trong mô phỏng sẽ chuyển đổi nó.

Các phím tắt sau tồn tại trong mô phỏng:

  • Thoát - thoát khỏi chương trình
  • Không gian - ngẫu nhiên toàn bộ bảng
  • P - tạm dừng mô phỏng
  • S - bước đơn mô phỏng
  • F - chuyển chế độ "nhanh", chỉ hiển thị mỗi khung hình thứ 25

Hạt giống ban đầu của thuật toán ốp lát penrose là một vòng tròn gồm mười hình tam giác hẹp. Điều này có thể được thay đổi thành tam giác đơn, hoặc một sự sắp xếp khác nhau của hình tam giác, đối xứng hoặc không.

Nguồn:

#!/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
Tôi đã ngay lập tức nghĩ về điều này, bởi vì tôi đã đọc bài đăng này: newscientist.com/article/ Khăn mà tôi có thể dễ dàng nhận được 50 điểm. Bạn có thể mở rộng từ ý tưởng đó? EDIT: Ahh, mới nhận ra rằng chúng ta cần sử dụng các quy tắc Game of Life gốc.
cần

49

C ++ w / OpenGL (+17)

Vì vậy, tôi đã thử lưới ngũ giác lồi 3-Isoh thờ. Hoạt động cho tôi;) Trò chơi tiêu chuẩn về quy tắc cuộc sống được áp dụng, ngoại trừ lưới không phải là vô hạn - có các ô viền bên ngoài hình ảnh. 30% các tế bào ban đầu còn sống.

Đây là cách lưới trông như thế nào:

nhập mô tả hình ảnh ở đây

Phiên bản trực tiếp:

Tế bào màu xanh còn sống, màu trắng đã chết. Tế bào đỏ vừa chết, xanh vừa mới sinh. Lưu ý rằng các tạo phẩm trong ảnh là kết quả của quá trình nén gif, SO không giống như gif 10MB :(.

nhập mô tả hình ảnh ở đây

Cuộc sống tĩnh lặng: (+2)

nhập mô tả hình ảnh ở đây

Dao động T = 2, T = 3, T = 12: (+9)

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Dao động T = 6, T = 7: (+6)

nhập mô tả hình ảnh ở đây

Có nhiều bộ dao động khác nhau ... Nhưng dường như lưới điện không đủ thường xuyên cho một con tàu ...

Điều này không có gì (không có điểm), nhưng tôi thích nó:

nhập mô tả hình ảnh ở đây

Mã này là một mớ hỗn độn :) Sử dụng một số OpenGL cố định cổ xưa. Mặt khác, sử dụng GLEW, GLFW, GLM và ImageMagick để xuất gif.

/**
 * 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
Rất tuyệt! Nhưng ý bạn là gì khi 23% tế bào ban đầu còn sống? Xin lỗi nếu tôi chỉ hiểu nhầm bạn nhưng một trong những quy tắc là By default the background should be all dead tiles.(vì vậy bạn không thể gieo lưới với số lượng gạch sống vô hạn).
Sở thích của Calvin

1
@ Calvin'sHob sở thích: Tôi không chắc là mình theo dõi .. Bạn phải đặt một số loại cấu hình ban đầu ... Nếu tất cả các ô đã chết ngay từ đầu, sẽ không có gì xảy ra.
Jaa-c

1
Tất nhiên. Tôi chỉ đề cập đến một trường hợp, ví dụ, một tàu vũ trụ phụ thuộc vào một hàng gạch vô hạn được khởi tạo trước bên cạnh nó để làm việc. Bây giờ tôi thấy rằng bạn tình cờ khởi tạo 23% số gạch của mình cho hoạt hình ngẫu nhiên, vì vậy đừng lo lắng, không có vấn đề gì ở đây cả.
Sở thích của Calvin

2
Bộ tạo dao động lớn của bạn hiện có giá trị điểm :)
Sở thích của Calvin

1
@ Calvin'sHobbies: Thật không may, tôi vừa tìm thấy một lỗi trong mã của mình (Tôi đã trộn lẫn các trạng thái di truyền mới và cũ), vì vậy bộ tạo dao động không còn hiệu lực: / Đã sửa bây giờ.
Jaa-c

38

Đi ,? điểm

Vì vậy, thay vì đặt mình xuống một lát gạch cụ thể, tôi đã viết một chương trình lấy gif hoặc png của ốp lát và điều hành cuộc sống trên đó. Gif / png phải sử dụng một màu duy nhất cho tất cả các ô.

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

Sau đó, tôi chỉ cần truy cập web, lấy một số hình ảnh ốp lát thú vị và chạy chương trình trên chúng.

go run life.go penrose1.go

Nó tạo ra một tệp có tên là "anim.gif" chứa mô phỏng vòng đời 100 bước của lát gạch đã cho.

Cuộc sống tiêu chuẩn:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Gạch Penrose:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Trên một có một dao động của giai đoạn 12.

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Trên một có một dao động của giai đoạn 3.


7
Ý tưởng rất tuyệt vời, nhưng tôi không nghĩ thuật toán của bạn xử lý hàng xóm góc cạnh, ít nhất là trong ví dụ cuối cùng của bạn. Khi dao động giai đoạn 3 có 3 gạch gần nhau, 9 gạch còn lại ở đỉnh đó sẽ trở nên sống động vì tất cả đều lân cận 3 gạch sống. Xem các ô màu xanh tại i.stack.imgur.com/veUA1.png .
Sở thích của Calvin

33

Java - 11 (ish) điểm

Đi kèm với môi trường tương tác đầy đủ (chủ yếu)!

BIÊN TẬP

Lỗ hổng chết người được phát hiện :(

Đường đi của các khu vực còn sống được giới hạn bởi khu vực ban đầu được hình thành. Để vượt qua hàng rào hình vuông - hình ngũ giác kép, người ta phải có một khu vực được tô bóng trước ở phía bên kia. Điều này là do mỗi hình dạng bên dưới nó chỉ chạm vào 2 trong số các khu vực phía trên nó. Điều này có nghĩa là không có tàu vũ trụ hoặc mở rộng bất cứ điều gì, loại giới hạn các khả năng. Tôi sẽ thử với một mô hình khác.

NHƯNG!!! nếu bạn vẫn muốn thử nó ... hãy thử nó ở đây .

dao động

nhập mô tả hình ảnh ở đây

Không biết gọi cái này là gì - một bộ dao động khác

nhập mô tả hình ảnh ở đây

Cái này trông hơi giống một ngôi sao ninja - cuộc sống tĩnh lặng

nhập mô tả hình ảnh ở đây

cái này trông giống như một con ruồi - cuộc sống tĩnh lặng

nhập mô tả hình ảnh ở đây

dao động khác

nhập mô tả hình ảnh ở đây

BIÊN TẬP

một dao động khác được tìm thấy. Tôi đặt tên cho con đại bàng này.

nhập mô tả hình ảnh ở đây

Chào! dao động khác! (kỳ 4) Cối xay gió.

nhập mô tả hình ảnh ở đây

2 kỳ một.

nhập mô tả hình ảnh ở đây

Dường như có một cấu trúc cách nhiệt bên ngoài từ bên trong. Điều này (và ví dụ trước) sử dụng nó. Điều duy nhất có thể phá vỡ hộp là nếu một trong những ô vuông biên còn sống ở đầu (cho đến nay). Nhân tiện, đây là trò chơi điện tử - giai đoạn 2.

nhập mô tả hình ảnh ở đây

Tôi đã xây dựng điều này trong nhật thực, và có nhiều tệp. Họ đây rồi.

Lớp chính -

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

Lớp vải -

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

            }
        }
    }
}

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

Lớp 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;
    }
}

và cuối cùng ... Lớp phối hợp

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

4
Đó là thứ hai chắc chắn là một con dấu nhỏ hạnh phúc.
Martin Ender

Có ai biết làm thế nào tôi sẽ đăng một tệp jar để mọi người có thể thử nghiệm thiết kế của tôi (một cách dễ dàng) không?
Căng thẳng điên cuồng

3
Tôi thích con trỏ trong Cối xay gió.
cjfaure

10
"cối xay gió" là giống như hành quân kiến nazi
bebe

1
Con trỏ cũng ở trong Eagle. Nó làm tôi bối rối lúc đầu.
mbomb007

25

Con trăn

Tôi đặt nhiều điểm trên một siêu vật liệu, sau đó được sao chép định kỳ trong một lát hình chữ nhật hoặc hình lục giác (các siêu dữ liệu được phép chồng lên nhau). Từ tập hợp tất cả các điểm, sau đó tôi tính toán sơ đồ Voronoi tạo nên lưới của tôi.

Một số ví dụ cũ

Biểu đồ ngẫu nhiên, phép đo tốc độ Delaunay được hiển thị cũng được sử dụng trong nội bộ để tìm hàng xóm

Đồ thị cuộc sống

Ốp lát định kỳ đánh vần GoL

nhập mô tả hình ảnh ở đây

Một số lưới nữa cho thấy cuộc sống vẫn còn

nhập mô tả hình ảnh ở đây

Đối với bất kỳ lưới nào như vậy, có một số lượng lớn các vòng đời với nhiều kích cỡ khác nhau và một số bộ dao động 2, 3 hoặc 5 chu kỳ nhỏ, nhưng tôi không tìm thấy bất kỳ tàu lượn nào, có thể là do sự bất thường của lưới . Tôi nghĩ về việc tự động hóa việc tìm kiếm các dạng sống bằng cách kiểm tra các tế bào cho các dao động định kỳ.

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
Ý tưởng thú vị nhưng một lát gạch ngẫu nhiên sẽ không có nhiều prototile. Đối với ốp lát định kỳ của bạn, bạn cần chọn một sắp xếp và hiển thị rõ ràng cách thức tất cả các bộ dao động và công cụ có thể được thực hiện.
Sở thích của Calvin

Sẽ thật tuyệt nếu đồ thị dựa trên bản đồ thế giới (ví dụ: các thành phố)
Ming-Tang

@SHiNKiROU Ý tưởng tuyệt vời, tôi nhớ đã thấy một gói python hoạt động với bản đồ địa lý, vì vậy tôi sẽ làm điều này, đặc biệt là vì tôi không thể giải quyết trên một lưới duy nhất.
DenDenDo

Tôi nghĩ rằng bạn chỉ xử lý các ô như là hàng xóm khi chúng chia sẻ một cạnh trong khi một đỉnh được chia sẻ là đủ mặc dù biểu đồ kết nối có thể không phẳng trong các trường hợp như vậy. Ví dụ. 5 ô chia sẻ một đỉnh tạo thành K_5 trong biểu đồ kết nối.
ví dụ

Thật vậy, đôi khi chúng được kết nối bởi đỉnh đôi khi chúng tạo ra các ô + liên kết Khi lần đầu tiên tôi xây dựng biểu đồ liên kết, tôi muốn chắc chắn rằng mặt phẳng của nó, tức là không có giao điểm, nhưng đây không phải là trường hợp khi có hơn 3 cạnh gặp nhau một đỉnh. Nhưng may mắn là điều này dễ tránh bằng cách làm cho các tế bào hơi bất đối xứng.
DenDenDo

21

Javascript [25+?]

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

nhập mô tả hình ảnh ở đây

Nhà tessellations! Có hai hình dạng: "Nhà" và "Nhà lộn ngược", mỗi hình có 7 hàng xóm.

Hiện tại tôi có số điểm 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)

Đặt tên quyền cho các mẫu để lấy nếu bạn tìm thấy chúng: p

Cuộc sống tĩnh lặng - Ngôi sao
Ngôi sao

Bộ tạo dao động 2 giai đoạn - "Beacon": Được tìm thấy bởi isaacg
2stagOator

Tàu vũ trụ - "Con cóc": Được tìm thấy bởi isaacg
nhập mô tả hình ảnh ở đây

Tàu lượn - Chưa được đặt tên: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây

Fiddle hiện đang được thiết lập để cư trú ngẫu nhiên trên thế giới như là một trạng thái ban đầu.

Mã số:

// 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
Tôi tìm thấy một bộ dao động, dựa trên đèn hiệu GoL. Dán đoạn sau vào fiddle của bạn: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 Thêm hình ảnh và bao gồm trong fiddle. Bạn có muốn đặt tên cho nó?
Kevin L

Tôi sẽ gọi nó là đèn hiệu. Nó quá giống với đèn hiệu GoL để gọi nó là bất cứ thứ gì khác.
isaacg

5
Tôi tìm thấy một tàu lượn! Tôi muốn gọi nó là con cóc, bởi vì nó trông giống như cơ thể của một con cóc trong một trong các giai đoạn của nó. 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 Tìm lại lần nữa! Và lần này tôi bắt được nó;). Nó thực sự chỉ là một biến thể của bạn mặc dù có thêm hai tế bào sống: 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;tôi nghĩ theo quy tắc, nó vẫn là một tàu vũ trụ khác biệt.
Martin Ender

20

Javascript [27+?]

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

Vòng 2! Bây giờ với hình lục giác, hình vuông và hình tam giác. Và tính tương tác

Phiên bản này hỗ trợ nhấp vào gạch để chuyển trạng thái của chúng, cho bạn những người săn tìm mô hình ngoài kia. Lưu ý: Một số xử lý nhấp chuột có thể hơi phức tạp, đặc biệt đối với các giá trị thấp s, vì các sự kiện nhấp được theo dõi dưới dạng số nguyên nhưng các phép tính được thực hiện với các giá trị dấu phẩy động

nhập mô tả hình ảnh ở đây

Điểm hiện tại - 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

Dao động giai đoạn 4: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây

Dao động giai đoạn 6: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây

Dao động giai đoạn 10: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây

Dao động giai đoạn 12: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây

Tàu vũ trụ giai đoạn 20: Được tìm thấy bởi Martin Büttner
nhập mô tả hình ảnh ở đây


6
Tìm thấy tàu lượn / tàu vũ trụ với giai đoạn 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;
Martin Ender

Một hình dạng khởi đầu khá thú vị khác cho cùng một tàu vũ trụ là 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;bởi vì nó chỉ bao gồm 3 bộ dao động 2 kỳ.
Martin Ender

Bộ tạo dao động giai đoạn 4, trong trường hợp đó là bất kỳ trợ giúp nào:world[53].e=1; world[54].e=1; world[54].c=1; world[54].d=1; world[54].e=1; world[71].e=1; world[71].b=1; world[71].c=1;
Martin Ender

Và gần nhất tôi đã đến với một cái gì đó trông giống như sự tăng trưởng không giới hạn hoặc một tàu vũ trụ thẳng đứng là 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;. Có lẽ điều đó sẽ giúp ai đó tìm thấy một biến thể hoạt động. Đủ rồi ...
Martin Ender

Dao động 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;chu kỳ 6: Nó cũng hoạt động với một nửa kích thước nếu nó nằm trên đường biên.
Martin Ender

16

Ốp lát ngũ giác Cairo (+ khung chung), 17+ điểm

Ốp lát này rất dễ vẽ một cách đáng ngạc nhiên: điều quan trọng là số vô tỷ duy nhất quan trọng để vẽ nó, sqrt(3)rất gần với số hữu tỷ 7/4, có phần thưởng bổ sung mà nếu bạn trừ đi 1tử số và mẫu số bạn nhận được 6/3 = 2, vì vậy rằng các đường thẳng không trục là đối xứng độc đáo.

Nếu bạn muốn giấy lưới, tôi đã tạo một ý chính PostScript cho A4. Hãy thoải mái để ngã ba cho các kích cỡ giấy khác.

Mã này đủ chung để hỗ trợ các nghiêng khác. Giao diện cần được thực hiện là:

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

Sau đó, lát gạch Cairo là:

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

và mã kiểm soát là

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

Bất kỳ đỉnh nào cũng tạo ra một cuộc sống tĩnh (2 điểm):

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

Cuộc sống tĩnh lặng

Dao động (15 điểm): theo chiều kim đồng hồ từ trên cùng bên trái, chúng tôi có các đơn đặt hàng 2, 3, 4, 6, 11, 12.

Các loại dao động


Tôi không thể nhìn thấy con rùa.
Quentin

@Quentin, biệt danh của tôi cho bộ dao động p3 là ebola. Bạn đã có đầu và đuôi rối.
Peter Taylor

Tôi đã suy nghĩ về một p2. Trông giống như một con rùa lật liên tục.
Quentin

Chiếc p4 trông giống như một con rùa bơi.
Ross Presser

16

Hình thoi (hơn 30 điểm)

Lưới này có khả năng kết nối khá cao (mỗi ô có 10 lân cận), và điều kỳ lạ là điều này dường như đóng góp hiệu quả hơn cho việc sinh hơn là chết. Hầu hết các lưới ngẫu nhiên dường như kích hoạt sự tăng trưởng vô hạn (25 điểm); ví dụ: vị trí bắt đầu 5 ô này:

Điểm xuất phát

phát triển hơn 300 thế hệ thành một cái gì đó to lớn:

Sự phát triển của vị trí bắt đầu đó

và dân số tăng trưởng bậc hai với thế hệ trong ít nhất 3000 thế hệ.

Có lẽ đây là lý do tại sao tôi chỉ tìm thấy một bộ dao động , trong giai đoạn 2 (3 điểm):

Dao động 3 ô

Đối với cuộc sống tĩnh (2 điểm): lấy 4 ô bất kỳ xung quanh một đỉnh.

Mã (sử dụng với khung chung và AbstractLatticecác lớp tôi đã đăng trong các câu trả lời trước đó):

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

Ốp lát hình thoi , 17+ điểm

Theo yêu cầu của Martin Büttner.

Cuộc sống tĩnh lặng (2 điểm):

Một chuỗi có hai vòng

Dao động của các thời kỳ (theo chiều kim đồng hồ từ trên cùng bên trái) 2, 4, 5, 6, 11 (15 điểm):

Dao động khác nhau

Nói chung, một bộ tạo dao động có một tập hợp các ô thay đổi ( lõi ), một tập hợp các ô lân cận lõi ( lớp bọc ) và một tập hợp các ô giữ cho lớp vỏ không thay đổi ( hỗ trợ ). Với ốp lát này, đôi khi sự hỗ trợ của bộ dao động có thể chồng lấp: vd

4 dao động và 5 dao động với sự hỗ trợ chồng chéo

Nếu bộ dao động 4 bị loại bỏ, sự hỗ trợ của bộ dao động 5 sẽ thất bại và cuối cùng nó sẽ phát triển thành bộ tạo dao động 2. Nhưng nếu loại bỏ 5 bộ dao động, sự hỗ trợ của bộ tạo dao động 4 sẽ chỉ cần thêm một hex và ổn định, vì vậy đây không thực sự là bộ tạo dao động 20.


Mã thực hiện ốp lát này cực kỳ chung chung: dựa trên kinh nghiệm của tôi với ốp lát định kỳ, tôi nhận ra rằng mở rộng đến một ranh giới đã biết và thực hiện tra cứu bằng đỉnh là một kỹ thuật rất linh hoạt, mặc dù có thể không hiệu quả đối với các mạng đơn giản. Nhưng vì chúng tôi quan tâm đến các mạng phức tạp hơn, tôi đã thực hiện phương pháp đó ở đây.

Mỗi lát gạch định kỳ là một mạng tinh thể và có thể xác định một đơn vị cơ bản (trong trường hợp ốp lát này là hình lục giác, hai hình tam giác và ba hình vuông) được lặp lại dọc theo hai trục. Sau đó, chỉ cần cung cấp độ lệch trục và tọa độ của các ô nguyên thủy của một đơn vị cơ bản và bạn đã hoàn tất.

Tất cả các mã này có thể được tải xuống dưới dạng zip tại https://gist.github.com/pjt33/becd56784480ddd751bf và cũng bao gồm một mã GenericLifeGuimà tôi chưa đăng trên trang này.

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

Hỗ trợ cho điều này là khung chung được đăng trước đây của tôi cộng với AbstractLatticelớp:

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

Sau một vài giờ thời gian CPU, tôi đã thêm một bộ dao động 7 và bộ dao động 15, cộng với một số cặp dao động thú vị nơi chúng chia sẻ một số tế bào giữ cho chúng ổn định.
Peter Taylor

Và bằng cách điều chỉnh thủ công bộ tạo dao động 7, tôi vô tình tạo ra bộ tạo dao động 3, cho bạn biết điều gì đó về cách tìm kiếm ngẫu nhiên hiệu quả ... Bây giờ hãy nghĩ về cách xử lý đối xứng theo cách chung.
Peter Taylor

13

Ốp lát mê cung định kỳ (hơn 45 điểm)

Điều này sử dụng khung chung từ câu trả lời trước đó của tôi.

Cuộc sống tĩnh lặng (2 điểm):

Mê cung tĩnh vật: bốn hình tam giác gặp nhau tại một đỉnh-12

Dao động (3 điểm):

Hình ảnh dao động

Bộ dao động này là cực kỳ phổ biến, bật lên trong kết quả của hầu hết các điểm bắt đầu ngẫu nhiên.

Mã số:

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

Phép chiếu Penrose-esque của mạng 7 chiều (64+ điểm)

Đây là giống với ốp lát Penrose (để có được một ốp lát Penrose thay thế N = 7với N = 5) và đủ điều kiện cho tiền thưởng không tuần hoàn (40 điểm).

Still life (2 điểm): tầm thường vì các protocell là lồi, do đó, bất kỳ đỉnh nào của thứ tự 3 hoặc nhiều hơn đều đủ. (Chọn tất cả các mặt của nó nếu nó là thứ tự 3 hoặc bất kỳ 4 mặt nào khác).

Dao động ngắn hạn (15 điểm):

Ốp lát này rất giàu dao động. Khoảng thời gian nhỏ nhất mà tôi chỉ tìm thấy một bộ dao động là 11 và khoảng thời gian nhỏ nhất mà tôi không tìm thấy là 13.

p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12

Dao động dài hạn (7 điểm):

Tôi đã cố tình chọn một trong các biến thể của ốp lát này có tính đối xứng quay và hóa ra nó hữu ích cho bộ dao động dài hạn. Nó thực hiện 1/7 vòng quay quanh điểm trung tâm cứ sau 28 thế hệ, biến nó thành p196.

p196

Mã này sử dụng khung mà tôi đã đăng trong các câu trả lời trước đó cùng với lớp ốp sau:

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, điểm-hiện 11

Đây là phiên bản mới và được cải tiến của phiên bản trên, ngoại trừ không có lỗ hổng chết người!

thử nó ở đây , bây giờ với nút ngẫu nhiên! (nhấn nhiều lần để có thêm thông tin) Cũng bao gồm nút tốc độ.

Đầu tiên, dao động giai đoạn 4, 3 điểm

nhập mô tả hình ảnh ở đây

Tiếp theo, 2 3 chu kỳ 2 dao động - 3 điểm

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Thêm 2 dao động 2 kỳ, lịch sự của Martin Büttner (oooohhhhhhh ... màu)

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Tôi đã thực hiện một chương trình để chạy nó ngẫu nhiên và liên tục, tìm kiếm các dao động. Nó tìm thấy cái này kỳ 5 +3 điểm

nhập mô tả hình ảnh ở đây

Và một giai đoạn 5 khác, được tìm thấy bởi ngẫu nhiên.

nhập mô tả hình ảnh ở đây

Và tất nhiên, một cuộc sống tĩnh lặng (ví dụ, có rất nhiều) 2 điểm

nhập mô tả hình ảnh ở đây

Mã- Lớp chính

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

Tranh sơn dầu -

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

Danh từ: Tọa độ -

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

Đa giác

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

Nếu bất cứ ai tìm thấy bất kỳ, họ sẽ được đề cập. (Điều này nhắc nhở tôi: anh tôi đã tìm thấy 2 dao động đầu tiên)



10

Javascript, HexagonSplit

Tuyên bố từ chối trách nhiệm: Nó khá chậm do có nhiều thao tác dom và có lẽ cần một lỗi để trục x không bao bọc.

Vĩ cầm

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

Fiddle hiện cho phép chuyển đổi các ô đang hoạt động.

Vẫn còn sống

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Dao động

2 pha 2 pha

Tàu vũ trụ (2 giai đoạn, hai biến thể)

2 pha biến thể đầu tiên

Tàu vũ trụ (4 giai đoạn)

nhập mô tả hình ảnh ở đây

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" (hơn 24 điểm *)

Lấy cảm hứng từ lát gạch hình ngũ giác hoa: một khối 7 hình lục giác xếp mặt phẳng, và chúng ta có thể cắt các hình lục giác theo nhiều cách khác nhau. Như tên cho thấy, đây là biến thể thứ ba mà tôi đã thử, nhưng nó đáng để đăng bởi vì đây là lần đầu tiên để nhận 7 điểm cho bộ dao động p30 +.

Ốp lát là:

Mặt trong của 7 hình lục giác được chia thành 6 hình tam giác đều;  sáu bên ngoài thành 3 hình thoi, với sự tương đương xen kẽ

Vì các nguyên mẫu là lồi, nên bất kỳ đỉnh bậc 3 nào cũng có tuổi thọ tĩnh (2 điểm).

Tôi đã tìm thấy năm dao động chu kỳ nhỏ (15 điểm): giai đoạn 2, 3, 4, 6, 12.

dao động p2 dao động p3 dao động p4 dao động p6 dao động p12

pièce de résistance : một bộ dao động p48 (7 điểm) xoay 60 độ cứ sau 8 thế hệ:

dao động p48

* Với tính chất của ốp lát này, tôi có thể chọn một hình lục giác được chia thành hình thoi và xoay nó 60 độ. Điều này sẽ làm cho ốp lát có chu kỳ mà không vi phạm bất kỳ quy tắc nào và cũng sẽ không phá vỡ bất kỳ bộ dao động nào. Nhưng tôi không nghĩ đó là tinh thần của câu hỏi, vì vậy tôi sẽ không cố gắng để đạt được 40 điểm đó.

Mã này dựa trên rất nhiều mã tôi đã đăng trong các câu trả lời khác; phần duy nhất là

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

Hình chữ nhật có chiều rộng 2 hàng trong Python 3, +2

Hình dạng của lưới này như sau:

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

Thật trùng hợp, mỗi ô trong lưới này có 8 hàng xóm, giống như lát gạch vuông ban đầu của Trò chơi cuộc sống.

Thật không may, ốp lát này có tài sản khủng khiếp mà mỗi tế bào chỉ có hai hàng xóm phía bắc. Điều đó có nghĩa là một mô hình không bao giờ có thể lan truyền về phía nam, bao gồm cả phía đông nam hoặc tây nam. Tính chất này dẫn đến một tình huống làm cho các bộ dao động khá khó xảy ra, mặc dù người ta có thể tồn tại loại có các bức tường ở hai bên và các ô nhấp nháy ở giữa.

Nó dường như cũng có tài sản (tôi chưa chắc chắn 100%) rằng không có mô hình nào có thể phát triển trong khi di chuyển về phía bắc. Một hàng sẽ không bao giờ phát triển đến số lượng ô tối đa rộng hơn so với hàng bên dưới nó. Tôi nghĩ rằng điều đó có nghĩa là không có tàu lượn hoặc các hình thức phức tạp hơn.

Điều đó khiến chúng tôi nhận được phần thưởng +2 cho rất nhiều cuộc sống vẫn còn, trong đó đây chỉ là một mẫu nhỏ:

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

Đây là mã, khi chạy sẽ vẽ một lưới 8 hàng (1 ô ở hàng trên cùng, 128 ô ở hàng dưới cùng). Bất kỳ khóa nào sẽ tiến lên một bước, ngoại trừ rsẽ ngẫu nhiên bảng và qsẽ thoát khỏi chương trình.

#!/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: Lưới này tương đương với thường xuyên trong một không gian phi Euclide có hình dạng đặc biệt :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.