在六角形平铺上计数生物


18

这项挑战将让您在拼贴游戏Palago中计算“生物”

生物是可以由六边形网格中匹配颜色的Palago瓷砖形成的任何闭合形状。

游戏Palago由如下图块组成:

帕拉戈瓷砖

这些砖可以旋转120240,或者根本不与任何地方放置在六边形网格上。例如,这是一个需要12个图块的(红色)生物。十二个瓷砖生物。

挑战

挑战的目标是编写一个程序,该程序将整数n作为输入并计算需要n图块的生物数量(包括旋转和反射)。该计划应能处理多达n=10TIO。这是,因此最少的字节获胜。

示例数据

这些值应与创建者网站的“ 创建计数和估算”部分中找到的数据相匹配。即

 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

“该程序应该能够处理多达n=10TIO的事件。” -如果这是执行速度的要求,请使用code-challenge而不是code-golf,后者是指纯字节优化任务。
乔纳森·弗雷希

10
根据此处的讨论,只要得分是字节数,在代码高尔夫球问题中对执行速度有要求就可以了。
Peter Kagey

2
+1就像一个螺旋序列一样,这个挑战很容易理解,而且解决起来确实很有趣……但是需要很多代码。:p
Arnauld

1
所以....我们只是从上面的列表中获取一个输入并返回输出,对于1到10之间的n?我可以只使用查找表吗?
BradC

6
ñ=10

Answers:


5

的JavaScript(Node.js的)480个 417字节

-63个字节,感谢@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

在线尝试!

首先,尊重Arnauld的回答,这给了我进一步挖掘灵感的灵感。尽管我有意更改了一些代码以使用与Arnauld相同的变量,但我仍努力尝试使算法具有原创性,以便可以更轻松地比较代码。

搜索空的十六进制

寻找生物的方法是:

  • 用0,0的图块1初始化图块列表
  • 递归地:
    • 搜索完成生物所需的空十六进制
    • 如果发现空十六进制
      • 将每种类型的图块0,1,2添加到空十六进制并递归
    • 如果找不到空的十六进制
      • 如果生物的大小正确且尚未进入动物园
        • 一个人发现的不同生物的数量增加
        • 将生物的所有旋转和倒影添加到动物园

寻找空的六角形发现了一个有趣的对称性。Arnauld发现可以忽略六个方向之一,但实际上可以忽略六个方向中的三个!

这是Arnauld的原始方向和图块键:

Arnauld的方向和平铺键

假设我们从蓝点处的类型1的图块A开始。看来我们必须在d = 0和d = 5中递归。但是,无论放置在d = 0中的哪个图块,都肯定会在d = 4中具有一个出口,该出口将与在d = 5中离开图块A的十六进制相同。那就是Arnauld的发现,这就是我开始思考的原因。

注意:

  • 在d = 0处有出口的每个图块在d = 5处都有一个出口
  • 在d = 2中具有出口的每个图块在d = 1中均具有出口
  • 每个在d = 4中具有出口的图块在d = 3中具有一个出口

  • 可以从d = 0输入的每个图块在d = 4中都有一个出口

  • 可以从d = 2输入的每个图块在d = 0处都有一个出口
  • 可以从d = 4输入的每个图块在d = 2中都有一个出口

这意味着我们只需要考虑方向0、2、4。可以忽略方向1、3、5上的任何出口,因为可以改为使用方向0、2或4从相邻的十六进制到达方向1、3、5上的十六进制。

多么酷啊!?

重新标记的方向

因此,我像这样重新标记了方向和图块(编辑了Arnauld的图像):

简化方向

现在,我们在图块,入口和出口之间具有以下关系:

    |  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

所以出口是:d + t == 2?(4-t)%3:2-t和2 * t%3

六角旋转和反射

对于旋转和反射,我决定尝试使用x,y 六边形轴向坐标,而不是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

在这个系统中,旋转和反射比我预期的要简单:

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

要获得我执行的所有组合:腐烂,腐烂,腐烂,反射,腐烂,腐烂

代码(原始480字节)

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
)

代码(Arnauld 417字节)

Arnauld友好地提交了一个63字节的保存文件,该文件使用了一些技巧,这些技巧使我花了很多时间来解决问题。由于它包含许多有趣的编辑,因此我想将他的代码放在下面(我已经添加了评论),以便可以与我的版本进行对比。

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

很好的方向指导!而且我认为这可以解决我的问题。
Arnauld


@Arnauld太棒了!我现在有一个艰巨的工作日,但是期待明天能解决这个问题。谢谢。
约翰·里斯

20

JavaScript(Node.js) 578 ... 433431 字节

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

ñ=1个ñ=13

怎么样?

方向和瓷砖

我们将以下代码用于6向罗盘和图块:

方向和瓷砖

我们假设该生物是蓝色的。

连接数

我们需要一个表来知道当我们沿给定方向输入给定图块时,该生物的哪些部分需要与其他图块连接:

     |  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

例:

1个51个34

连接数

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 |  "$"  |  "<"  |  "%"

展平后,将得到:

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

座标

为了描述图块的位置,我们使用带有立方体坐标X+ÿ+ž=0

立方体坐标

积分:www.redblobgames.com

这将使在算法的最后一步中处理旋转和反射更加容易。

瓦片编码

磁贴存储在列表中,没有特定的顺序。这意味着我们不必担心某些动态2D分配,并且我们可以轻松地迭代现有的图块。缺点是,在给定特定坐标的情况下,我们需要find()列表中的相应图块。

XÿžŤ

  • Xÿž
  • Ť01个2

算法

1个0000

初始图块

因此,此图块被编码为 [0,0,0,1,1]

在每次迭代中,我们寻找:

  • 缺少连接的图块:在这种情况下,我们将依次尝试完成每种图块的连接。

  • 已经连接但由于在另一个方向已到达而需要为其添加新连接的图块:在这种情况下,我们将更新方向遮罩(按位或)并强制执行新的迭代。

如果所有连接均有效,并且我们已达到要求的磁贴数量,则仍然需要测试它是新生物还是只是现有生物的修改版本:

  1. 我们应用以下转换:

    • 我们通过将替换执行3种可能的旋转XÿXÿÿžžX(240°)并更新其类型来相应地平铺。

    • XÿÿXžÿXž

  2. 00

  3. 我们根据图块的坐标和类型对其进行排序。(这种处理按字典顺序进行,很好。)

  4. 我们最终将结果列表强制为一个可以与其他键进行比较的键字符串。

  5. 一旦已知密钥匹配,我们将中止;如果没有转换导致已知密钥,则存储新密钥并增加最终结果。

已评论

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

喜欢这个答案。让我所有人都开除,在周末试一试!
约翰·里斯

我将要发布一个答案,希望您觉得有趣。我可以用您的一张图片来帮助我解释吗?我当然会相信你。
约翰·里斯

@JohnRees当然,没问题。
阿纳尔德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.