บล็อกฝูงอาคาร Bot!


42

การประกวดสิ้นสุดแล้ว!

Intro

นี่คือการโต้ตอบแบบประกวดซึ่งคอนโทรลเลอร์มีอยู่ใน Stack Snippet ที่ด้านล่างของคำถาม ตัวควบคุมจะอ่านคำตอบและเล่นผ่านเกมโดยอัตโนมัติ ทุกคนสามารถเรียกใช้ได้ทุกเมื่อในเบราว์เซอร์

กลศาสตร์ของการประกวดครั้งนี้มีความคล้ายคลึงกับสีแดงกับสีน้ำเงิน - Pixel ทีม BattleBots ยกเว้นเกมที่กำลังเล่นอยู่ในขณะที่ยังใช้กริดอยู่จะแตกต่างกันโดยสิ้นเชิง แต่ละเกมคือ 1 ต่อ 1 และไม่มีทีม แต่ละรายการจะต่อสู้เพื่อตัวเองและมีเพียงหนึ่งเดียวเท่านั้นที่จะเป็นผู้ชนะรอบชิงชนะเลิศ

คอนโทรลเลอร์ใช้ JavaScript และ JavaScript เป็นภาษาสคริปต์ฝั่งไคลเอ็นต์เท่านั้นที่สนับสนุนเบราว์เซอร์ส่วนใหญ่คำตอบทั้งหมดจะต้องเขียนด้วย JavaScript เช่นกัน

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

เพลย์

ข้อมูลพื้นฐานเกี่ยวกับ

คำตอบสำหรับคำถามนี้ทุกคนเป็นตัวแทนของผู้เล่น เกมการแข่งขันระหว่างผู้เล่นสองP1และP2 แต่ละควบคุมผู้เล่นฝูง 8 บอทหมายเลขตั้งแต่ 0 ถึง 7 เกมจะเกิดขึ้นในตาราง , 128 × 64 เซลล์ที่เกิดเหตุซึ่งอยู่ด้านล่าง 8 แถวเริ่มจากการเป็นผนัง (ที่บล็อก ') และแถวอื่น ๆ เริ่มต้นเป็นอากาศ เซลล์นอกขอบเขตกริดถือว่าเป็นอากาศ

พิกัด x ของกริดจะอยู่ในช่วงจาก 0 ทางด้านซ้ายถึง 127 ทางด้านขวาและ y มีช่วงจาก 0 ที่ด้านบนถึง 63 ที่ด้านล่าง

ตัวอย่างตารางเริ่มต้น:

บ็อตจะอยู่ในแนวเดียวกับเซลล์กริดเสมอและบ็อตจำนวนมากอาจอยู่ในเซลล์เดียวกัน บอตสามารถครอบครองเซลล์อากาศได้เท่านั้น บอทของ P1 มักจะเริ่มในบรรทัดที่ 0-7 ที่ด้านซ้ายสุดของแถวเหนือกำแพงและบอทของ P2 จะเริ่มในแถวที่ 7-0 ที่ด้านขวาสุดเสมอ

เพื่อนบ้านของบอทหรือมือถือเป็น 8 เซลล์โดยตรงมุมฉากและเส้นทแยงมุมกับมัน

เขตการมอง ( FOV ) ของบอทคือเซลล์สี่เหลี่ยมจัตุรัสขนาด 13 × 13 ที่มีศูนย์กลางอยู่ที่บอท บอทของเซลล์หรือศัตรูถูกกล่าวว่าอยู่ใน FOV ของผู้เล่นหากอยู่ใน FOV อย่างน้อยหนึ่งบอทของผู้เล่น

การเคลื่อนไหว & การกระทำ

ในระหว่างเกมผู้เล่นแต่ละคนได้รับการย้าย 1000 ครั้ง P1 เลื่อนก่อนแล้วตามด้วย P2 จากนั้นตามด้วย P1 และต่อ ๆ ไปจนกระทั่งมีการเคลื่อนไหวทั้งหมด 2,000 ครั้งซึ่งจะทำให้เกมจบลง

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

การกระทำเริ่มต้นจะไม่ทำอะไรเลยโดยที่บอทไม่ย้ายหรือโต้ตอบกับกริด

การดำเนินการอื่น ๆ ที่มีการย้าย , คว้าและสถานที่ :

  • บอทสามารถย้ายไปยังหนึ่งในเซลล์ที่อยู่ใกล้เคียง C ได้ถ้า:

    • C ไม่อยู่นอกขอบเขต
    • C คืออากาศ (เช่นไม่ใช่กำแพง)
    • และอย่างน้อยหนึ่งในประเทศเพื่อนบ้านของ C เป็นกำแพง

    ถ้าสำเร็จบอทจะย้ายไปที่ C

  • บอทสามารถคว้าหนึ่งในเซลล์ใกล้เคียง C ได้ถ้า:

    • C ไม่อยู่นอกขอบเขต
    • C คือกำแพง
    • และบอทก็ไม่ได้ถือกำแพงอยู่แล้ว

    หากประสบความสำเร็จ C จะกลายเป็นอากาศและบอทก็จะถือกำแพงได้

  • บอทสามารถวางไว้ที่หนึ่งในเซลล์ใกล้เคียง C ถ้า:

    • C ไม่อยู่นอกขอบเขต
    • C คืออากาศ
    • ไม่มีบอทของผู้เล่นทั้งสองใช้ C
    • บอทกำลังถือกำแพงอยู่

    หากประสบความสำเร็จ C จะกลายเป็นกำแพงและบอทจะไม่ถือกำแพงอีกต่อไป

การกระทำที่ไม่สำเร็จส่งผลให้ไม่ทำอะไรเลย

เซลล์ที่ถูกครอบครองโดยบ็อตแบกผนังอย่างน้อยหนึ่งแห่งจะมีสี่เหลี่ยมจัตุรัสสีผนังเล็ก ๆ บอทเริ่มโดยไม่มีกำแพง

หน่วยความจำ

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

เป้าหมาย

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

หากบอตหลายตัวเคลื่อนที่ไปยังเป้าหมายระหว่างการย้ายผู้เล่นยังคงได้รับเพียงแค่จุดเดียว

หากเป้าหมายอยู่ในที่เดียวกันสำหรับการเคลื่อนไหว 500 ครั้งการเปลี่ยนตำแหน่งนั้นจะถูกสุ่มอีกครั้ง เมื่อใดก็ตามที่เป้าหมายอยู่ในตำแหน่งสุ่มจะรับประกันได้ว่าจะไม่ถูกวางไว้ในเซลล์ที่ครอบครองโดยบอท

โปรแกรมอะไร

เขียนเนื้อหาสำหรับฟังก์ชันนี้:

function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
    //body goes here
}

มันจะถูกเรียกหนึ่งครั้งทุกครั้งที่ผู้เล่นของคุณเคลื่อนไหวและต้องการคืนการกระทำที่คุณต้องการให้บอทของคุณแต่ละคนไปในระหว่างการเคลื่อนไหวนั้น

คุณสามารถใช้รหัสBaselineเป็นจุดเริ่มต้น

พารามิเตอร์

  • p1เป็นบูลนั่นคือtrueถ้าคุณเป็น P1 และfalseถ้าคุณเป็น P2
  • id เป็นจำนวนเต็มที่เป็น ID คำตอบของคำตอบของคุณ
    • คุณสามารถค้นหา ID ของคำตอบได้โดยคลิกที่ลิงก์ 'แชร์' ด้านล่างและค้นหาหมายเลขทันทีa/ใน URL
    • ID ของรายการทดสอบคือ -1
  • eid เป็นจำนวนเต็มที่เป็น ID คำตอบของคำตอบของศัตรูของคุณ
  • move เป็นจำนวนเต็มตั้งแต่ 1 ถึง 1,000 ที่บอกว่าคุณกำลังเคลื่อนไหวอะไร
  • goalเป็นวัตถุที่มีxและyคุณสมบัติ นี่คือพิกัดของเป้าหมาย พวกเขาจะได้รับแม้ว่าเป้าหมายจะออกมาจาก FOV ของคุณ
  • gridเป็นฟังก์ชั่นที่ใช้ใน x และ y grid(x,y)ขัดแย้งเช่น มันกลับมา:
    • -1สำหรับ 'ไม่ทราบ' หากอาร์กิวเมนต์ไม่ใช่จำนวนเต็มสองค่าหรือหากx,yไม่ได้อยู่ใน FOV ของคุณ
    • 0สำหรับ 'อากาศ' ถ้าx,yอยู่นอกขอบเขตหรือถ้าเซลล์ที่x,yมีอากาศ
    • 1สำหรับ 'ผนัง' หากเซลล์ที่x,yเป็นผนัง
  • botsคืออาร์เรย์ของบอท 8 อันของคุณ องค์ประกอบของวัตถุที่มีคุณสมบัติx, yและhasWall:

    • xและyเป็นพิกัดของบอท
    • hasWallคือtrueถ้าบอทจะแบกผนังและfalseหากไม่ได้

    bots มีการจัดเรียงตามปกติเสมอดัชนี Nth สอดคล้องกับหมายเลข bot N

  • ebotsเป็นอาร์เรย์ของวัตถุที่มีx, yและคุณสมบัติเช่นเดียวกับhasWall botsเฉพาะบอทของศัตรูใน FOV ของคุณเท่านั้นที่เข้าebotsมา ดังนั้นมันจะมีความยาว 0 ถ้าหากไม่มีบอทศัตรูใน FOV ของคุณ มันมีคำสั่งแบบสุ่ม
  • getMem เป็นฟังก์ชันที่ไม่มีอาร์กิวเมนต์ที่ส่งคืนหน่วยความจำของคุณ
  • setMem เป็นฟังก์ชั่นที่รับอาร์กิวเมนต์หนึ่งตัว M หาก M เป็นสายอักขระ 256 ตัวหรือน้อยกว่าหน่วยความจำของคุณจะถูกอัพเดตเป็น M ไม่เช่นนั้นจะไม่มีอะไรเกิดขึ้น

consoleวัตถุของเบราว์เซอร์นั้นมีให้สำหรับรายการทดสอบเพียงอย่างเดียว

ส่งคืนค่า

ฟังก์ชันของคุณจำเป็นต้องส่งคืนอาร์เรย์ของจำนวนเต็ม 8 จำนวนโดยมีค่าตั้งแต่ 0 ถึง 24 ค่าที่ดัชนี N คือการดำเนินการที่บอทหมายเลข N จะรับ

บอตทั้งหมดของคุณจะไม่ทำอะไรเลยถ้าฟังก์ชั่นของคุณ:

  • โยนความผิดพลาดใด ๆ ( ข้อผิดพลาด )
  • ใช้เวลานานกว่า20 มิลลิวินาทีในการดำเนินการ ( หมดเวลา )
  • ไม่ส่งคืนอาร์เรย์จำนวนเต็ม 8 จำนวนตั้งแต่ 0 ถึง 24 ( มีรูปแบบไม่ถูกต้อง )

เพื่อความสะดวกจะมีการแสดงจำนวนข้อผิดพลาดการหมดเวลาและการดำเนินการที่ผิดรูปแบบเมื่อเกมสิ้นสุด

แต่ละหมายเลขตั้งแต่ 0 ถึง 24 สอดคล้องกับการกระทำของบอทโดยเฉพาะ:

  • 0 ใช้เพื่อไม่ทำอะไรเลย
  • 1-8 สำหรับการเคลื่อนย้าย
  • 9-16 มีไว้สำหรับโลภ
  • 17-24 สำหรับการวาง

แต่ละค่า 8 ค่าสำหรับการเคลื่อนที่การจับและการวางสอดคล้องกับหนึ่งในเซลล์ใกล้เคียงของบอทดังที่แสดงไว้ที่นี่:

ตัวอย่างเช่น15การกระทำคือการจับเซลล์ด้านล่าง ธ ปท.

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

การกระทำที่ไม่ประสบความสำเร็จกลายเป็นทำ nothings และบอกว่าจะล้มเหลว ตัวนับการกระทำที่ล้มเหลวจะปรากฏขึ้นเมื่อเกมสิ้นสุด

กฎระเบียบ

ฉันอาจตัดสิทธิ์ผู้ใช้หรือคำตอบที่ไม่ปฏิบัติตามกฎเหล่านี้เป็นการชั่วคราวหรือถาวร ผลงานที่ถูกตัดสิทธิ์จะไม่มีสิทธิ์ชนะ

  • เมื่อประกาศตัวแปรหรือฟังก์ชั่นคุณต้องใช้varคำหลัก
    เช่นvar x = 10หรือvar sum = function(a, b){ return a + b }
    สิ่งต่าง ๆ ที่ประกาศโดยไม่varกลายเป็นส่วนกลางและอาจรบกวนการทำงานของคอนโทรลเลอร์ มีการดำเนินการตามขั้นตอนต่าง ๆ เพื่อให้การแทรกแซงนี้เป็นไปไม่ได้ แต่ให้ทำเช่นนี้เพื่อให้แน่ใจ

  • รหัสของคุณไม่ควรทำงานช้าหรือเสียเวลา
    เป็นไปไม่ได้ที่จะหยุดการทำงานของ JavaScript ในการทำงานกลางคันดังนั้นโค้ดของผู้เล่นแต่ละคนจะถูกเรียกใช้จนเสร็จสมบูรณ์ หากรหัสของคุณใช้เวลานานในการรันทุกคนที่ใช้งานเครื่องเล่นของคุณจะสังเกตเห็นและรำคาญ โดยทั่วไปแล้วรายการจะทำงานได้ดีภายในขีด จำกัด 20ms

  • คุณต้องใช้รหัสที่เข้ากันได้กับ ECMAScript 5 ใน Firefox เวอร์ชันล่าสุดเนื่องจากนี่คือที่ที่ฉันจะใช้งาน อย่าใช้คุณสมบัติจาก ECMAScript 6เนื่องจากยังไม่รองรับในเบราว์เซอร์จำนวนมาก
  • คุณอาจตอบได้สูงสุด 3ครั้ง แต่หากกลยุทธ์ของคุณแตกต่างกันมาก คุณสามารถแก้ไขคำตอบได้มากเท่าที่ต้องการ
  • คุณอาจจะไม่พยายามที่จะมีการจัดเรียงของหน่วยความจำใด ๆ ยกเว้นผ่านการใช้และgetMemsetMem
  • คุณต้องไม่พยายามเข้าถึงหรือดัดแปลงคอนโทรลเลอร์รหัสของผู้เล่นอื่นหรือแหล่งข้อมูลภายนอก
  • คุณไม่สามารถพยายามแก้ไขสิ่งใด ๆ ที่มีอยู่ใน JavaScript
  • คำตอบไม่จำเป็นต้องกำหนด Math.randomคุณอาจจะใช้

รูปแบบคำตอบ

#EntryName

Notes, etc.

<!-- language: lang-js -->

    //function body
    //probably on multiple lines

More notes, etc.

บล็อกโค้ดหลายบรรทัดแรกจะต้องมีเนื้อหาของฟังก์ชันของคุณ
ชื่อรายการมีความยาวได้ไม่เกิน 20 ตัวอักษร

รายการของคุณจะปรากฏขึ้นในตัวควบคุมที่มีชื่อเรื่องEntryName - Username [answer ID]รวมทั้ง[DQ]ถ้ามันถูกตัดสิทธิ์

การชนะ

เมื่อคำถามเกิดขึ้นเป็นเวลาอย่างน้อย 3 สัปดาห์และเมื่อการตอบคำถามสิ้นสุดลงฉันจะครองตำแหน่งแชมป์

ฉันจะใช้คุณสมบัติการทำงานอัตโนมัติของคอนโทรลเลอร์ ในรอบการทำงานอัตโนมัติผู้เล่นที่ไม่ได้ถูกตัดสิทธิ์ทุกคนเล่นเกมสองเกมโดยมีเกมหนึ่งเป็น P1 หนึ่งเกมเป็น P2 (รอบสองรอบ)

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

ฉันจะใช้ Firefox บนแล็ปท็อปที่มี Windows 8.1 64 บิต, RAM 4 GB และโปรเซสเซอร์ Quad-Core 1.6GHz

รางวัล

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

ตัวควบคุม

เรียกใช้ตัวอย่างนี้หรือไปที่JSFiddle นี้เพื่อใช้ตัวควบคุม มันเริ่มต้นด้วยการสุ่มเลือกผู้เล่น ฉันทดสอบอย่างถี่ถ้วนใน Firefox และ Chrome เท่านั้น

<style>html *{font-family:Consolas,Arial,sans-serif}canvas{margin:6px}button,input table,select{font-size:100%}textarea{font-family:monospace}input[type=text],textarea{padding:2px}textarea[readonly]{background-color:#eee}select{width:250pt;margin:3px 0}input[type=radio]{vertical-align:-.25em}input[type=checkbox]{vertical-align:-.15em}.c{margin:12px}.h{font-size:125%;font-weight:700}#main td{padding:12px;text-align:left}#main table{margin-left:auto;margin-right:auto}#main{text-align:center}#title{margin:12px;font-size:175%;font-weight:700;color:#333}#delay{text-align:right}#statsTable table{border-collapse:collapse}#statsTable td{border:1px solid gray;padding:3pt;font-family:monospace;text-align:center}#footnotes{margin:18px 0 0;font-size:75%}#arWrapper{border:2px solid red;background-color:#fff4f4}</style><div id=loadStatus>Loading entries...</div><div id=main><div id=title>Block Building Bot Flocks</div><div><span id=p1Title class=p1Color></span> vs. <span id=p2Title class=p2Color></span></div><canvas id=canvas>Canvas unsupported!</canvas><div><span id=p1Score class=p1Color>0</span> | <span id=moveCounter>0</span> | <span id=p2Score class=p2Color>0</span></div><div class=c><button id=runPause type=button onclick=runPause()>Run</button> <button id=moveOnce type=button onclick=moveOnce()>Move Once</button> <button type=button onclick=newGame()>New Game</button></div><div class=c><input id=delay size=4 value=20> ms delay <input id=showNumbers type=checkbox onclick=toggleNumbers()><label for=showNumbers>Show bot numbers</label>&nbsp;<input id=showLOS type=checkbox onclick=toggleLOS()><label for=showLOS>Show field of view</label></div><table><tr><td><div id=p1Header class="p1Color h">Player 1</div><div><select id=p1Select onchange=changeSelect(!0)></select></div><div><a id=p1Link href=javascript:;>Answer Link</a></div><td><div id=p2Header class="p2Color h">Player 2</div><div><select id=p2Select onchange=changeSelect(!1)></select></div><div><a id=p2Link href=javascript:;>Answer Link</a></div></table><div>Test Entry</div><div><textarea id=testEntry rows=8 cols=64>return [0,0,0,0,0,0,0,0]</textarea></div><div class=c><button type=button onclick=autorun()>Autorun N Rounds</button> N&nbsp;=&nbsp;<input id=N size=4 value=1> <input id=arTestEntry type=checkbox><label for=arTestEntry>Include Test Entry</label></div><div id=footnotes><input id=debug type=checkbox onclick=toggleDebug()><label for=debug>Console debug messages</label>&nbsp;| Scale: <input id=sc1 type=radio name=sc value=1><label for=sc1>Micro</label><input id=sc3 type=radio name=sc value=3><label for=sc3>Small</label><input id=sc6 type=radio name=sc value=6 checked><label for=sc6>Normal</label><input id=sc9 type=radio name=sc value=9><label for=sc9>Large</label>&nbsp;| Colors: <input id=normalCo type=radio name=co value=normal checked><label for=normalCo>Normal</label><input id=pastelCo type=radio name=co value=pastel><label for=pastelCo>Pastels</label><input id=neonCo type=radio name=co value=neon><label for=neonCo>Neon</label>&nbsp; <button type=button onclick=reload()>Reload</button><div id=invalidWrapper><br>No entry name/code found: <span id=invalid></span></div></div></div><div id=arWrapper><div id=arInfo class=c>Autorun in progress. Running game <span id=arProgress></span>.</div><div id=arResults><div class="c h">Autorun Results</div><div class=c>Players: <span id=arPlayers></span><br>Rounds: <span id=arRounds></span><br>Games per round: <span id=arGpR></span><br>Total games: <span id=arTotal></span><br></div><div class=c><strong>Leaderboard:</strong></div><div id=leaderboard class=c></div><div class=c>(W = wins, T = ties, L = losses, G = total goals, E = errors, I = timeouts, M = malformed actions, F = failed actions)</div><div class=c><strong>Player vs. Player Statistics:</strong></div><div id=statsTable class=c></div><div class=c>The top row has the ID's of P1.<br>The left column has the ID's of P2.<br>Every other cell not on the diagonal has the form "[P1 win count] [tie count] [P2 win count]".</div><div class=c><button type=button onclick=closeAutorun()>Close</button></div></div></div><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script>function setGlobals(e){G={},G.QID=50690,G.SITE="codegolf",G.DQ_ANSWERS=[],G.DQ_USERS=[],G.DEBUG=Q("#debug").is(":checked"),G.SHOW_NUMBERS=Q("#showNumbers").is(":checked"),G.SHOW_LOS=Q("#showLOS").is(":checked"),G.BOTS=8,G.LOS=6,G.W=128,G.H=64,G.SCALE=e?6:parseInt(Q('input[name="sc"]:checked').val()),G.CW=G.SCALE*G.W,G.CH=G.SCALE*G.H,G.TOTAL_MOVES=2e3,G.GOAL_LIFESPAN=500,G.MEM_MAX_LENGTH=256,G.TIME_LIMIT=20;var t=Q('input[name="co"]:checked').val();e||"normal"===t?G.COLORS={AIR:"#ccc",WALL:"#888",GOAL:"rgba(255,255,0,0.6)",BG:"#f7f7f7",P1:"#00f",P1_TEXT:"#008",P1_LOS:"rgba(0,0,255,0.1)",P2:"#f00",P2_TEXT:"#800",P2_LOS:"rgba(255,0,0,0.1)"}:"pastel"===t?G.COLORS={AIR:"#cef0ff",WALL:"#66cc66",GOAL:"rgba(0,0,0,0.3)",BG:"#fdfde6",P1:"#f4a034",P1_TEXT:"#a35f00",P1_LOS:"rgba(255,179,71,0.2)",P2:"#f67cf6",P2_TEXT:"#b408b4",P2_LOS:"rgba(249,128,249,0.2)"}:"neon"===t&&(G.COLORS={AIR:"#000",WALL:"#444",GOAL:"rgba(255,255,0,0.9)",BG:"#999",P1:"#0f0",P1_TEXT:"#5f5",P1_LOS:"rgba(255,128,0,0.15)",P2:"#f0f",P2_TEXT:"#f5f",P2_LOS:"rgba(0,255,255,0.15)"}),G.SCOREBOARD={P1SCORE:void 0,MOVE:void 0,P2SCORE:void 0},G.CTX=void 0,G.PLAYERS=void 0,G.GAME=void 0,G.TIMER=void 0,G.RUNNING=!1}function reload(){var e="undefined"==typeof G;e||stopTimer(),setGlobals(e);var t=Q("#canvas");t.width(G.CW).height(G.CH).prop({width:G.CW,height:G.CH}),G.CTX=t[0].getContext("2d"),G.CTX.font=(2*G.SCALE).toString()+"px Courier New",G.SCOREBOARD.P1SCORE=Q("#p1Score"),G.SCOREBOARD.MOVE=Q("#moveCounter"),G.SCOREBOARD.P2SCORE=Q("#p2Score"),Q("body").css("background-color",G.COLORS.BG),Q(".p1Color").css("color",G.COLORS.P1),Q(".p2Color").css("color",G.COLORS.P2),Q("#invalidWrapper").hide(),Q("#arWrapper").hide(),loadAnswers(G.SITE,G.QID,function(e){Q.isArray(e)?(Q("#loadStatus").remove(),loadPlayers(e),newGame()):Q("#loadStatus").text("Error loading entries - "+e)})}function maskedEval(e,t){var r={};for(i in this)r[i]=void 0;for(i in t)t.hasOwnProperty(i)&&(r[i]=t[i]);return new Function("with(this) { "+e+";}").call(r)}function toKey(e,t){return G.W*t+e}function fromKey(e){return{x:e%G.W,y:Math.floor(e/G.W)}}function outOfBounds(e,t){return 0>e||e>=G.W||0>t||t>=G.H}function rnd(e){return Math.floor(Math.random()*e)}function isInt(e){return"number"==typeof e&&e%1===0}function isString(e){return"string"==typeof e||e instanceof String}function decode(e){return Q("<textarea>").html(e).text()}function shuffle(e){for(var t,r,o=e.length;o;t=rnd(o),r=e[--o],e[o]=e[t],e[t]=r);}function makeTable(e){for(var t=Q("<table>"),r=0;r<e.length;r++){for(var o=Q("<tr>"),a=0;a<e[r].length;a++)o.append(Q("<td>").text(e[r][a]));t.append(o)}return t}function toggleDebug(){G.DEBUG=Q("#debug").is(":checked")}function toggleNumbers(){G.SHOW_NUMBERS=Q("#showNumbers").is(":checked"),drawGame(G.GAME)}function toggleLOS(){G.SHOW_LOS=Q("#showLOS").is(":checked"),drawGame(G.GAME)}function closeAutorun(){Q("#arWrapper").hide(),Q("#main").show()}function changeSelect(e){var t=Q(e?"#p1Select":"#p2Select").val(),r=Q(e?"#p1Link":"#p2Link");null===t&&0>t?r.attr("href","javascript:;"):r.attr("href",G.PLAYERS[t].link)}function stopTimer(){"undefined"!=typeof G.TIMER&&clearInterval(G.TIMER)}function moveOnce(){gameOver(G.GAME)||(moveGame(G.GAME),drawGame(G.GAME),gameOver(G.GAME)&&(stopTimer(),Q("#runPause").text("Run").prop("disabled",!0),Q("#moveOnce").prop("disabled",!0),G.DEBUG&&console.log("======== GAME OVER: "+G.GAME.p1.score+" TO "+G.GAME.p2.score+" ========"),alert(gameOverMessage(G.GAME))))}function runPause(){if(G.RUNNING)stopTimer(),Q("#runPause").text("Run"),Q("#moveOnce").prop("disabled",!1);else{var e=parseInt(Q("#delay").val());if(isNaN(e)||0>e)return void alert("Delay must be a non-negative integer.");Q("#runPause").text("Pause"),Q("#moveOnce").prop("disabled",!0),G.TIMER=setInterval(moveOnce,e)}G.RUNNING=!G.RUNNING}function newGame(){stopTimer();var e=G.PLAYERS[Q("#p1Select").val()],t=G.PLAYERS[Q("#p2Select").val()];G.RUNNING=!1,Q("#runPause").text("Run").prop("disabled",!1),Q("#moveOnce").prop("disabled",!1),Q("#p1Title").text(e.title),Q("#p2Title").text(t.title),G.GAME=createGame(e,t),drawGame(G.GAME)}function tryParse(e,t){var r=parseInt(Q(e).val());return!isNaN(r)&&r>=0?r:void alert(t+" must be a non-negative integer.")}function autorun(){function e(){for(var e=new Array(a.length),t={},r=["wins","goals","errors","timeouts","malformed","invalid"],n=0;n<e.length;n++){t.wins=t.ties=t.losses=t.goals=t.errors=t.timeouts=t.malformed=t.invalid=0;for(var l=0;l<e.length;l++)n!==l&&(t.ties+=s[n][l].ties+s[l][n].ties,t.losses+=s[n][l].p2.wins+s[l][n].p1.wins,r.forEach(function(e){t[e]+=s[n][l].p1[e]+s[l][n].p2[e]}));e[n]={wins:t.wins,text:a[n].title+" : "+t.wins+"W, "+t.ties+"T, "+t.losses+"L, "+t.goals+"G, "+t.errors+"E, "+t.timeouts+"I, "+t.malformed+"M, "+t.invalid+"F"}}e=e.sort(function(e,t){return t.wins-e.wins}).map(function(t,r){return r+1+". "+t.text+(r<e.length-1?"<br>":"")});for(var i=new Array(s.length+1),G=0;G<i.length;G++){i[G]=new Array(s.length+1);for(var c=0;c<i.length;c++){var f;i[G][c]=0===c&&0===G?"P2\\P1":0===c?a[G-1].id:0===G?a[c-1].id:(f=s[c-1][G-1])?f.p1.wins+" "+f.ties+" "+f.p2.wins:"-"}}Q("#arPlayers").text(a.length),Q("#arRounds").text(o),Q("#arGpR").text(S/o),Q("#arTotal").text(S),Q("#leaderboard").empty().append(e),Q("#statsTable").empty().append(makeTable(i)),Q("#arInfo").hide(),Q("#arResults").show()}function t(e,t){for(var r=createGame(a[e],a[t]);!gameOver(r);)moveGame(r);r.p1.score>r.p2.score?s[e][t].p1.wins++:r.p1.score<r.p2.score?s[e][t].p2.wins++:s[e][t].ties++,["p1","p2"].forEach(function(o){s[e][t][o].goals+=r[o].score,s[e][t][o].errors+=r[o].stats.errors,s[e][t][o].timeouts+=r[o].stats.timeouts,s[e][t][o].malformed+=r[o].stats.malformed,s[e][t][o].invalid+=r[o].stats.invalid.reduce(function(e,t){return e+t},0)})}function r(){if(c!==f&&(t(c,f),++p<=S&&Q("#arProgress").text(p+"/"+S)),f+1<a.length)f++;else if(f=0,c+1<a.length)c++;else{if(c=0,!(o>i+1))return void e();i++}setTimeout(r,0)}var o=parseInt(Q("#N").val());if(isNaN(o)||1>o)return void alert("N must be a positive integer.");var a=[];Q("#arTestEntry").is(":checked")&&a.push(G.PLAYERS[0]);for(var n=1;n<G.PLAYERS.length;n++)G.PLAYERS[n].dq||a.push(G.PLAYERS[n]);for(var s=new Array(a.length),n=0;n<a.length;n++){s[n]=new Array(a.length);for(var l=0;l<a.length;l++)n!==l&&(s[n][l]={ties:0,p1:{wins:0,goals:0,errors:0,timeouts:0,malformed:0,invalid:0},p2:{wins:0,goals:0,errors:0,timeouts:0,malformed:0,invalid:0}})}var i=0,c=0,f=0,p=1,S=o*a.length*(a.length-1);Q("#arProgress").text("1/"+S),Q("#main").hide(),Q("#arInfo").show(),Q("#arResults").hide(),Q("#arWrapper").show(),setTimeout(r,0)}function gameOver(e){return e.move>=G.TOTAL_MOVES}function gameOverMessage(e){function t(e,t){return"P"+(t?1:2)+": "+e.entry.title+"\nScore: "+e.score+"\nErrors: "+e.stats.errors+"\nTimeouts: "+e.stats.timeouts+"\nMalformed actions: "+e.stats.malformed+"\nFailed actions: ["+e.stats.invalid.toString().replace(/,/g,", ")+"]"}var r="GAME OVER - ";return r+=e.p1.score>e.p2.score?"PLAYER 1 WINS":e.p1.score<e.p2.score?"PLAYER 2 WINS":"TIE GAME",r+="\n\n"+t(e.p1,!0)+"\n\n"+t(e.p2,!1)}function createGame(e,t){function r(e){return{entry:e,bots:new Array(G.BOTS),mem:"",score:0,stats:{errors:0,timeouts:0,malformed:0,invalid:Array.apply(null,new Array(G.BOTS)).map(Number.prototype.valueOf,0)}}}var o={},a=Math.floor(.875*G.H)-1;o.move=0,o.walls=new Array(G.H);for(var n=0;n<G.H;n++){o.walls[n]=new Array(G.W);for(var s=0;s<G.W;s++)o.walls[n][s]=n>a}o.p1=r(e),o.p2=r(t);for(var l=0;l<G.BOTS;l++)o.p1.bots[l]={x:l,y:a,hasWall:!1},o.p2.bots[l]={x:G.W-1-l,y:a,hasWall:!1};if(-1===o.p1.entry.id||-1===o.p2.entry.id){var i=decode(Q("#testEntry").val());-1===o.p1.entry.id&&(o.p1.entry.code=i),-1===o.p2.entry.id&&(o.p2.entry.code=i)}return resetGoal(o),G.DEBUG&&console.log("======== NEW GAME: "+o.p1.entry.title+" VS "+o.p2.entry.title+" ========"),o}function moveGame(e){movePlayer(e,++e.move%2===1),++e.goal.age>=G.GOAL_LIFESPAN&&resetGoal(e)}function setupParams(e,t){function r(e,t){var r=toKey(e,t);if(!n.hasOwnProperty(r)){n[r]=!1;for(var a=0;a<G.BOTS;a++)if(Math.abs(o.bots[a].x-e)<=G.LOS&&Math.abs(o.bots[a].y-t)<=G.LOS){n[r]=!0;break}}return n[r]}var o=t?e.p1:e.p2,a=t?e.p2:e.p1,n={},s={};s.p1=t,s.id=o.entry.id,s.eid=a.entry.id,s.score=o.score,s.escore=a.score,s.move=Math.floor((e.move+1)/2),s.goal={x:e.goal.x,y:e.goal.y},s.getMem=function(){return o.mem},s.setMem=function(e){isString(e)&&e.length<=G.MEM_MAX_LENGTH&&(o.mem=e)},s.grid=function(t,o){return isInt(t)&&isInt(o)&&r(t,o)?outOfBounds(t,o)?0:e.walls[o][t]?1:0:-1},s.bots=new Array(G.BOTS),s.ebots=[];for(var l=0;l<G.BOTS;l++)s.bots[l]={x:o.bots[l].x,y:o.bots[l].y,hasWall:o.bots[l].hasWall},r(a.bots[l].x,a.bots[l].y)&&s.ebots.push({x:a.bots[l].x,y:a.bots[l].y,hasWall:a.bots[l].hasWall});return shuffle(s.ebots),-1===o.entry.id&&(s.console=console),s}function movePlayer(e,t){var r,o,a=t?e.p1:e.p2,n=t?e.p2:e.p1,s=setupParams(e,t);G.DEBUG&&(console.log("######## MOVE "+e.move+" - P"+(t?1:2)+" ########"),console.log("PARAMETERS:"),console.log(s)),o=performance.now();try{r=maskedEval(a.entry.code,s)}catch(n){return a.stats.errors++,void(G.DEBUG&&(console.log("!!!! ERRORED !!!!"),console.log(n)))}if(o=performance.now()-o,G.DEBUG&&console.log("TIME TAKEN: "+o+"ms"),o>G.TIME_LIMIT)return a.stats.timeouts++,void(G.DEBUG&&console.log("!!!! TIMED OUT !!!!"));if(G.DEBUG&&(console.log("ACTIONS:"),console.log(r)),!Array.isArray(r)||r.length!==G.BOTS)return a.stats.malformed++,void(G.DEBUG&&console.log("!!!! MALFORMED ACTIONS !!!!"));for(var l=0;l<G.BOTS;l++)if(!isInt(r[l])||r[l]<0||r[l]>24)return a.stats.malformed++,void(G.DEBUG&&console.log("!!!! MALFORMED ACTIONS !!!!"));performActions(e,a,r)}function performActions(e,t,r){function o(e){t.stats.invalid[e]++,G.DEBUG&&console.log("!! BOT"+e+" ACTION FAILED !!")}function a(e){return e.x!==i||e.y!==c}for(var n=!1,s=0;s<G.BOTS;s++){var l=r[s];if(l){var i,c;switch((l-1)%8){case 0:i=-1,c=-1;break;case 1:i=0,c=-1;break;case 2:i=1,c=-1;break;case 3:i=-1,c=0;break;case 4:i=1,c=0;break;case 5:i=-1,c=1;break;case 6:i=0,c=1;break;case 7:i=1,c=1}if(i+=t.bots[s].x,c+=t.bots[s].y,outOfBounds(i,c))o(s);else switch(Math.floor((l-1)/8)){case 0:!e.walls[c][i]&&(i>0&&c>0&&e.walls[c-1][i-1]||c>0&&e.walls[c-1][i]||i<G.W-1&&c>0&&e.walls[c-1][i+1]||i>0&&e.walls[c][i-1]||i<G.W-1&&e.walls[c][i+1]||i>0&&c<G.H-1&&e.walls[c+1][i-1]||c<G.H-1&&e.walls[c+1][i]||i<G.W-1&&c<G.H-1&&e.walls[c+1][i+1])?(t.bots[s].x=i,t.bots[s].y=c,i!==e.goal.x||c!==e.goal.y||n||(n=!0,G.DEBUG&&console.log("** BOT"+s+" REACHED GOAL **"))):o(s);break;case 1:e.walls[c][i]&&!t.bots[s].hasWall?(e.walls[c][i]=!1,t.bots[s].hasWall=!0):o(s);break;case 2:!e.walls[c][i]&&t.bots[s].hasWall&&e.p1.bots.every(a)&&e.p2.bots.every(a)?(e.walls[c][i]=!0,t.bots[s].hasWall=!1):o(s)}}}n&&(t.score++,resetGoal(e)),G.DEBUG&&(console.log("FINAL PLAYER STATE:"),console.log(t))}function resetGoal(e){for(var t={},r=[],o=0;o<G.BOTS;o++)t[toKey(e.p1.bots[o].x,e.p1.bots[o].y)]=!0,t[toKey(e.p2.bots[o].x,e.p2.bots[o].y)]=!0;for(var a=0;a<G.H;a++)for(var n=0;n<G.W;n++){var s=toKey(n,a);t.hasOwnProperty(s)||r.push(s)}var l=fromKey(r[rnd(r.length)]);e.goal={age:0,x:l.x,y:l.y}}function drawGame(e){function t(e,t){G.CTX.fillRect(e*G.SCALE,t*G.SCALE,G.SCALE,G.SCALE)}function r(e,t){G.CTX.fillRect(e*G.SCALE+1,t*G.SCALE+1,G.SCALE-2,G.SCALE-2)}G.CTX.fillStyle=G.COLORS.AIR,G.CTX.fillRect(0,0,G.CW,G.CH),G.CTX.fillStyle=G.COLORS.WALL;for(var o=0;o<G.H;o++)for(var a=0;a<G.W;a++)e.walls[o][a]&&t(a,o);if(G.SHOW_LOS){var n=(2*G.LOS+1)*G.SCALE;G.CTX.fillStyle=G.COLORS.P1_LOS;for(var s=0;s<G.BOTS;s++)G.CTX.fillRect((e.p1.bots[s].x-G.LOS)*G.SCALE,(e.p1.bots[s].y-G.LOS)*G.SCALE,n,n);G.CTX.fillStyle=G.COLORS.P2_LOS;for(var s=0;s<G.BOTS;s++)G.CTX.fillRect((e.p2.bots[s].x-G.LOS)*G.SCALE,(e.p2.bots[s].y-G.LOS)*G.SCALE,n,n)}G.CTX.fillStyle=G.COLORS.P1;for(var s=0;s<G.BOTS;s++)t(e.p1.bots[s].x,e.p1.bots[s].y);G.CTX.fillStyle=G.COLORS.P2;for(var s=0;s<G.BOTS;s++)t(e.p2.bots[s].x,e.p2.bots[s].y);G.CTX.fillStyle=G.COLORS.WALL;for(var s=0;s<G.BOTS;s++)e.p1.bots[s].hasWall&&r(e.p1.bots[s].x,e.p1.bots[s].y),e.p2.bots[s].hasWall&&r(e.p2.bots[s].x,e.p2.bots[s].y);if(G.SHOW_NUMBERS){var l=-.1,i=2.75;G.CTX.fillStyle=G.COLORS.P1_TEXT;for(var s=0;s<G.BOTS;s++)G.CTX.fillText(s.toString(),(e.p1.bots[s].x+l)*G.SCALE,(e.p1.bots[s].y+i)*G.SCALE);G.CTX.fillStyle=G.COLORS.P2_TEXT;for(var s=0;s<G.BOTS;s++)G.CTX.fillText(s.toString(),(e.p2.bots[s].x+l)*G.SCALE,(e.p2.bots[s].y+i)*G.SCALE)}G.CTX.fillStyle=G.COLORS.GOAL,t(e.goal.x+1,e.goal.y),t(e.goal.x-1,e.goal.y),t(e.goal.x,e.goal.y+1),t(e.goal.x,e.goal.y-1),G.SCOREBOARD.P1SCORE.text(e.p1.score),G.SCOREBOARD.MOVE.text(e.move),G.SCOREBOARD.P2SCORE.text(e.p2.score)}function loadPlayers(e){var t=/<pre\b[^>]*><code\b[^>]*>([\s\S]*?)<\/code><\/pre>/,r=/<h1\b[^>]*>(.*?)<\/h1>/;G.PLAYERS=[];var o={id:-1,dq:!1,code:void 0,link:"javascript:;",title:"TEST ENTRY [-1]"};G.PLAYERS.push(o);var a=[];e.forEach(function(e){var o=decode(e.owner.display_name),n=t.exec(e.body),s=r.exec(e.body);if(null===n||n.length<=1||null===s||s.length<=1)return a.push(" "),void a.push(Q("<a>").text(o).attr("href",e.link));var l={};l.id=e.answer_id,l.dq=G.DQ_ANSWERS.indexOf(e.answer_id)>-1||G.DQ_USERS.indexOf(e.owner.user_id)>-1,l.code=decode(n[1]),l.link=e.link,l.title=s[1].substring(0,20)+" - "+o+" ["+l.id.toString()+"]",l.dq&&(l.title+="[DQ]"),G.PLAYERS.push(l)}),a.length>0&&(Q("#invalid").empty().append(a),Q("#invalidWrapper").show());for(var n=new Array(G.PLAYERS.length),s=new Array(G.PLAYERS.length),l=0;l<G.PLAYERS.length;l++)n[l]=Q("<option>").text(G.PLAYERS[l].title).val(l),s[l]=Q("<option>").text(G.PLAYERS[l].title).val(l);Q("#p1Select").empty().append(n).val(rnd(G.PLAYERS.length)),changeSelect(!0),Q("#p2Select").empty().append(s).val(rnd(G.PLAYERS.length)),changeSelect(!1)}function loadAnswers(e,t,r){function o(){Q.get("https://api.stackexchange.com/2.2/questions/"+t.toString()+"/answers?page="+(s++).toString()+"&pagesize=100&order=asc&sort=creation&site="+e+"&filter=!YOKGPOBC5Yad4mOOn8Z4WcAE6q",a)}function a(e){e.hasOwnProperty("error_id")?r(e.error_id.toString()):(n=n.concat(e.items),e.hasMore?o():r(n))}var n=[],s=1;o(s,a)}Q=jQuery,Q(reload);</script>

คำถามนี้มีห้องแชทของตัวเอง ฉันจะโพสต์กระดานผู้นำที่นั่นทุกสองสามวัน



3
ขอขอบคุณที่ปุ่มเพื่อปรับขนาดของตารางที่ฉันสามารถเรียกใช้กองโค้ดบนโทรศัพท์ของฉัน :)
Trichoplax

3
จำนวนมากของความรัก (codegolf.stackexchange.com/questions/50768/) แต่ที่ไม่ง่าย
edc65

3
นี่คือสิ่งที่ชวนให้หลงใหล
DLosc

1
@ สตีเฟ่นใช่ กฎข้อที่ 4: "คุณสามารถแก้ไขคำตอบได้มากเท่าที่ต้องการ"
งานอดิเรกของ Calvin

คำตอบ:


13

อัศวินดำ

ชื่อของบ็อตมาจากแผนแรกเพื่อให้มันสามารถเคลื่อนไหวได้เหมือนอัศวินหมากรุก: สองตัวขึ้นหนึ่งอัน ฯลฯ ซึ่งจะเร็วกว่าในบางกรณี

var moves = new Array(8),
    mem = getMem(), newMem = '';

var decodeMem = function(){
  //mmtxy
  for(var ind = 0; ind < 8; ind++){
    var sub = mem.substr(ind * 5, 5)
    bots[ind].lastMove = parseInt(sub[0], 36);
    bots[ind].last2Move = parseInt(sub[1], 36);
    bots[ind].timesStill = sub.charCodeAt(2) - 48;
    bots[ind].lastX = sub.charCodeAt(3) - 187;
    bots[ind].lastY = sub.charCodeAt(4) - 187;
  }
}
decodeMem();

var distanceTo = function(fromX, fromY, toX, toY){
  // Chebyshev distance
  return Math.max(Math.abs(fromX - toX),
                  Math.abs(fromY - toY));
}

var direction = function(from, to){ // Math.sign()
  var diff = to - from;
  return diff > 0 ? 1 : (diff < 0 ? -1 : 0);
}

var dirs = [
  [1, 2, 3],
  [4, 0, 5],
  [6, 7, 8]
];

var moveTo = function(from, to){
  var prioritiesWall = [
    [0],
    [1, 2, 4, 17, 18, 20, 19, 22, 23, 21, 3, 6, 5, 7, 24, 8],
    [2, 3, 1, 17, 19, 18, 23, 22, 24, 4, 5, 20, 21, 6, 8, 7],
    [3, 2, 5, 19, 18, 21, 17, 24, 23, 20, 1, 8, 4, 7, 22, 6],
    [4, 1, 6, 22, 17, 20, 21, 24, 19, 2, 7, 18, 23, 3, 8, 5],
    [5, 3, 8, 24, 19, 21, 20, 22, 17, 2, 7, 18, 23, 1, 6, 4],
    [6, 4, 7, 22, 20, 23, 17, 24, 18, 21, 1, 8, 2, 5, 19, 3],
    [7, 8, 6, 22, 24, 23, 18, 17, 19, 4, 5, 20, 21, 1, 3, 2],
    [8, 5, 7, 24, 21, 23, 19, 22, 18, 20, 3, 6, 2, 4, 17, 1]
  ];
  var prioritiesNoWall = [
    [9, 10, 11, 12, 13, 14, 15, 16, 0],
    [1, 2, 4, 9, 16, 10, 12, 3, 6, 11, 14, 5, 7, 13, 15, 8],
    [2, 3, 1, 10, 15, 14, 16, 4, 5, 12, 13, 9, 11, 6, 8, 7],
    [3, 2, 5, 11, 14, 10, 13, 1, 8, 9, 16, 4, 7, 12, 15, 6],
    [4, 1, 6, 12, 13, 16, 11, 2, 7, 10, 15, 9, 14, 3, 8, 5],
    [5, 3, 8, 13, 12, 14, 9, 2, 7, 10, 15, 11, 16, 1, 6, 4],
    [6, 4, 7, 14, 11, 12, 15, 1, 8, 9, 16, 2, 5, 10, 13, 3],
    [7, 8, 6, 15, 10, 9, 11, 4, 5, 12, 13, 14, 16, 1, 3, 2],
    [8, 5, 7, 16, 9, 13, 15, 3, 6, 11, 14, 2, 4, 10, 12, 1]
  ];

  var dir = dirs[direction(from.y, to.y) + 1][direction(from.x, to.x) + 1],
      method = from.hasWall ? prioritiesWall[dir] : prioritiesNoWall[dir];

  if(distanceTo(from.x, from.y, goal.x, goal.y) === 1){
    method.splice(1,2);
  }

  for(var i=0; i<method.length; i++){
    var attempt = method[i];
    if(checkMove(from, attempt)) return attempt;
  }
  return 0;
}

var numWalls = function(x, y, indexes){
  var allCoords = [
    [x - 1, y - 1],
    [x,     y - 1],
    [x + 1, y - 1],
    [x - 1, y    ],
    [x + 1, y    ],
    [x - 1, y + 1],
    [x,     y + 1],
    [x + 1, y + 1],
  ];
  var allTypes = allCoords.map(function(e){
    return grid(e[0], e[1]); // air = 0, wall = 1
  });
  var justWalls = allTypes.filter(function(e){
    return e === 1;
  }).length;

  return indexes ? allTypes : justWalls;
}

var checkMove = function(coords, moveCode){
  var x = coords.x, y = coords.y,
      baseX = [0, -1, 0, 1, -1, 1, -1, 0, 1],
      baseY = [0, -1, -1, -1, 0, 0, 1, 1, 1],
      targetX = x + baseX[(moveCode - 1) % 8 + 1],
      targetY = y + baseY[(moveCode - 1) % 8 + 1];

  if((targetX > 127 || targetX < 0 || targetY > 63 || targetY < 0) || // Don't bother if it's out of bounds
     (coords.timesStill > 2 && x == coords.lastX && y == coords.lastY && (moveCode == coords.lastMove || moveCode == coords.last2Move)))
    // Or is doing the same moves and not moving
    return false;

  var targetGrid = grid(targetX, targetY), enemyNear = false, couldStrandEnemy = false,
      eWallDirMove, hasNeighbor = numWalls(targetX, targetY) > 0;

  ebots.forEach(function(ebot){
    // Don't place a wall where an enemy can take it
    if(distanceTo(targetX, targetY, ebot.x, ebot.y) === 1 && !ebot.hasWall && (y != ebot.y || x != ebot.x))
      enemyNear = true;

    // Don't move if you can strand an enemy
    var eWallDir = numWalls(ebot.x, ebot.y, true).indexOf(1) + 1,
        wallX = ebot.x + baseX[eWallDir], wallY = ebot.y + baseY[eWallDir];

    if(!coords.hasWall && numWalls(ebot.x, ebot.y) === 1 &&
       distanceTo(x, y, wallX, wallY) === 1){
      eWallDirMove = dirs[direction(y, wallY) + 1][direction(x, wallX) + 1] + 8;
      couldStrandEnemy = true;
    }
  })

  if(targetX == goal.x && targetY == goal.y && targetGrid === 0){
    targetGrid = 2 // Don't place a wall in the goal
  } else {
    ebots.concat(bots).forEach(function(bot){
      // Ensure target cell doesn't have a bot in it
      if(bot.x == targetX && bot.y == targetY) targetGrid = 2;
    });
  }

  return ((moveCode < 9 && targetGrid !== 1 && hasNeighbor && !couldStrandEnemy) || // Move
          (moveCode > 8 && moveCode < 17 && targetGrid === 1 && !coords.hasWall && (!couldStrandEnemy || (couldStrandEnemy && eWallDirMove == moveCode))) || // Grab
          (moveCode > 16 && targetGrid === 0 && coords.hasWall && !enemyNear)) // Place
}

var goalClosest = {dist: Infinity}, rescuers = {};
bots.forEach(function(bot, index){

  // Check if bot is stranded
  bot.stranded = false;
  if(numWalls(bot.x, bot.y) / 8 == bot.hasWall){
    bot.stranded = true;
    rescuers[index] = -1;
  }
});

bots.forEach(function(bot, index){
  if(!bot.stranded){
    // Find which bot is closest to the goal
    var goalDist = distanceTo(bot.x, bot.y, goal.x, goal.y);

    if(goalDist < goalClosest.dist){
      goalClosest.dist = goalDist;
      goalClosest.index = index;
    }
  }
});

bots.forEach(function(bot, index){
  var destination = {
    x: 14 + (index % 4) * 32 + 3 * (index > 3),
    y: index > 3 ? 55 : 27
  }
  if(index == goalClosest.index){
    destination = goal;
  }

  moves[index] = moveTo(bot, destination);

  if(moves[index] == bot.lastMove || moves[index] == bot.last2Move) bot.timesStill++;

  newMem += moves[index].toString(36) +
    bot.lastMove.toString(36) +
    String.fromCharCode(bot.timesStill + 48) +
    String.fromCharCode(bot.x + 187) +
    String.fromCharCode(bot.y + 187);
});

setMem(newMem);

return moves;

คำอธิบาย

การพิจารณาว่าการย้ายที่จะทำสำหรับแต่ละบอทสามารถแบ่งออกเป็นสองภารกิจหลักคือการหาว่าจะไปที่ไหนและไปที่นั่นได้อย่างไร

ว่าจะไปที่ไหน

ภารกิจพื้นฐานในการหาว่าจะไปไหนง่าย: ไปที่เป้าหมายหากคุณอยู่ใกล้ที่สุดหรือพยายามวางตำแหน่งตัวเองให้ห่างจากเพื่อนร่วมทีม ก่อนจะผ่านแต่ละบอทและกำหนดว่ามันติดอยู่หรือไม่ (เช่นไม่มีบล็อกล้อมรอบและไม่ถือกำแพงหรือถูกล้อมรอบด้วยกำแพงและถือกำแพง) จากนั้นจะวนซ้ำไปตามบอทอีกครั้งเพื่อหาบอทที่ไม่ติดอยู่ใกล้กับเป้าหมาย ทั้งหมดบอทอื่น ๆ ทำให้ทางของพวกเขาที่มีต่อการถูกเว้นระยะห่างออกไปด้วยแถวด้านล่างบนพื้นผิวของบล็อก (คนy=55) y=27และแถวบนสุดที่ เมื่อรู้ว่าจะไปที่ไหนมันจะส่งไปยังmoveToฟังก์ชั่น

วิธีเดินทาง

การตัดสินใจว่าจะไปถึงจุดหมายนั้นยากกว่านี้มากเพราะบอตจะต้องอยู่ติดกับกำแพงเพื่อเคลื่อนที่ ก่อนอื่นมันจะคำนวณรหัสทิศทาง (1-8) ของปลายทางที่สัมพันธ์กับตำแหน่งปัจจุบัน ตัวอย่างเช่นถ้าบอทอยู่ที่มุมล่างซ้ายและต้องการไปที่ด้านบนขวามันจะใช้รหัสทิศทาง 3 สำหรับแต่ละทิศทางฉันฮาร์ดโค้ดรายการของการเคลื่อนไหวโดยแรกจะเป็นอุดมคติอุดมคติ - เลื่อนลำดับความสำคัญและสุดท้ายเป็นทางเลือกสุดท้าย สิ่งนี้ถูกคั่นด้วยบ็อตที่มีกำแพงหรือไม่เพราะคุณไม่สามารถใช้การย้ายสถานที่โดยไม่มีกำแพงหรือใช้การย้ายแบบคว้าในขณะที่มีกำแพงอยู่แล้ว

แน่นอนว่าการใช้การเคลื่อนไหวในอุดมคตินั้นไม่ได้ผลเสมอไปและจะส่งผลให้เกิดการกระทำที่ล้มเหลวมากมาย นี่คือที่checkMoveมาใน. ฟังก์ชั่นนี้การตรวจสอบการย้ายที่อาจเกิดขึ้นกับทุกความต้องการเพื่อป้องกันไม่ให้บอทจากการย้ายออกจากขอบเขตหรือเข้าไปในผนังเช่น หากบอทศัตรูที่อยู่ใกล้เคียงสามารถถูกควั่นได้ (มันมีเพียงกำแพงที่อยู่ติดกันเพียงบอทเดียวที่สามารถยึดบอทได้) นั่นจะกลายเป็นสิ่งที่มีความสำคัญดังนั้นฟังก์ชั่นนี้จะกลับมาfalseเพื่อการเคลื่อนไหวที่ถูกต้องตามกฎหมาย ศัตรู. ฟังก์ชั่นนี้ช่วยป้องกันการเคลื่อนที่โง่ ๆ เช่นวางกำแพงในประตูหรือบอตอื่น

สตริงหน่วยความจำ

บางครั้งบอทจะไม่ติดค้างจริง ๆ แต่จะพยายามต่อเนื่องกันและไม่จบลงด้วยการเคลื่อนไหว (โดยปกติแล้วจะยกกำแพงขึ้นมาวางลงยกขึ้นและวางลง ฯลฯ ) เพื่อป้องกันสิ่งนี้มันใช้สตริงหน่วยความจำเพื่อจดจำการเคลื่อนไหวสองครั้งสุดท้ายของตำแหน่ง x และ y ล่าสุดและจำนวนครั้งที่ยังคงอยู่ แต่ละฐานข้อมูลจะถูกเข้ารหัสเป็นอักขระเดียวเพื่อให้แยกง่าย (สตริงต้องมีอักขระ 256 ตัวไม่ใช่ไบต์ดังนั้นการใช้อักขระหลายไบต์ Unicode จึงไม่ใช่ปัญหาเนื่องจากเป็นความท้าทายทั่วไปเกี่ยวกับการเล่นกอล์ฟ)

ตัวอย่างเช่นสมมติว่าบอคว้าผนังด้านซ้ายของมัน (รหัส12) เทิร์นนี้แทนที่มันไปทางซ้ายของมัน (รหัส20) ในทางกลับกันก่อนหน้านี้และได้รับการที่พิกัด ( 107, 3) สำหรับที่ผ่านมา16ผลัดกัน สตริงหน่วยความจำสำหรับอินสแตนซ์นี้จะถูกเข้ารหัสดังนี้:

  • ck: สองรหัสการกระทำล่าสุดถูกแปลงเป็น base36 เพื่อให้ตัวเลขสองหลักเป็นตัวอักษรเดียว
  • @: จำนวนครั้งที่ยังคงแสดงเป็นอักขระ ASCII ที่มีรหัส +48 เพื่อข้ามอักขระที่ไม่สามารถพิมพ์ได้ดังนั้นเก้าครั้งแรกยังคงแสดงจำนวนจริง ( String.fromCharCode(0 + 48)0)
  • Ħ¾: พิกัด x และ y ยังแสดงเป็นตัวละครที่มีค่านั้นในเวลานี้ชดเชยด้วยค่าค่อนข้างโดยพลการของ 187 เพื่อหลีกเลี่ยงอักขระที่มีปัญหา

สตริงหน่วยความจำทั่วไปในระหว่างเกมอาจ53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬòมีกลุ่มของอักขระห้าตัวสำหรับแต่ละแปดบอท


1
หากผู้เล่นอยู่ในเกมกับตัวเองผู้เล่น 1 หรือผู้เล่น 2 คนใดจะชนะโดยไม่มีอคติชัดเจน คำตอบนี้เป็นข้อยกเว้นเนื่องจากผู้เล่น 1 ชนะเกือบมากเสมอ ฉันไม่เห็นเหตุผลว่าทำไม - มันน่าสนใจ
trichoplax

@trichoplax ฉันคิดว่ามันเป็นเพราะ P1 เคลื่อนที่ก่อน P2 ดังนั้น P1 จะควั่น P2 ก่อนที่มันจะทำเช่นเดียวกัน
NinjaBearMonkey

4
ความท้าทายสิ้นสุดลงแล้วและผลลัพธ์ก็อยู่ในนั้น ! หลังจาก 30 รอบอัศวินดำ 204 ชนะและรายการที่ดีที่สุดต่อไปหา , มีเพียง 147 ขอแสดงความยินดีNinjaBearMonkey ! คุณความท้าทายที่เฉพาะเจาะจงอยู่ในทาง
งานอดิเรกของ Calvin

นินจาอย่างแน่นอน! ขอบคุณ
edc65

12

จักรภพ

8 บอทแต่ละอันมีขนาด 32 คูณ 32 ตารางและวิ่งไปที่กึ่งกลางของมัน (ฉันชดเชยจุดศูนย์กลางเล็กน้อยไม่เช่นนั้นพวกเขาจะจับคู่และเดินทางในแนวตั้งด้วยกำแพงบล็อกหนึ่งอันระหว่างพวกเขาดังนั้นหนึ่งในนั้นจึงติดอยู่)

บอทแต่ละตัวจะอยู่ในใจกลางของจัตุรัสเว้นแต่เป้าหมายจะอยู่ภายใน 32 เซลล์ของศูนย์นั้น ๆ ซึ่งในกรณีนี้มันจะวิ่งไปที่เป้าหมายแล้วกลับไปที่จุดศูนย์กลาง

วิธีนี้ยังคงใช้วิธีการพื้นฐานในการเข้าถึงเป้าหมาย (เป้าหมายหรือศูนย์กลาง) ดังนั้นจึงไม่ย้ายในแนวทแยงมุม เป็นเพียงจุดเริ่มต้น ...

var encodeAction = function(type, dx, dy) {
    var d
    if (dx === -1 && dy === -1) d = 1
    else if (dx === 0 && dy === -1) d = 2
    else if (dx === 1 && dy === -1) d = 3
    else if (dx === -1 && dy === 0) d = 4
    else if (dx === 1 && dy === 0) d = 5
    else if (dx === -1 && dy === 1) d = 6
    else if (dx === 0 && dy === 1) d = 7
    else if (dx === 1 && dy === 1) d = 8
    else return 0
    return 8 * type + d
}

var getNeighborCell = function(x, y, wallState) {
    if (x > 0 && y > 0 && grid(x - 1, y - 1) === wallState) return { x: x - 1, y: y - 1 }
    if (y > 0 && grid(x, y - 1) === wallState) return { x: x, y: y - 1 }
    if (x < 127 && y > 0 && grid(x + 1, y - 1) === wallState) return { x: x + 1, y: y - 1 }
    if (x > 0 && grid(x - 1, y) === wallState) return { x: x - 1, y: y }
    if (x < 127 && grid(x + 1, y) === wallState) return { x: x + 1, y: y }
    if (x > 0 && y < 63 && grid(x - 1, y + 1) === wallState) return { x: x - 1, y: y + 1 }
    if (y < 63 && grid(x, y + 1) === wallState) return { x: x, y: y + 1 }
    if (x < 127 && y < 63 && grid(x + 1, y + 1) === wallState) return { x: x + 1, y: y + 1 }
    return null
}

var moveBot = function(n) {
    var assignedX = (n % 4) * 32 + 14 + Math.floor(n/4) * 4
    var assignedY = (Math.floor(n / 4)) * 32 + 16
    if (Math.abs(goal.x - assignedX) < 33 && Math.abs(goal.y - assignedY) < 33) {
        assignedX = goal.x
        assignedY = goal.y
    }
    var b = bots[n], moveX = b.x !== assignedX, x = b.x, y = b.y, type
    if (moveX) {
        x += b.x < assignedX ? 1 : -1
    } else {
        y += b.y < assignedY ? 1 : -1
    }
    if (grid(x, y) === 1) {
        if (b.hasWall) {
            type = 2 //place
            var c = getNeighborCell(b.x, b.y, 0)
            if (!c) { //stuck holding wall with walls all around
                return 0
            }
            x = c.x
            y = c.y
        } else {
            type = 1 //grab
        }
    } else if (grid(x, y) === 0) {
        if (getNeighborCell(x, y, 1)) {
            type = 0 //move
        } else {
            if (b.hasWall) {
                type = 2 //place
                if (moveX) {
                    y += y > 0 ? -1 : 1
                } else {
                    x += x > 0 ? -1 : 1
                }
            } else {
                type = 1 //grab
                var c = getNeighborCell(b.x, b.y, 1)
                if (!c) { //stuck without wall in midair
                    return 0
                }
                x = c.x
                y = c.y
            }
        }
    } else {
        return 0 //should never get here
    }
    return encodeAction(type, x - b.x, y - b.y)
}

var actions = []
for (var i = 0; i < 8; i++) {
    actions[i] = moveBot(i)
}

return actions

1
ฉันรู้ว่านี่เป็นเพียงจุดเริ่มต้น แต่สิ่งหนึ่งที่ฉันสังเกตเห็นในขณะที่ดูการต่อสู้ (ไม่สม่ำเสมอ) กับพื้นฐาน: หากเป้าหมายอยู่ไม่ไกลจากศูนย์หลายแห่งพวกเขาทั้งหมดวิ่งไปสู่เป้าหมาย อาจจะดีกว่าถ้าเลือกอันที่ใกล้เคียงที่สุดและให้คนอื่นอยู่ในตำแหน่ง "อุดมคติ" สนุกกับการดู btw
Reto Koradi

ขอบคุณ! ใช่ว่าจะเป็นการปรับปรุงอย่างแน่นอน มีรายการอื่น ๆ อีกมากมายที่ให้คิดเช่นกันและกลยุทธ์ที่แตกต่างหลากหลายที่จะใช้ ฉันกำลังมองไปข้างหน้าเห็นว่ากลยุทธ์ที่คนอื่นเลือกและวิธีการที่พวกเขามีปฏิสัมพันธ์ ...
Trichoplax

9

พื้นฐาน

นี่คือคอนโทรลเลอร์ฝูง bot ที่ทำงานได้อย่างต่อเนื่องที่ง่ายที่สุดที่ฉันคิดได้ มันจะเป็นคำตอบเดียวที่ไม่มีคุณสมบัติของฉันและจะทำหน้าที่เป็นพื้นฐานในการตัดสินคำตอบอื่น ๆ โดย มันเป็นเทคนิคในการวิ่งเพื่อชนะการแข่งขัน แต่การตีนั้นไม่ควรยาก

รหัสใด ๆ ที่นี่อาจถูกคัดลอกและใช้ในคำตอบอื่นโดยไม่ต้องระบุแหล่งที่มา

var encodeAction = function(type, dx, dy) {
    var d
    if (dx === -1 && dy === -1) d = 1
    else if (dx === 0 && dy === -1) d = 2
    else if (dx === 1 && dy === -1) d = 3
    else if (dx === -1 && dy === 0) d = 4
    else if (dx === 1 && dy === 0) d = 5
    else if (dx === -1 && dy === 1) d = 6
    else if (dx === 0 && dy === 1) d = 7
    else if (dx === 1 && dy === 1) d = 8
    else return 0
    return 8 * type + d
}

var getNeighborCell = function(x, y, wallState) {
    if (x > 0 && y > 0 && grid(x - 1, y - 1) === wallState) return { x: x - 1, y: y - 1 }
    if (y > 0 && grid(x, y - 1) === wallState) return { x: x, y: y - 1 }
    if (x < 127 && y > 0 && grid(x + 1, y - 1) === wallState) return { x: x + 1, y: y - 1 }
    if (x > 0 && grid(x - 1, y) === wallState) return { x: x - 1, y: y }
    if (x < 127 && grid(x + 1, y) === wallState) return { x: x + 1, y: y }
    if (x > 0 && y < 63 && grid(x - 1, y + 1) === wallState) return { x: x - 1, y: y + 1 }
    if (y < 63 && grid(x, y + 1) === wallState) return { x: x, y: y + 1 }
    if (x < 127 && y < 63 && grid(x + 1, y + 1) === wallState) return { x: x + 1, y: y + 1 }
    return null
}

var moveBot = function(n) {
    var b = bots[n], moveX = b.x !== goal.x, x = b.x, y = b.y, type
    if (moveX) {
        x += b.x < goal.x ? 1 : -1
    } else {
        y += b.y < goal.y ? 1 : -1
    }
    if (grid(x, y) === 1) {
        if (b.hasWall) {
            type = 2 //place
            var c = getNeighborCell(b.x, b.y, 0)
            if (!c) { //stuck holding wall with walls all around
                return 0
            }
            x = c.x
            y = c.y
        } else {
            type = 1 //grab
        }
    } else if (grid(x, y) === 0) {
        if (getNeighborCell(x, y, 1)) {
            type = 0 //move
        } else {
            if (b.hasWall) {
                type = 2 //place
                if (moveX) {
                    y += y > 0 ? -1 : 1
                } else {
                    x += x > 0 ? -1 : 1
                }
            } else {
                type = 1 //grab
                var c = getNeighborCell(b.x, b.y, 1)
                if (!c) { //stuck without wall in midair
                    return 0
                }
                x = c.x
                y = c.y
            }
        }
    } else {
        return 0 //should never get here
    }
    return encodeAction(type, x - b.x, y - b.y)
}

var actions = []
for (var i = 0; i < 8; i++) {
    actions[i] = moveBot(i)
}

return actions

8 บอตแต่ละตัวเป็นอิสระตามวิธีการพื้นฐานเดียวกัน พวกเขามักจะรวมตัวกันด้วยเหตุนี้เว้นแต่พวกเขาจะถูกแยกจากกันโดยสิ่งภายนอก บอทไม่เคยสนใจว่าเพื่อนร่วมทีมหรือศัตรูอยู่ที่ไหนพวกเขาเพียง แต่พยายามเคลื่อนไปสู่เป้าหมายเท่านั้น พวกเขาเคลื่อนที่แบบ orthogonally โดยจับคู่ x เข้ากับเป้าหมาย x จากนั้นก็เป็น y ไม่เคยเคลื่อนที่ในแนวทแยงมุมหมายความว่าพวกเขาเสียเวลามากในการเดินทาง

อัลกอริทึมการเคลื่อนไหวของแต่ละบอทมีดังต่อไปนี้:

If my X is not equal to the goal's X
    P = position to my left or right that is closer to the goal  
    Make a note that I'm trying to move horizontal  
Else  
    P = position above or below me that is closer to the goal  
    Make a note that I'm trying to move vertical  

If P is a wall  
    If I'm holding a wall  
        Place my wall in any neighboring air cell  
    Else  
        Grab the wall at P  
Else if P is air  
    If P has a wall neighboring it (i.e. if I can move to P)  
        Move to P  
    Else  
        If I'm holding a wall  
            If I'm trying to move horizontal  
                Place my wall above or below P  
            Else if I'm trying to move vertical  
                Place my wall to the left or right of P  
        Else  
            Grab wall from any neighboring wall cell   

6

ทีมผู้เล่น

ในขณะนี้การส่งนี้ยังห่างไกลจากความสมบูรณ์แบบ มันมีกลยุทธ์ที่คล้ายกันเช่นด่านหน้า แต่มีเพียง 6 บอทเท่านั้นที่ "อยู่ในอากาศ" บอทอีก 2 อันจัดหากำแพงให้ถ้าพวกมันถูกขโมย แก้ไข:บอตผู้สนับสนุนทำงานได้ดีขึ้นมากในขณะนี้

var outside = function(x,y) {
    return x < 0 || x > 127 || y < 0 || y > 127
}

var distance = function(x1, y1, x2, y2){
  return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}

var isStuck = function(bot) {
    if (bot.hasWall) {
        for (var i=-1; i<=1; i++) {
            for (var j=-1; j<=1; j++) {
                if ((i != 0 || j != 0) && grid(bot.x+i,bot.y+j) == 0 && !outside(bot.x+i,bot.y+j))
                    return false
            }
        }
        return true
    }
    for (var i=-1; i<=1; i++) {
        for (var j=-1; j<=1; j++) {
            if (grid(bot.x+i, bot.y+j) == 1)
                return false
        }
    }
    return true
}

var isPlayer = function(x,y) {
    for (var i = 0; i < bots.length; i++) {
        if (bots[i].x == x && bots[i].y == y)
            return true
    }
    for (var i = 0; i < ebots.length; i++) {
        if (ebots[i].x == x && ebots[i].y == y)
            return true
    }
    return false
}

var encodeAction = function(type, dx, dy) {
    var d
    if (dx === -1 && dy === -1) d = 1
    else if (dx === 0 && dy === -1) d = 2
    else if (dx === 1 && dy === -1) d = 3
    else if (dx === -1 && dy === 0) d = 4
    else if (dx === 1 && dy === 0) d = 5
    else if (dx === -1 && dy === 1) d = 6
    else if (dx === 0 && dy === 1) d = 7
    else if (dx === 1 && dy === 1) d = 8
    else return 0
    return 8 * type + d
}

var surrounding = function(x,y) {
    var cell = {hasStone:false, cells: []}
    for (var i=-1; i<=1; i++) {
        for(var j=-1; j<=1; j++) {
            if ((i != 0 || j != 0) && !outside(x+i,y+j)) {
                cell.cells.push({x:x+i, y:y+j})
                if (grid(x+i,y+j) == 1) {
                    cell.hasStone = true
                }
            }
        }
    }
    return cell
}


var hunt = function(i, destination) {
    destination = destination || {x: 31+((i-2)%3)*32, y: 20+((i-2)%2)*21}, bot = bots[i]
    if (i < 5 && i > 1) {
        destination.x -= 2
    }
    if (bot.isStuck) {
        return 0
    }
    if ((p1 && destination.x >= move + i) || (!p1 && 127 - destination.x > move - i)) {
        destination.y = bot.y
    }
    if (i == bestBotId && move > 50) {
        destination.x = goal.x
        destination.y = goal.y
    }
    var dx = destination.x > bot.x ? 1 : destination.x == bot.x ? 0 : -1, newX = bot.x + dx
    var dy = destination.y > bot.y ? 1 : destination.y == bot.y ? 0 : -1, newY = bot.y + dy
    var surr = surrounding(newX, newY), botSurr = surrounding(bot.x, bot.y)
    if (grid(newX, newY) == 0) {
        if (surr.hasStone) {
            return encodeAction(0, dx, dy)
        } else {
            if (bot.hasWall) {
                for (var i=0; i<surr.cells.length; i++) {
                    var cell = surr.cells[i];
                    if (Math.abs(cell.x - bot.x) <= 1 && Math.abs(cell.y - bot.y) <= 1 && grid(cell.x, cell.y) == 0 && !isPlayer(cell.x, cell.y)) {
                        return encodeAction(2, cell.x - bot.x, cell.y - bot.y)
                    }
                }
            } else {
                if (bot.walls.length == 1) {
                    return encodeAction(1, bot.walls[0].x - bot.x, bot.walls[0].y - bot.y)
                } else {
                    for (var i=0; i<bot.walls.length; i++) {
                        var wall = bot.walls[i], canUseWall = true
                        for (var j=0; j<bots.length; j++) {
                            if (bots[j].walls.length == 1 && bots[j].walls[0].x == wall.x && bots[j].walls[0].y == wall.y) {
                                canUseWall = false
                            }
                        }
                        if (canUseWall) {
                            return encodeAction(1, wall.x - bot.x, wall.y - bot.y)
                        }
                    }
                }
            }
        }
    } else {
        if (bot.hasWall) {
            for (var i=0; i<botSurr.cells.length; i++) {
                var cell = botSurr.cells[i];
                if (grid(cell.x, cell.y) == 0 && !isPlayer(cell.x, cell.y) && !outside(cell.x, cell.y)) {
                    return encodeAction(2, cell.x - bot.x, cell.y - bot.y)
                }
            }
        } else {
            return encodeAction(1, dx, dy)
        }
    }
    return 0 //hopefully never happens
}

var help = function(i) {
    if (bots[i].isStuck) {
        return 0
    }
    var bot = bots[i], destination = helpDestinations[i]
    if (destination.stuckBot == -1) {
        if (bot.walls.length >= 2 || (bot.hasWall && bot.walls.length == 1)) {
            var stuckId = -1
            for (var j = 0; j < bots.length; j++) {
                if (j != helpDestinations[(i+1)%2].stuckBot && bots[j].isStuck)
                    stuckId = j
            }
            if (stuckId != -1) {
                destination.stuckBot = stuckId
                destination.x = bots[stuckId].x
                destination.y = bots[stuckId].y
                return 0
            } else {
                return hunt(i, destination)
            }
        } else if (bot.x == destination.x && bot.y == destination.y) {
            if (move % 2 == 0)
                destination.y += 1
            else
                destination.x -= 1
            return hunt(i, destination)
        } else {
            return hunt(i, destination)
        }
    } else if (bots[destination.stuckBot].isStuck) {
        if (bot.walls.length < 2 && !(bot.hasWall && bot.walls.length == 1)) {
            destination.stuckBot = -1
            destination.x = i == 0 ? 42 : 85
            destination.y = 55
            return hunt(i, destination)
        }
        var dx = destination.x > bot.x ? 1 : destination.x == bot.x ? 0 : -1, newX = bot.x + dx
        var dy = destination.y > bot.y ? 1 : destination.y == bot.y ? 0 : -1, newY = bot.y + dy
        var surr = surrounding(newX, newY), botSurr = surrounding(bot.x, bot.y), surrWalls = 0
        for (var i = 0; i < surr.cells.length; i++) {
            var cell = surr.cells[i]
            if (grid(cell.x,cell.y) == 1)
                surrWalls++
        }
        if (grid(newX, newY) == 0) {
            if (surrWalls >= 2 || (surr.hasWall && bot.hasWall)) {
                return encodeAction(0, dx, dy)
            } else {
                if (bot.hasWall) {
                    for (var i=0; i<surr.cells.length; i++) {
                        var cell = surr.cells[i];
                        if (Math.abs(cell.x - bot.x) <= 1 && Math.abs(cell.y - bot.y) <= 1 && grid(cell.x, cell.y) == 0 && !isPlayer(cell.x, cell.y)) {
                            return encodeAction(2, cell.x - bot.x, cell.y - bot.y)
                        }
                    }
                } else {
                    if (bot.walls.length == 1) {
                        return encodeAction(1, bot.walls[0].x - bot.x, bot.walls[0].y - bot.y)
                    } else {
                        for (var i=0; i<bot.walls.length; i++) {
                            var wall = bot.walls[i], canUseWall = true
                            for (var j=0; j<bots.length; j++) {
                                if (bots[j].walls.length == 1 && bots[j].walls[0].x == wall.x && bots[j].walls[0].y == wall.y) {
                                    canUseWall = false
                                }
                            }
                            for (var j=0; j<surr.cells.length; j++) {
                                if (surr.cells[j].x == wall.x && surr.cells[j].y == wall.y)
                                    canUseWall = false
                            }
                            if (canUseWall) {
                                return encodeAction(1, wall.x - bot.x, wall.y - bot.y)
                            }
                        }
                    }
                }
            }
        } else {
            if (bot.hasWall) {
                for (var i=0; i<botSurr.cells.length; i++) {
                    var cell = botSurr.cells[i];
                    if (grid(cell.x, cell.y) == 0 && !isPlayer(cell.x, cell.y)) {
                        return encodeAction(2, cell.x - bot.x, cell.y - bot.y)
                    }
                }
            } else {
                return encodeAction(1, dx, dy)
            }
        }
    } else {
        destination.stuckBot = -1
        destination.x = i == 0 ? 42 : 85
        destination.y = 55
        return hunt(i, destination)
    }
    return 0 //hopefully never happens
}

var moves = new Array(8)    
var mem = getMem(), helpDestinations = []
if (mem.length == 0) {
    mem = "42,55,-1 85,55,-1"
}
mem = mem.split(" ")
for (var i = 0; i < mem.length; i++) {
    var cell = mem[i].split(",")
    helpDestinations.push({x: parseInt(cell[0]), y: parseInt(cell[1]), stuckBot: parseInt(cell[2])})
}

for (var i = 0; i < 8; i++) {
    var bot = bots[i]
    var surr = surrounding(bot.x, bot.y)
    bot.walls = []
    for (var j = 0; j < surr.cells.length; j++) {
        if (grid(surr.cells[j].x, surr.cells[j].y) == 1) {
            bot.walls.push(surr.cells[j])
        }
    }
}

bots.forEach(function(bot, index) {
    if(isStuck(bot)) {
        bot.isStuck = true
    }
})

var bestDistance = 1000
var bestBotId = -1
for (var i=2; i<8; i++) {
    var dist = distance(bots[i].x, bots[i].y, goal.x, goal.y)
    if (dist < bestDistance && !bots[i].isStuck) {
        bestDistance = dist
        bestBotId = i
    }
}

for (var i=0; i<8; i++) {
    if (i < 2) {
        moves[i] = help(i)
    } else {
        moves[i] = hunt(i)  
    }
}

setMem(helpDestinations[0].x + "," + helpDestinations[0].y + "," + helpDestinations[0].stuckBot + " " + helpDestinations[1].x + "," + helpDestinations[1].y + "," + helpDestinations[1].stuckBot)

return moves

แนวคิดของผู้สนับสนุน (ดี) ดูเหมือนจะนำไปปฏิบัติได้ยาก
edc65

@ edc65 โดยพื้นฐานแล้วมันแค่ขยับด้วยกำแพง 2 ด้านแทนที่จะเป็นหนึ่งกำแพง แต่การสื่อสารระหว่างสองบอทสนับสนุนและหาผนังใหม่ค่อนข้างยากที่จะดำเนิน :)
CommonGuy

6

หา

กำลังทำงานอยู่ ฉันมีความคิดมากมาย แต่แทบจะไม่มีเลย

เหนือสิ่งอื่นใดปัญหาใหญ่กับการกระทำที่ล้มเหลว แก้ไข!

var action=[], myGrid=[], goalSort=[], i, j, curBot, curAction, goalSeek;

var check = function(x,y) {
  return (myGrid[[x,y]] || (myGrid[[x,y]] = grid(x,y)))|0;
};

var setGrid = function(x,y,v) {
  myGrid[[x,y]] = v + '';
};

var orGrid = function(x,y,v) {
  myGrid[[x,y]] |= v;
};

var encodeDir = function(dx, dy) {
    return dx < 0 && dy < 0 ? 1
    : dx === 0 && dy < 0 ? 2
    : dx > 0 && dy < 0 ? 3
    : dx < 0 && dy === 0 ? 4
    : dx > 0 && dy === 0 ? 5
    : dx < 0 && dy > 0 ? 6
    : dx === 0 && dy > 0 ? 7
    : dx > 0 && dy > 0 ? 8
    : 0;
};

var distance = function(p1, p2) {
  return Math.max(Math.abs(p1.x-p2.x),Math.abs(p1.y-p2.y));
};

var cellNearWall = function(x,y)
{
  var r = check(x,y) == 1 ? 0
  : check(x-1,y-1) == 1 ? 1
  : check(x,y-1) == 1 ? 2
  : check(x+1,y-1) == 1 ? 3
  : check(x-1,y) == 1 ? 4
  : check(x+1,y) == 1 ? 5
  : check(x-1,y+1) == 1 ? 6
  : check(x,y+1) == 1 ? 7
  : check(x+1,y+1) == 1 ? 8
  : 0;
  return r;
};

var cellNearBot = function(x,y,m)
{
  return check(x-1,y-1) & m ? 1
  : check(x,y-1) & m ? 2
  : check(x+1,y-1) & m ? 3
  : check(x-1,y) & m ? 4
  : check(x+1,y) & m ? 5
  : check(x-1,y+1) & m ? 6
  : check(x,y+1) & m ? 7
  : check(x+1,y+1) & m ? 8
  : 0;
};


var tryGrabWall = function(x, y)
{
  var dx, dy, r = 8;
  for(dy = -1; dy < 2; ++dy)
  {
    for(dx = -1; dx < 2; ++dx)
    {
      if (dx|dy)
      {
        ++r;
        if (check(x+dx, y+dy) == 1)
        {
          setGrid(x+dx, y+dy, 0); // remember that the wall is not there anymore
          return r;
        }
      }
    }
  }
  return 0;
};

var tryDropWall= function(x, y)
{
  var dx, dy, r = 16;
  for(dy = -1; dy < 2; ++dy)
  {
    for(dx = -1; dx < 2; ++dx)
    {
      if (dx|dy)
      {
        ++r;
        if (x+dx>=0 & x+dx < 128 & y+dy >= 0 & y+dy < 64 && check(x+dx, y+dy) == 0)
        {
          setGrid(x+dx, y+dy, 1); // remember that the wall is there 
          return r;
        }
      }
    }
  }
  return 0;
};


var approach = function(bot, target)
{
  var dx, dy, tx, ty, r = 0, wallPos;

  var checkDrop = function(dx,dy)
  {
    var x = bot.x+dx, y = bot.y+dy;
    if (check(x,y) == 0 && cellNearBot(x,y,8) == 0)
    {
      setGrid(x, y, 1);
      return 16 + encodeDir(dx, dy);
    }
  };

  dy = target.y - bot.y;
  dy = dy < 0 ? -1 : dy > 0 ? 1 : 0;
  dx = target.x - bot.x;
  dx = dx < 0 ? -1 : dx > 0 ? 1 : 0;
  tx = bot.x+dx;
  ty = bot.y+dy;

  if ((dx|dy) === 0)
  {
    if (!bot.hasWall) {
      return tryGrabWall(bot.x, bot.y);
    }
    return 0;
  }


  if (cellNearWall(tx,ty))
  {
    setGrid(tx, ty, 2);
    return encodeDir(dx, dy);
  }

  if (dx === 0)
  {
    if (cellNearWall(bot.x-1,ty))
    {
      setGrid(bot.x-1, ty, 2);
      return encodeDir(-1, dy);
    }
    if (cellNearWall(bot.x+1,ty))
    {
      setGrid(bot.x+1, ty, 2);
      return encodeDir(1, dy);
    }
    if (bot.hasWall) 
    {
      if (wallPos = checkDrop(1,dy)) { return wallPos; }
      if (wallPos = checkDrop(-1,dy)) { return wallPos; }
      if (wallPos = checkDrop(1,0)) { return wallPos; }
      if (wallPos = checkDrop(-1,0)) { return wallPos; }
    }
  }
  else if (dy === 0) 
  {
    if (cellNearWall(tx,bot.y-1))
    {
      setGrid(tx, bot.y-1, 2);
      return encodeDir(dx, -1);
    }
    if (cellNearWall(tx,bot.y+1))
    {
      setGrid(tx, bot.y+1, 2);
      return encodeDir(dx, 1);
    }
    if (bot.hasWall) 
    {
      if (wallPos = checkDrop(dx,1)) { return wallPos; }
      if (wallPos = checkDrop(dx,-1)) { return wallPos; }
      if (wallPos = checkDrop(0,1)) { return wallPos; }
      if (wallPos = checkDrop(0,-1)) { return wallPos; }
    }
  }
  else
  {
    if (cellNearWall(tx,bot.y))
    {
      setGrid(tx, bot.y, 2);
      return encodeDir(dx, 0);
    }
    if (cellNearWall(bot.x,ty))
    {
      setGrid(bot.x, ty, 2);
      return encodeDir(0,dy);
    }
    if (bot.hasWall) {
      if (wallPos = checkDrop(dx,0)) { return wallPos; }
      if (wallPos = checkDrop(0,dy)) { return wallPos; }
      if (wallPos = checkDrop(dx,dy)) { return wallPos; }
    }
  }

  if (!bot.hasWall)
  {
  if (check(tx, ty) == 1)
  {
      setGrid(tx, ty, 0); // remember that the wall is not there anymore
      return 8 + encodeDir(dx, dy);
    };
    return tryGrabWall(bot.x, bot.y);
  }
  else
  {
    return tryDropWall(bot.x, bot.y);
  }
};

for (i=0; curBot=ebots[i]; i++)
{
  setGrid(curBot.x, curBot.y, curBot.hasWall ? 4 : 8);
}

var goalDistance=[]

for (i=0; curBot=bots[i]; i++)
{
  orGrid(curBot.x, curBot.y, 2);
  goalDistance[i] = distance(curBot, goal);
}
var sorted = goalDistance.slice().sort(function(a,b){return a-b})
var ranks = goalDistance.slice().map(function(v){ return sorted.indexOf(v)});

var tt = p1 
? [ { x:32, y:20 },{ x:32, y:55 },{ x:64, y:20 },{ x:64, y:55 },
   { x:96, y:20 },{ x:96, y:55 },{ x:16, y:30 },{ x:112, y:30 }]
: [ { x:96, y:20 },{ x:96, y:55 },{ x:64, y:20 },{ x:64, y:55 },
   { x:32, y:20 },{ x:32, y:55 },{ x:112, y:30 },{ x:16, y:30 }]

var goalSeek = 3;

for (i=0; curBot=bots[i]; i++)
{
  if (ranks[i] < goalSeek)
  {
    curAction = approach(curBot, goal);
    if (curAction == 0) goalSeek += 1;
  }
  else
    curAction = approach(curBot, tt[i]);

  action[i] = curAction;
}

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