การนับสิ่งมีชีวิตบนกระเบื้องหกเหลี่ยม


18

ความท้าทายนี้จะทำให้คุณนับ "สิ่งมีชีวิต"ในเกมตัวต่อ Palago

สิ่งมีชีวิตเป็นรูปร่างปิดใด ๆ ที่สามารถเกิดขึ้นได้โดยกระเบื้อง Palago ที่มีสีตรงกันในตารางหกเหลี่ยม

Palago ของเกมประกอบด้วยไพ่แบบนี้:

กระเบื้อง Palago

กระเบื้องเหล่านี้สามารถหมุนได้120 , 240หรือไม่ได้ทั้งหมดและวางไว้ที่ใดก็ได้บนตารางหกเหลี่ยม ตัวอย่างเช่นนี่คือสิ่งมีชีวิต (สีแดง) ที่ต้องใช้ 12 แผ่นสิ่งมีชีวิตที่สิบสองกระเบื้อง

ท้าทาย

เป้าหมายของความท้าทายนี้คือการเขียนโปรแกรมที่ใช้จำนวนเต็มnเป็นอินพุทและคำนวณจำนวนสิ่งมีชีวิต (ขึ้นอยู่กับการหมุนและการสะท้อน) ที่ต้องใช้nกระเบื้อง โปรแกรมที่ควรจะสามารถจัดการได้ถึงn=10ในTIO นี่คือซึ่งมีจำนวนน้อยที่สุดที่จะชนะ

ตัวอย่างข้อมูล

ค่าที่ควรจะตรงกับข้อมูลที่พบใน "เคานต์สิ่งมีชีวิตและการประมาณการ" ของเว็บไซต์ของผู้สร้าง คือ

 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-golfซึ่งหมายถึงภารกิจการเพิ่มประสิทธิภาพไบต์ที่แท้จริง
Jonathan Frech

10
จากการสนทนาที่นี่ดูเหมือนว่าไม่เป็นไรที่จะมีความต้องการความเร็วในการเรียกใช้งานในคำถามรหัสกอล์ฟตราบใดที่คะแนนเป็นจำนวนไบต์
Peter Kagey

2
+1 เช่นเดียวกับลำดับหมุนวนความท้าทายนี้ง่ายต่อการเข้าใจและน่าสนใจในการแก้ปัญหา ... แต่ต้องใช้รหัสสักหน่อย : p
Arnauld

1
ดังนั้น .... เราแค่รับอินพุตและส่งคืนเอาต์พุตจากรายการด้านบนสำหรับ n ระหว่าง 1 ถึง 10? ฉันสามารถใช้ตารางการค้นหาได้หรือไม่
BradC

6
@BradC นี้เป็นช่องโหว่เริ่มต้น สหกรณ์เพียงแค่กล่าวว่าการแก้ปัญหาที่ควรจะสามารถจัดการได้ถึงใน TIO n=10
Arnauld

คำตอบ:


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 เพื่อให้รหัสสามารถเปรียบเทียบได้ง่ายขึ้น

ค้นหา hexes ที่ว่างเปล่า

การค้นหาสิ่งมีชีวิตคือ:

  • เริ่มต้นรายการของกระเบื้องด้วยไพ่ 1 ที่ 0,0
  • ซ้ำ:
    • ค้นหา hex ที่ว่างเปล่าที่จำเป็นเพื่อให้สิ่งมีชีวิต
    • หากพบ hex ที่ว่างเปล่า
      • เพิ่ม tile แต่ละประเภท 0,1,2 ไปที่ hex ที่ว่างเปล่าและเรียกคืน
    • หากไม่พบฐานสิบหกที่ว่างเปล่า
      • หากสิ่งมีชีวิตมีขนาดที่ถูกต้องและไม่ได้อยู่ในสวนสัตว์
        • จำนวนที่เพิ่มขึ้นของสิ่งมีชีวิตที่แตกต่างพบโดยหนึ่ง
        • เพิ่มการหมุนทั้งหมดและการสะท้อนกลับของสิ่งมีชีวิตในสวนสัตว์

การค้นหา hexes ว่างเปล่าเปิดโปงสมมาตรที่น่าสนใจ Arnauld ค้นพบว่าหนึ่งในหกทิศทางนั้นอาจถูกละเว้น

นี่คือทิศทางดั้งเดิมของ Arnauld และปุ่มตัวต่อ:

ทิศทางและปุ่มลูกศรของ Arnauld

ลองจินตนาการว่าเราเริ่มต้นที่ไทล์ A ของประเภท 1 ที่จุดสีน้ำเงิน ดูเหมือนว่าเราจะต้องจ่ายคืนใน d = 0 และ d = 5 อย่างไรก็ตามกระเบื้องใดก็ตามที่อยู่ใน d = 0 มันจะมีทางออกใน d = 4 ซึ่งจะไปที่ฐานสิบหกเดียวกันกับออกจากกระเบื้อง A ใน d = 5 นั่นคือการค้นพบของ 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 สามารถละเว้นได้เพราะ hexes เข้าถึงได้ในทิศทาง 1,3,5 สามารถเข้าถึงได้จากฐานสิบหกที่อยู่ติดกันโดยใช้ทิศทาง 0,2 หรือ 4

มันเจ๋งแค่ไหน!?

ทิศทาง relabelled

ดังนั้นฉันจึงติดฉลากทิศทางและไทล์แบบนี้ (แก้ไขรูปภาพของ 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 cube

-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

ในการรับชุดค่าผสมทั้งหมดที่ฉันแสดง: rot, rot, rot, reflect, rot, rot

รหัส (Original 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 นั่นยอดเยี่ยม! ฉันมีงานใหญ่วันข้างหน้าของฉันตอนนี้ แต่หวังว่าจะตรวจสอบเรื่องนี้ในวันพรุ่งนี้ ขอบคุณ
John Rees

20

JavaScript (Node.js) ,  578 ... 433  431 ไบต์

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 )

อย่างไร?

ทิศทางและกระเบื้อง

เราใช้รหัสต่อไปนี้สำหรับเข็มทิศ 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

ตัวอย่าง:

15134

สัมพันธ์

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+Y+Z=0

พิกัดก้อน

เครดิต: www.redblobgames.com

มันจะทำให้ง่ายต่อการประมวลผลการหมุนและการสะท้อนแสงในขั้นตอนสุดท้ายของอัลกอริทึม

การเข้ารหัสไทล์

ไทล์จะถูกเก็บไว้ในรายการโดยไม่มีคำสั่งซื้อเฉพาะ หมายความว่าเราไม่ต้องกังวลกับการจัดสรร 2D แบบไดนามิกและเราสามารถทำซ้ำบนกระเบื้องที่มีอยู่ได้อย่างง่ายดาย ข้อเสียคือเมื่อกำหนดพิกัดเฉพาะเราจำเป็นต้องfind()มีไทล์ที่สอดคล้องกันในรายการ

(x,Y,Z,ม.,เสื้อ)

  • (x,Y,Z)
  • ม.
  • เสื้อ012

ขั้นตอนวิธี

1(0,0,0)0

ไทล์เริ่มต้น

[0,0,0,1,1]ดังนั้นกระเบื้องนี้จะถูกเข้ารหัสเป็น

ในการทำซ้ำแต่ละครั้งเรามองหา:

  • ไทล์ที่ขาดการเชื่อมต่อ: ในกรณีนี้เราพยายามเชื่อมต่อกับไทล์แต่ละประเภทอย่างต่อเนื่อง

  • ไทล์ที่เชื่อมต่อแล้ว แต่จำเป็นต้องเพิ่มการเชื่อมต่อใหม่เนื่องจากมีการเชื่อมต่อในทิศทางอื่น: ในกรณีนี้เราอัปเดตมาสก์ทิศทาง (พร้อมบิตหรือ OR) และบังคับให้ทำซ้ำใหม่

หากการเชื่อมต่อทั้งหมดนั้นถูกต้องและเราได้มาถึงจำนวนไทล์ที่ร้องขอแล้วเรายังต้องทดสอบว่ามันเป็นสิ่งมีชีวิตใหม่หรือเพียงแค่เวอร์ชันที่ดัดแปลงของอันที่มีอยู่เดิม:

  1. เราใช้การแปลงต่อไปนี้:

    • (x,Y)(x,Y)(Y,Z)(Z,x)

    • (x,Y)(Y,x)(Z,Y)(x,Z)

  2. (0,0)

  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

รักคำตอบนี้ ให้ฉันทุกคนยิงขึ้นเพื่อยิงในช่วงสุดสัปดาห์!
John Rees

ฉันเพิ่งจะโพสต์คำตอบที่ฉันหวังว่าคุณจะสนใจ มันจะโอเคสำหรับฉันที่จะใช้หนึ่งในภาพของคุณเพื่อช่วยอธิบายของฉัน? ฉันจะให้เครดิตคุณแน่นอน
John Rees

@ JohnRees แน่นอนว่าไม่มีปัญหา
Arnauld
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.