Altıgen bir fayans üzerinde yaratıkları sayma


18

Bu meydan okuma kiremit oyunu Palago "yaratıklar" saymak olacaktır .

Bir yaratık, altıgen bir ızgarada eşleşen renkteki Palago fayansları tarafından oluşturulabilen herhangi bir kapalı şekildir.

Oyun Palago böyle fayans oluşur:

Palago kiremit

Bu karolar döndürülebilir 120 , 240 , ya da hiç bir altıgen ızgara üzerinde herhangi bir yere yerleştirilir. Örneğin, burada 12 kiremit gerektiren (kırmızı) bir yaratık var.On iki kiremit yaratık.

Meydan okuma

Bu zorluğun amacı, bir tamsayıyı ngirdi olarak alan ve nfayans gerektiren yaratıkların sayısını (dönüş ve yansımaya kadar) hesaplayan bir program yazmaktır . Program TIOn=10 üzerinde çalışabilmelidir . Bu , çok az bayt kazanır.

Örnek veriler

Değerler, içerik oluşturucunun web sitesinin "Yaratık Sayımları ve Tahminleri" bölümünde bulunan verilerle eşleşmelidir . Yani

 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

"Program n=10TIO üzerinde çalışabilmelidir ." - O bir yürütme hızı gereksinimi ise, kullanın lütfen kod meydan yerine kod golf ikincisi saf bayt optimizasyon görev atıfta bulunarak,.
Jonathan Frech

10
Buradaki tartışmaya dayanarak , puanlama bayt sayısı olduğu sürece, bir kod golf sorusunda yürütme hızı gereksinimi olması iyi görünüyor .
Peter Kagey

2
+1 Tıpkı sarmal bir dizi gibi , bu zorluğun anlaşılması kolaydır ve çözülmesi gerçekten ilginçtir ... ancak biraz kod gerektirir. : p
Arnauld

1
Yani ... sadece 1'den 10'a kadar n için bir giriş alıyoruz ve yukarıdaki listeden çıktıyı geri mi veriyoruz? Sadece bir arama tablosu kullanabilir miyim?
BradC

6
@BradC Bu varsayılan bir boşluktur . OP, çözümlerin TIO'da kadar işlem yapabilmesi gerektiğini söyledi . n=10
Arnauld

Yanıtlar:


5

JavaScript (Node.js) , 480 417 bayt

@Arnauld sayesinde -63 bayt. Vay.

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

Çevrimiçi deneyin!

İlk olarak, cevabı bana daha derine inmemiz için ilham veren Arnauld'a saygı gösterin. Kodları daha kolay karşılaştırılabilir böylece Arnauld ile aynı değişkenleri kullanmak için kasıtlı olarak bazı kodumu değiştirdim, ancak benim algoritmaları ile orijinal olmak zor çalıştım.

Boş altıgenleri arama

Yaratıkların arayışı:

  • Fayans 1 ile döşemelerin listesini 0,0 ile başlat
  • Tekrarlı:
    • Yaratığı tamamlamak için gereken boş bir hex'i arayın
    • Boş altıgen bulunursa
      • Her altı çini tipini 0,1,2 boş altıgen ve recurse ekleyin
    • Boş altıgen bulunmazsa
      • Yaratık doğru büyüklükteyse ve halihazırda hayvanat bahçesinde değilse
        • Birinin bulduğu farklı canlıların sayısını artırın
        • Yaratığın tüm dönüşlerini ve yansımalarını hayvanat bahçesine ekleyin

Boş altıgenler arayışı ilginç bir simetri ortaya çıkardı. Arnauld, altı yönden birinin göz ardı edilebileceğini keşfetti, ancak aslında altı kişiden üçü göz ardı edilebilir!

Arnauld'un orijinal yönü ve döşeme anahtarı:

Arnauld'un yönü ve döşeme anahtarı

Mavi noktadaki tip 1 kiremitle başladığımızı düşünün. Görünüşe göre d = 0 ve d = 5'de çürütmek zorundayız. Bununla birlikte, hangi karo d = 0'a yerleştirilirse, d = 4'te kesinlikle bir çıkış olacaktır, bu da d = 5'teki A döşemesinden çıkmakla aynı hex'i ziyaret edecektir. Arnauld'un keşfi bu ve beni düşünmeye iten şey buydu.

Dikkat:

  • D = 0 değerinde çıkışı olan her döşemenin d = 5 değerinde çıkışı vardır
  • D = 2'de çıkışı olan her döşemenin d = 1'de çıkışı vardır
  • D = 4'te çıkışı olan her döşemenin d = 3'te çıkışı vardır

  • D = 0'dan girilebilen her döşemenin d = 4'te bir çıkışı vardır

  • D = 2'den girilebilen her döşemenin d = 0'da bir çıkışı vardır
  • D = 4'ten girilebilen her döşemenin d = 2'de bir çıkışı vardır

Bu, yalnızca 0,2,4 yönlerini dikkate almamız gerektiği anlamına gelir. 1,3,5 yönlerinde herhangi bir çıkış göz ardı edilebilir, çünkü 1,3,5 yönlerinde ulaşılabilir altıgenlere bunun yerine 0,2 veya 4 yönleri kullanılarak bitişik bir altıgenden ulaşılabilir.

Ne kadar serin!?

Yeniden Etiketlenmiş Yönler

Bu yüzden yönleri ve fayansları yeniden etiketliyorum (Arnauld'un resmi düzenlendi):

Basitleştirilmiş yol tarifleri

Şimdi karolar, girişler ve çıkışlar arasında şu ilişkiye sahibiz:

    |  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

Yani çıkışlar: d + t == 2? (4-t)% 3: 2-t ve 2 * t% 3

Altıgen Dönüşler ve Yansımalar

Dönüşler ve yansımalar için, x, y, z küp koordinatları yerine x, y altıgen eksenel koordinatlarını denemeye karar verdim .

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

Bu sistemde, dönme ve yansıma beklediğimden daha basitti:

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

Yaptığım tüm kombinasyonları elde etmek için: rot, rot, rot, refl, rot, rot

Kod (Orijinal 480 bayt)

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
)

Kod (Arnauld 417 bayt)

Arnauld nazikçe bana kafamı sarmak için biraz zaman alan hileler kullanılan 63 baytlık bir tasarruf sundu. Birçok ilginç düzenlemeye sahip olduğundan, kodumu aşağıya koyacağımı düşündüm (yorumlarımı ekledim), böylece sürümümle kontrast oluşturabilir.

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

Yol tarifi güzel bir fikir! Ve bence bu cevabımın büyüklüğünün altında golf oynayabilir.
Arnauld


@Arnauld Bu harika! Önümde büyük bir iş günüm var, ama bunu yarın incelemek için sabırsızlanıyorum. Teşekkürler.
John Rees

20

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

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 )

Nasıl?

Yol tarifleri ve fayanslar

6 yönlü pusula ve fayanslar için aşağıdaki kodları kullanıyoruz:

yön ve fayans

Yaratığın mavi olduğunu varsayıyoruz.

Bağlantılar

Belirli bir döşemeyi belirli bir yönde girerken, yaratığın hangi bölümlerinin diğer döşemelere bağlanması gerektiğini bilmek için bir tabloya ihtiyacımız var:

     |  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

Misal:

15134

bağlantıları

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üzleştirildikten sonra, bu şunları verir:

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

Koordinatlar

x+y+z=0

küp koordinatları

Kredi: www.redblobgames.com

Algoritmanın son adımında dönüşleri ve yansımaları işlemeyi kolaylaştıracaktır.

Döşeme kodlaması

Kutucuklar belirli bir düzen olmadan bir listede saklanır. Bu, bazı dinamik 2D ayırma konusunda endişelenmemiz gerekmediği ve mevcut karoları kolayca tekrarlayabileceğimiz anlamına gelir. Dezavantajı, belirli koordinatlar verildiğinde,find() , listedeki ilgili döşemeye .

(x,y,z,m,t)

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

Algoritma

1(0,0,0)0

ilk karo

Bu nedenle, bu karo olarak kodlanır [0,0,0,1,1].

Her yinelemede aşağıdakileri ararız:

  • Bağlantıları eksik olan döşemeler: bu durumda, her bir döşeme türü ile bağlantıyı art arda tamamlamaya çalışırız.

  • Zaten bağlı olan, ancak farklı bağlantılara ulaşıldıkları için yeni bağlantıların eklenmesi gereken karolar: bu durumda, yön maskesini (bit yönlü VEYA ile) günceller ve yeni bir yinelemeyi zorlarız.

Tüm bağlantılar geçerliyse ve istenen sayıda karoya ulaştıysak, bunun yeni bir yaratık mı yoksa sadece mevcut bir varlığın değiştirilmiş bir versiyonu mu olduğunu test etmemiz gerekir:

  1. Aşağıdaki dönüşümleri uyguluyoruz:

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

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

  2. (0,0)

  3. Fayansları koordinatlarına ve türlerine göre sıralarız. (Bu tür sözlükbilimsel sırayla işlenir, bu iyidir.)

  4. Sonunda, sonuç listesini diğer anahtarlarla karşılaştırılabilecek bir anahtar dizesine zorlarız.

  5. Bilinen bir anahtar eşleştirilir kesilmez veya yeni anahtarı depolar ve hiçbir dönüşüm bilinen bir anahtara yol açmazsa nihai sonucu artırırız.

Yorumlananlar

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

Bu yanıtı seviyorum. Beni haftasonu denemek için kovuldum!
John Rees

İlgi çekici bulduğunuzu umuyorum. Açıklamama yardımcı olması için resimlerden birini kullanmam uygun olur mu? Sana elbette kredi vereceğim.
John Rees

@JohnRees Elbette, sorun değil.
Arnauld
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.