Đếm các sinh vật trên một lát gạch hình lục giác


18

Thử thách này sẽ giúp bạn đếm "sinh vật" trong trò chơi xếp gạch Palago.

Một sinh vật là bất kỳ hình dạng khép kín nào có thể được hình thành bởi các gạch Palago có màu phù hợp trong lưới lục giác.

Trò chơi Palago bao gồm các ô như thế này:

Gạch Palago

Những gạch có thể xoay 120 , 240 , hoặc không gì cả và đặt bất cứ nơi nào trên một mạng lưới hình lục giác. Ví dụ, đây là một sinh vật (màu đỏ) cần 12 ô.Mười hai sinh vật ngói.

Thử thách

Mục tiêu của thử thách này là viết một chương trình lấy một số nguyên nlàm đầu vào và tính toán số lượng sinh vật (tối đa để quay và phản xạ) yêu cầu ngạch. Chương trình có thể xử lý tối đa n=10trên TIO . Đây là , vì vậy ít byte nhất sẽ thắng.

Dữ liệu mẫu

Các giá trị phải khớp với dữ liệu được tìm thấy trong phần "Số lượng và Ước tính của sinh vật" trên trang web của người tạo . Cụ thể

 n | output
---+-------
 1 | 0
 2 | 0
 3 | 1 
 4 | 0
 5 | 1
 6 | 1
 7 | 2
 8 | 2
 9 | 9
10 | 13
11 | 37
12 | 81

"Chương trình sẽ có thể xử lý tối đa n=10trên TIO." - nếu đó là một yêu cầu tốc độ thực thi, vui lòng sử dụng thử thách thay vì mã golf , sau này đề cập đến một nhiệm vụ tối ưu hóa byte thuần túy.
Jonathan Frech

10
Dựa trên các cuộc thảo luận ở đây , có vẻ như không có yêu cầu về tốc độ thực hiện trong câu hỏi đánh gôn , miễn là điểm số là số byte.
Peter Kagey

2
+1 Giống như một chuỗi xoắn ốc , thử thách này rất dễ hiểu và thực sự thú vị để giải quyết ... nhưng đòi hỏi khá nhiều mã. : p
Arnauld

1
Vậy .... chúng ta chỉ lấy một đầu vào và trả lại đầu ra từ danh sách trên, cho n trong khoảng từ 1 đến 10? Tôi chỉ có thể sử dụng một bảng tra cứu?
BradC

6
n= =10

Câu trả lời:


5

JavaScript (Node.js) , 480 417 byte

-63 byte nhờ @Arnauld. Ồ

n=>(E=(x,y,d,k,h)=>V[k=[x+=1-(d%=3),y+=~d%3+1,d]]?0:(V[k]=1,h=H.find(h=>h[0]==x&h[1]==y))?(d^(t=2-h[2])?E(x,y,t)||E(x,y,h[2]*2):E(x,y,t+2)):[x,y,0],I=c=>c.map(([x,y,t])=>[x-g(0),y-g(1),t],g=p=>Math.min(...c.map(h=>h[p]))).sort(),S=e=>(V={},e=E(0,0,0))?(--n&&H.pop(H.push(e),S(),S(e[2]=1),S(e[2]=2)),++n):n-1||E[I(c=H)]||[0,0,0,++N,0,0].map(r=>E[I(c=c.map(([x,y,t])=>[-x-y,r?y:x,(r?t*2:t+1)%3]))]=1))(H=[[N=0,0,1]])&&N

Hãy thử trực tuyến!

Đầu tiên, hãy nhắc lại Arnauld, câu trả lời đã cho tôi cảm hứng để đào sâu hơn. Tôi đã cố gắng hết sức để trở thành bản gốc với các thuật toán của mình, mặc dù tôi đã cố tình thay đổi một số mã của mình để sử dụng các biến giống như Arnauld để mã có thể dễ dàng so sánh hơn.

Tìm kiếm các hình lục giác trống

Việc tìm kiếm sinh vật là:

  • Khởi tạo danh sách các ô có ô 1 ở 0,0
  • Đệ quy:
    • Tìm kiếm một hex trống cần thiết để hoàn thành sinh vật
    • Nếu tìm thấy hex rỗng
      • Thêm từng loại gạch 0,1,2 vào hex rỗng và lặp lại
    • Nếu không tìm thấy hex trống
      • Nếu sinh vật có kích thước chính xác và chưa có trong sở thú
        • Số lượng sinh vật khác biệt được tìm thấy bởi một
        • Thêm tất cả các góc quay và phản xạ của sinh vật vào sở thú

Việc tìm kiếm các hình lục giác trống đã phát hiện ra một đối xứng thú vị. Arnauld phát hiện ra rằng một trong sáu hướng có thể bị bỏ qua, nhưng trên thực tế, ba trong số sáu hướng có thể bị bỏ qua!

Đây là hướng ban đầu và khóa gạch của Arnauld:

Hướng của Arnauld và khóa gạch

Hãy tưởng tượng chúng ta bắt đầu ở ô A loại 1 ở chấm màu xanh. Có vẻ như chúng ta phải lặp lại trong d = 0 và d = 5. Tuy nhiên, bất kỳ ô nào được đặt trong d = 0, chắc chắn nó sẽ có lối ra trong d = 4, sẽ truy cập vào cùng một hex như thoát khỏi ô A trong d = 5. Đó là khám phá của Arnauld, và đó là điều khiến tôi suy nghĩ.

Thông báo rằng:

  • Mỗi ô có lối ra trong d = 0 có lối ra trong d = 5
  • Mỗi ô có lối ra trong d = 2 có lối ra trong d = 1
  • Mỗi ô có lối ra trong d = 4 có lối ra trong d = 3

  • Mỗi ô có thể được nhập từ d = 0 có lối ra trong d = 4

  • Mỗi ô có thể được nhập từ d = 2 đều có lối ra trong d = 0
  • Mỗi ô có thể được nhập từ d = 4 có lối ra trong d = 2

Điều này có nghĩa là chúng ta chỉ cần xem xét hướng 0,2,4. Bất kỳ lối thoát nào theo hướng 1,3,5 đều có thể bị bỏ qua vì các hình lục giác có thể tiếp cận theo hướng 1,3,5 thay vào đó có thể được tiếp cận từ một hình lục giác liền kề bằng cách sử dụng các hướng 0,2 hoặc 4.

Thật tuyệt phải không!?

Chỉ đường

Vì vậy, tôi đã dán nhãn lại các hướng và gạch như thế này (hình ảnh của Arnauld đã được chỉnh sửa):

Hướng đơn giản hóa

Bây giờ chúng ta có mối quan hệ sau đây giữa các ô, mục nhập và lối thoát:

    |  t=0  |  t=1  |  t=2
----+-------+-------+-------
d=0 |  0,2  |  1,2  |    2
d=1 |  0,2  |    0  |  0,1
d=2 |    1  |  1,2  |  0,1

Vậy lối thoát là: d + t == 2? (4-t)% 3: 2-t và 2 * t% 3

Xoay và phản xạ lục giác

Để quay và phản xạ, tôi quyết định thử tọa độ trục hình lục giác x, y thay vì tọa độ khối x, y, z.

-1,2   0,2   1,2   2,2
    0,1   1,1   2,1
 0,0   1,0   2,0   3,0

Trong hệ thống này, việc xoay và phản xạ đơn giản hơn tôi mong đợi:

120 Rotation:   x=-x-y   y=x   t=(t+1)%3
Reflection:     x=-x-y   y=y   t=(t*2)%3

Để có được tất cả các kết hợp tôi thực hiện: thối, thối, thối, phản xạ, thối, thối

Mã (480 byte gốc)

f=n=>(
    // H:list of filled hexes [x,y,tile] during search for a complete creature
    // N:number of distinct creatures of size n
    // B:record of all orientations of all creatures already found
    H=[[0,0,1]],N=0,B={},

// E: find an empty hex required to complete creature starting in direction d from x,y
    E=(x,y,d,k,h)=>(
        x+=1-d,
        y+=1-(d+1)%3,
        // V: list of visited hexes during this search in E
        V[k=[x,y,d]] ? 
            0
        : (V[k]=1, h=H.find(h=>h[0]==x&&h[1]==y)) ? 
            // this hex is filled, so continue search in 1 or 2 directions
            (d==2-h[2] ? E(x,y,(4-h[2])%3) : (E(x,y,2-h[2]) || E(x,y,h[2]*2%3))) 
        : [x,y,0] // return the empty hex 
    ),

    // I: construct unique identifier for creature c by moving it so x>=0 and y>=0
    I=c=>(
        M=[0,1].map(p=>Math.min(...c.map(h=>h[p]))),
        c.map(([x,y,t])=>[x-M[0],y-M[1],t]).sort()
    ),

    // A: add complete creature c to B
    A=c=>{
        n==1&&!B[I(c)]&&(
            // creature is correct size and is not already in B
            N++,
            [0,0,0,1,0,0].map(
                // Add all rotations and reflections of creature into B
                // '0' marks a rotation, '1' marks a (vertical) reflection
                // rotation:   x=-x-y   y=x   t=(t+1)%3
                // reflection: x=-x-y   y=y   t=(t*2)%3
                r=>B[I(c=c.map(([x,y,t])=>[-x-y,r?y:x,(r?t*2:t+1)%3]))]=1)          
        )
    },

    // S: recursively search for complete creatures starting with hexes H
    S=e=>{
        V={};
        (e=E(0,0,0)) ?
            // e is a required empty hex, so try filling it with tiles 0,1,2
            (--n && (H.push(e),S(),S(e[2]=1),S(e[2]=2),H.pop()), ++n)
        : A(H) // creature is complete, so add it to B
    },

    S(),
    N
)

Mã (Arnauld 417 byte)

Arnauld vui lòng gửi một tiết kiệm 63 byte sử dụng các thủ thuật khiến tôi mất khá nhiều thời gian để quấn đầu. Vì nó có nhiều chỉnh sửa thú vị, tôi nghĩ rằng tôi đã đặt mã của anh ấy bên dưới (tôi đã thêm nhận xét của mình) để nó có thể tương phản với phiên bản của tôi.

f=n=>(
    // E:find an empty hex required to complete creature starting in direction d from x,y
    E=(x,y,d,k,h)=>
      V[k=[x+=1-(d%=3),y+=~d%3+1,d]] ?
        0
      :(V[k]=1,h=H.find(h=>h[0]==x&h[1]==y)) ?
        (d^(t=2-h[2]) ? E(x,y,t) || E(x,y,h[2]*2) : E(x,y,t+2))
      :[x,y,0],

    // I: construct unique identifier for creature c by moving it so x>=0 and y>=0
    I=c=>c.map(([x,y,t])=>[x-g(0),y-g(1),t],g=p=>Math.min(...c.map(h=>h[p]))).sort(),

    // S: recursively search for complete creatures starting with hexes H
    S=e=>
      (V={},e=E(0,0,0)) ?
        (--n&&H.pop(H.push(e),S(),S(e[2]=1),S(e[2]=2)),++n)
      :n-1
        ||E[I(c=H)] 
        // creature is the correct size and has not been seen before
        // so record all rotations and reflections of creature in E[]
        ||[0,0,0,++N,0,0].map(r=>E[I(c=c.map(([x,y,t])=>[-x-y,r?y:x,(r?t*2:t+1)%3]))]=1)
)
// This wonderfully confusing syntax initializes globals and calls S()
(H=[[N=0,0,1]]) && N

Cái nhìn sâu sắc tốt đẹp về các hướng! Và tôi nghĩ rằng điều này có thể được đánh golf dưới kích thước câu trả lời của tôi.
Arnauld


@Arnauld Thật tuyệt vời! Bây giờ tôi có một ngày làm việc lớn trước mắt, nhưng mong được kiểm tra vào ngày mai. Cảm ơn.
John Rees

20

JavaScript (Node.js) ,  578 ... 433  431 byte

f=(n,T=[B=[N=0,0,0,1,1]])=>!n||T.some(([x,y,q,m])=>B.some((p,d)=>m>>d&1&&((p=x+~-s[d],q=y+~-s[d+2],t=T.find(([X,Y])=>X==p&Y==q))?(q=t[3])&(p=D[d*3+t[4]])^p?t[f(n,T,t[3]|=p),3]=q:0:[0,1,2].map(t=>f(n-1,[...T,[p,q,-p-q,D[d*3+t],t]])))),s="2100122",D=Buffer("160).(9!'8 &$<%"))|n>1||[0,1,2,1,2,0].some((_,d,A)=>B[k=T.map(a=>[(h=n=>Math.min(...T.map(R=a=>a[A[(d+n)%6]]))-R(a))(0),h(3),(x=(a[4]+d*2)%3,d>2)*x?3-x:x]).sort()])?N:B[k]=++N

n= =1n= =13

Làm sao?

Chỉ đường và gạch

Chúng tôi sử dụng các mã sau cho la bàn 6 hướng và gạch:

chỉ đường & gạch

Chúng tôi cho rằng sinh vật có màu xanh.

Kết nối

Chúng ta cần một bảng để biết những phần nào của sinh vật cần được kết nối với các ô khác khi chúng ta nhập một ô nhất định theo một hướng nhất định:

     |  T=0  |  T=1  |  T=2
-----+-------+-------+-------
 d=0 | 0,4,5 | 1,2,4 |   4
 d=1 | 0,3,5 | 1,2,3 |   3
 d=2 | 0,3,4 |   0   | 0,1,2
 d=3 | 3,4,5 |   5   | 1,2,5
 d=4 |   2   | 2,3,4 | 0,2,5
 d=5 |   1   | 1,3,4 | 0,1,5

Thí dụ:

15134

kết nối

5

     |  T=0  |  T=1  |  T=2
-----+-------+-------+-------
 d=0 |  0,4  | 1,2,4 |   4
 d=1 |  0,3  | 1,2,3 |   3
 d=2 | 0,3,4 |   0   | 0,1,2
 d=3 |  3,4  |   -   |  1,2
 d=4 |   2   | 2,3,4 |  0,2

+32

     |  T=0  |  T=1  |  T=2              |  T=0  |  T=1  |  T=2
-----+-------+-------+-------       -----+-------+-------+-------
 d=0 |   17  |   22  |   16          d=0 |  "1"  |  "6"  |  "0"
 d=1 |    9  |   14  |    8          d=1 |  ")"  |  "."  |  "("
 d=2 |   25  |    1  |    7    -->   d=2 |  "9"  |  "!"  |  "'"
 d=3 |   24  |    0  |    6          d=3 |  "8"  |  " "  |  "&"
 d=4 |    4  |   28  |    5          d=4 |  "$"  |  "<"  |  "%"

Sau khi làm phẳng, điều này mang lại:

D = Buffer("160).(9!'8 &$<%")

Tọa độ

x+y+z= =0

tọa độ khối

Tín dụng: www.redblobgames.com

Nó sẽ làm cho nó dễ dàng hơn để xử lý các phép quay và phản xạ trong bước cuối cùng của thuật toán.

Mã hóa gạch

Các gạch được lưu trữ trong một danh sách, không có thứ tự cụ thể. Điều đó có nghĩa là chúng ta không phải lo lắng về một số phân bổ 2D động và chúng ta có thể dễ dàng lặp lại trên các ô hiện có. Nhược điểm là, với các tọa độ cụ thể, chúng ta cần đến find()ô tương ứng trong danh sách.

(x,y,z,m,t)

  • (x,y,z)
  • m
  • t012

Thuật toán

1(0,0,0)0

gạch ban đầu

Do đó, gạch này được mã hóa như [0,0,0,1,1].

Ở mỗi lần lặp, chúng tôi tìm kiếm:

  • Gạch có kết nối bị thiếu: trong trường hợp này, chúng tôi liên tục cố gắng hoàn thành kết nối với từng loại gạch.

  • Các ô đã được kết nối nhưng cần thêm các kết nối mới vì chúng đã được tiếp cận theo một hướng khác: trong trường hợp này, chúng tôi cập nhật mặt nạ hướng (với bit OR OR) và buộc lặp lại mới.

Nếu tất cả các kết nối là hợp lệ chúng tôi đã đạt đến số lượng ô được yêu cầu, chúng tôi vẫn cần kiểm tra xem đó là sinh vật mới hay chỉ là phiên bản sửa đổi của một ô hiện có:

  1. Chúng tôi áp dụng các biến đổi sau:

    • (x,y)(x,y)(y,z)(z,x)

    • (x,y)(y,x)(z,y)(x,z)

  2. (0,0)

  3. Chúng tôi sắp xếp gạch theo tọa độ và loại của chúng. (Loại này được xử lý theo thứ tự từ điển, điều này là tốt.)

  4. Cuối cùng chúng tôi đã ép danh sách kết quả thành một chuỗi khóa có thể so sánh với các khóa khác.

  5. Chúng tôi hủy bỏ ngay khi một khóa đã biết được khớp, hoặc lưu trữ khóa mới và tăng kết quả cuối cùng nếu không có biến đổi nào dẫn đến khóa đã biết.

Đã bình luận

f = (n, T = [B = [N = 0, 0, 0, 1, 1]]) =>
  // abort if n = 0
  !n ||
  // for each tile in T
  T.some(([x, y, q, m]) =>
    // for d = 0 to d = 4
    B.some((p, d) =>
      // if this tile requires a connection in this direction
      m >> d & 1 && (
        // look for a connected tile t at the corresponding position (p, q)
        (
          p = x + ~-s[d],
          q = y + ~-s[d + 2],
          t = T.find(([X, Y]) => X == p & Y == q)
        ) ?
          // if t exists, make sure that its direction mask is up-to-date
          (q = t[3]) & (p = D[d * 3 + t[4]]) ^ p ?
            // if it's not, update it and force a new iteration
            t[f(n, T, t[3] |= p), 3] = q
          :
            0
        :
          // if t does not exist, try each type of tile at this position
          [0, 1, 2].map(t => f(n - 1, [...T, [p, q, -p - q, D[d * 3 + t], t]]))
      )
    ),
    // s is used to apply (dx, dy)
    s = "2100122",
    // D holds the direction masks for the connections
    D = Buffer("160).(9!'8 &$<%")
  ) |
  // stop here if the above some() was truthy or we have more tiles to add
  n > 1 ||
  // otherwise, apply the transformations
  [0, 1, 2, 1, 2, 0].some((_, d, A) =>
    B[
      // compute the key k
      k =
        // by generating the updated tuples [x, y, type] and sorting them
        T.map(a =>
          [
            // transform the 1st coordinate
            (h = n => Math.min(...T.map(R = a => a[A[(d + n) % 6]])) - R(a))(0),
            // transform the 2nd coordinate
            h(3),
            // update the type
            (x = (a[4] + d * 2) % 3, d > 2) * x ? 3 - x : x
          ]
        ).sort()
    ]
  ) ?
    // if the key was found, just return N
    N
  :
    // if this is a new creature, store its key and increment N
    B[k] = ++N

Yêu câu trả lời này. Có tất cả tôi bị sa thải để cho nó một shot vào cuối tuần!
John Rees

Tôi chỉ sắp đăng một câu trả lời mà tôi hy vọng bạn thấy thú vị. Tôi có thể sử dụng một trong những hình ảnh của bạn để giúp tôi giải thích không? Tôi sẽ tín dụng bạn tất nhiên.
John Rees

@JohnRees Chắc chắn, không có vấn đề.
Arnauld
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.