เพื่อ Vectory! - Vector Racing Grand Prix


39

ผู้ใช้ CarpetPython โพสต์สิ่งใหม่เกี่ยวกับปัญหานี้ซึ่งให้ความสำคัญกับโซลูชั่นฮิวริสติกมากขึ้นเนื่องจากมีพื้นที่การค้นหาเพิ่มขึ้น โดยส่วนตัวฉันคิดว่าความท้าทายนั้นดีกว่าของฉันมากดังนั้นลองพิจารณาดูสิ!

Vector racing เป็นเกมเสพติดที่สามารถเล่นได้ด้วยปากกาและแผ่นกระดาษสี่เหลี่ยมจตุรัส คุณวาดสนามแข่งตามอำเภอใจลงบนกระดาษกำหนดจุดเริ่มต้นและจุดสิ้นสุดจากนั้นให้คุณควบคุมรถขนาดจุดของคุณในลักษณะเลี้ยว ไปให้ถึงจุดสิ้นสุดให้เร็วที่สุดเท่าที่จะทำได้ แต่ระวังอย่าให้ถูกกำแพง!

ติดตาม

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

ขับรถ

(0, 0)รถของคุณเริ่มต้นที่ได้รับการประสานงานและด้วยความเร็วเวกเตอร์ ในแต่ละเทิร์นคุณสามารถปรับแต่ละส่วนของความเร็ว±1ได้ จากนั้นเวกเตอร์ความเร็วที่ได้จะถูกเพิ่มไปยังตำแหน่งรถของคุณ

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

                                    ป้อนคำอธิบายรูปภาพที่นี่

หากคุณที่ดินในผนังที่คุณสูญเสียทันที

งานของคุณ

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

อินพุต

เมื่อโปรแกรมของคุณถูกเรียกใช้อ่านจาก stdin :

target
n m
[ASCII representation of an n x m racetrack]
time

targetคือจำนวนรอบการหมุนสูงสุดที่คุณอาจใช้ในการติดตามและtimeเป็นงบประมาณเวลาทั้งหมดของคุณสำหรับการติดตามเป็นวินาที (ไม่จำเป็นต้องเป็นจำนวนเต็ม) ดูด้านล่างสำหรับรายละเอียดเกี่ยวกับเวลา

สำหรับแทร็กที่คั่นด้วย newline จะใช้อักขระต่อไปนี้:

  • # - ผนัง
  • S- เริ่มต้น
  • *- เป้าหมาย
  • . - เซลล์ติดตามอื่น ๆ ทั้งหมด (เช่นถนน)

เซลล์ทั้งหมดที่อยู่นอกn x mตารางที่ระบุไว้จะเป็นผนัง

จุดกำเนิดพิกัดอยู่ที่มุมซ้ายบน

นี่คือตัวอย่างง่ายๆ:

8
4.0
9 6
###...***
###...***
###...***
......###
S.....###
......###

โดยใช้การจัดทำดัชนี 0-based (0,4)เริ่มต้นของการประสานงานจะเป็น

หลังจากย้ายทุกครั้งคุณจะได้รับการป้อนข้อมูลเพิ่มเติม:

x y
u v
time

ที่ไหนx, y, u, vมีทั้งหมดจำนวนเต็ม 0-based (x,y)คือตำแหน่งปัจจุบันของคุณและ(u,v)เป็นความเร็วปัจจุบันของคุณ โปรดทราบว่าx+uและ / หรือy+vอาจอยู่นอกขอบเขต

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

เมื่อเกมจบลง (เนื่องจากคุณลงจอดในกำแพง, ย้ายออกนอกสนาม, targetเทิร์นเกิน, หมดเวลาหรือไปถึงเป้าหมาย) คุณจะได้รับบรรทัดว่างเปล่า

เอาท์พุต

สำหรับการเลี้ยวแต่ละครั้งเขียนถึง stdout :

Δu Δv

ที่ΔuและΔvแต่ละหนึ่ง-1, ,0 1สิ่งนี้จะถูกเพิ่มเข้าไป(u,v)เพื่อกำหนดตำแหน่งใหม่ของคุณ เพียงเพื่อชี้แจงทิศทางมีดังนี้

(-1,-1) ( 0,-1) ( 1,-1)
(-1, 0) ( 0, 0) ( 1, 0)
(-1, 1) ( 0, 1) ( 1, 1)

ทางออกที่ดีที่สุดสำหรับตัวอย่างข้างต้นจะเป็น

1 0
1 -1
1 0

โปรดทราบว่าคอนโทรลเลอร์ไม่ได้แนบตัวเองเข้ากับstderrดังนั้นคุณสามารถใช้งานได้กับเอาต์พุตการดีบักทุกประเภทที่คุณอาจต้องใช้ในขณะพัฒนาบอทของคุณ โปรดลบ / แสดงความคิดเห็นเอาท์พุทใด ๆ ในรหัสที่คุณส่ง

บอทของคุณอาจใช้เวลาครึ่งวินาทีในการตอบสนองในแต่ละเทิร์น สำหรับรอบที่ใช้เวลานานคุณจะมีเวลางบประมาณ (ต่อแทร็ก) เป็นtarget/2วินาที ทุกครั้งที่การเลี้ยวใช้เวลานานกว่าครึ่งวินาทีเวลาเพิ่มเติมจะถูกหักออกจากงบประมาณเวลาของคุณ เมื่องบประมาณเวลาของคุณถึงศูนย์การแข่งขันปัจจุบันจะถูกยกเลิก

ใหม่:สำหรับเหตุผลในทางปฏิบัติฉันต้องตั้งค่าขีด จำกัด หน่วยความจำ (เนื่องจากหน่วยความจำดูเหมือนว่าจะมีการ จำกัด มากกว่าเวลาสำหรับขนาดแทร็กที่เหมาะสม) ดังนั้นผมจะต้องยกเลิกการทดสอบการทำงานใด ๆ ที่แข่งใช้มากกว่า1GBหน่วยความจำที่วัดโดยProcess Explorerเป็นไบต์ส่วนตัว

เกณฑ์การให้คะแนน

มีเกณฑ์มาตรฐาน 20 เพลง สำหรับแต่ละแทร็ก:

  • หากคุณดำเนินการติดตามคะแนนของคุณคือจำนวนของการเคลื่อนไหวที่คุณต้องไปถึงเซลล์เป้าหมายโดยแบ่งออกเป็นtarget
  • หากคุณหมดเวลา / หน่วยความจำหรือไม่ถึงเป้าหมายก่อนที่targetจะเปลี่ยนผ่านไปหรือคุณที่ดินในผนัง / ออกจากขอบเขตในเวลาใด ๆ 2คะแนนของคุณคือ
  • หากโปรแกรมของคุณไม่ได้กำหนดไว้คะแนนของคุณคือค่าเฉลี่ยมากกว่า 10 การรันบนแทร็กนั้น (โปรดระบุสิ่งนี้ในคำตอบของคุณ)

คะแนนโดยรวมของคุณคือผลรวมของคะแนนเพลงแต่ละเพลง คะแนนต่ำสุดชนะ!

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

เน็คไททำลาย

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

หากมีการผูก (เนื่องจากคำตอบหลายคำตอบในการแก้ไขแทร็กทั้งหมดอย่างเหมาะสมหรืออย่างอื่น) ฉันจะเพิ่มกรณีทดสอบเพิ่มเติม (ใหญ่กว่า) เพื่อแยกการผูก เพื่อหลีกเลี่ยงความลำเอียงของมนุษย์เมื่อสร้างตัวแบ่งเบรกเกอร์เหล่านี้จะถูกสร้างขึ้นในลักษณะที่แน่นอน:

  • ฉันจะเพิ่มความยาวด้านnโดย10เปรียบเทียบกับเพลงสุดท้ายที่สร้างด้วยวิธีนี้ (ฉันอาจข้ามขนาดได้หากพวกเขาไม่ได้ผูกไท)
  • พื้นฐานคือเวกเตอร์กราฟิกนี้
  • สิ่งนี้จะได้รับการแรสเตอร์ด้วยความละเอียดที่ต้องการโดยใช้ข้อมูลโค้ด Mathematicaนี้
  • จุดเริ่มต้นอยู่ที่มุมซ้ายบน โดยเฉพาะอย่างยิ่งมันจะเป็นเซลล์ด้านซ้ายสุดของแถวบนสุดของจุดสิ้นสุดของแทร็กนั้น
  • เป้าหมายอยู่ที่มุมขวาล่าง โดยเฉพาะอย่างยิ่งมันจะเป็นเซลล์ที่ถูกต้องที่สุดของแถวล่างสุดของแทร็กนั้น
  • ประสงค์target4*n

n = 50ติดตามสุดท้ายของมาตรฐานเริ่มต้นที่ถูกสร้างขึ้นแล้วเช่นนี้ด้วย

ผู้ควบคุม

โปรแกรมที่ทดสอบการส่งนั้นเขียนด้วย Ruby และสามารถพบได้ใน GitHubพร้อมกับไฟล์มาตรฐานที่ฉันจะใช้ นอกจากนี้ยังมีบอทตัวอย่างที่เรียกว่าrandomracer.rbในนั้นซึ่งเพียงแค่เลือกการเคลื่อนไหวแบบสุ่ม คุณสามารถใช้โครงสร้างพื้นฐานเป็นจุดเริ่มต้นสำหรับบอทของคุณเพื่อดูว่าการสื่อสารทำงานอย่างไร

คุณสามารถเรียกใช้บ็อตของคุณเองกับไฟล์แทร็คที่คุณต้องการดังนี้:

ruby controller.rb track_file_name command to run your racer

เช่น

ruby controller.rb benchmark.txt ruby randomracer.rb

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

สวิตช์บรรทัดคำสั่ง

คุณสามารถเพิ่มสวิตช์บรรทัดคำสั่ง-v, -s, -tก่อนที่ชื่อไฟล์มาตรฐานของ หากคุณต้องการใช้สวิตช์หลายตัวคุณสามารถทำได้เช่น-vsกัน นี่คือสิ่งที่พวกเขาทำ:

-v (verbose): ใช้สิ่งนี้เพื่อสร้างผลลัพธ์การดีบักเพิ่มเติมจากคอนโทรลเลอร์

-s (เงียบ): หากคุณต้องการติดตามตำแหน่งและความเร็วด้วยตัวคุณเองและไม่สนใจงบประมาณเวลาคุณสามารถปิดเอาต์พุตสามบรรทัดเหล่านั้นในแต่ละเทิร์น (ส่งไปยังการส่งของคุณ) ด้วยการตั้งค่าสถานะนี้

-t(เพลง): ให้คุณเลือกเพลงที่จะทดสอบ เช่น-t "1,2,5..8,15"จะทดสอบแทร็ก 1, 2, 5, 6, 7, 8 และ 15 เท่านั้น ขอบคุณมากสำหรับVenteroสำหรับคุณลักษณะนี้และตัวแยกวิเคราะห์ตัวเลือก

การส่งของคุณ

โดยสรุปโปรดระบุสิ่งต่อไปนี้ในคำตอบของคุณ:

  • คะแนนของคุณ.
  • หากคุณกำลังใช้การสุ่มโปรดระบุสิ่งนี้เพื่อที่ฉันจะได้คะแนนเฉลี่ยของคุณมากกว่าการวิ่งหลายครั้ง
  • รหัสสำหรับการส่งของคุณ
  • ตำแหน่งของคอมไพเลอร์หรือล่ามฟรีสำหรับภาษาที่คุณเลือกซึ่งทำงานบนเครื่อง Windows 8
  • คำแนะนำในการรวบรวมหากจำเป็น
  • สตริงคำสั่ง Windows เพื่อเรียกใช้การส่งของคุณ
  • การส่งของคุณต้องการการ-sตั้งค่าสถานะหรือไม่
  • (เป็นทางเลือก)แทร็กใหม่ที่แก้ไขได้ซึ่งจะถูกเพิ่มในเกณฑ์มาตรฐาน ฉันจะกำหนดเหตุผลที่เหมาะสมtargetสำหรับการติดตามของคุณด้วยตนเอง เมื่อเพิ่มแทร็กในเกณฑ์มาตรฐานฉันจะแก้ไขมันออกจากคำตอบของคุณ ฉันขอสงวนสิทธิ์ในการขอแทร็คอื่น (ในกรณีที่คุณเพิ่มแทร็กที่มีขนาดใหญ่อย่างไม่เป็นสัดส่วนรวมถึงศิลปะ ASCII ที่ไม่สุภาพในแทร็ก ฯลฯ ) เมื่อฉันเพิ่มกรณีทดสอบลงในชุดเบนช์มาร์กฉันจะแทนที่แทร็กในคำตอบของคุณด้วยลิงก์ไปยังแทร็กในไฟล์เบนช์มาร์กเพื่อลดความยุ่งเหยิงในโพสต์นี้

อย่างที่คุณเห็นฉันจะทดสอบการส่งทั้งหมดในเครื่อง Windows 8 หากไม่มีวิธีใดที่จะทำให้การส่งของคุณทำงานบน Windows ได้ฉันสามารถลองใช้ Ubuntu VM ได้เช่นกัน สิ่งนี้จะช้ากว่านี้มากดังนั้นหากคุณต้องการ จำกัด เวลาให้มากที่สุดตรวจสอบให้แน่ใจว่าโปรแกรมของคุณทำงานบน Windows

ขอให้คนขับรถที่ดีที่สุดออกมาดุร้าย!

แต่ฉันต้องการเล่น!

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

ลีดเดอร์บอร์ด

ปรับปรุงล่าสุด: 01/09/2014, 21:29 UTC
ติดตามมาตรฐาน: 25
ขนาดไทเบรคเกอร์: 290, 440

  1. 6.86688 - Kuroi Neko
  2. 8.73108 - user2357112 - การส่งครั้งที่ 2
  3. 9.86627 - nneonneo
  4. 10.66109 - user2357112 - การส่งครั้งที่ 1
  5. 12.49643 - เรย์
  6. 40.0759 - นามแฝง 117 (ความน่าจะเป็น)

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

คำตอบ:


5

C ++ 11 - 6.66109

การใช้งานการค้นหาแบบกว้างครั้งแรกเป็นการปรับให้เหมาะสมเท่านั้น

มันจะต้องทำงานด้วยตัวเลือก-s
อินพุตไม่ถูกทำให้สะอาดเลยแทร็กที่ผิดอาจเปลี่ยนเป็นฟักทองได้

ฉันทดสอบด้วย Microsoft Visual C ++ 2013, ปล่อยบิลด์ด้วยแฟล็ก / O2 เริ่มต้น (ปรับให้เหมาะสมสำหรับความเร็ว)
สร้างตกลงด้วย g ++ และ Microsoft IDE
ตัวจัดสรรหน่วยความจำแบร์โบนของฉันเป็นชิ้นส่วนของขยะดังนั้นอย่าคาดหวังว่ามันจะทำงานร่วมกับการติดตั้ง STL อื่น ๆunordered_set!

#include <cstdint>
#include <iostream>
#include <fstream>
#include <sstream>
#include <queue>
#include <unordered_set>

#define MAP_START 'S'
#define MAP_WALL  '#'
#define MAP_GOAL  '*'

#define NODE_CHUNK_SIZE   100 // increasing this will not improve performances
#define VISIT_CHUNK_SIZE 1024 // increasing this will slightly reduce mem consumption at the (slight) cost of speed

#define HASH_POS_BITS 8 // number of bits for one coordinate
#define HASH_SPD_BITS (sizeof(size_t)*8/2-HASH_POS_BITS)

typedef int32_t tCoord; // 32 bits required to overcome the 100.000 cells (insanely) long challenge

// basic vector arithmetics
struct tPoint {
    tCoord x, y;
    tPoint(tCoord x = 0, tCoord y = 0) : x(x), y(y) {}
    tPoint operator+ (const tPoint & p) { return tPoint(x + p.x, y + p.y); }
    tPoint operator- (const tPoint & p) { return tPoint(x - p.x, y - p.y); }
    bool operator== (const tPoint & p) const { return p.x == x && p.y == y;  }
};

// a barebone block allocator. Improves speed by about 30%
template <class T, size_t SIZE> class tAllocator
{
    T * chunk;
    size_t i_alloc;
    size_t m_alloc;
public:
    typedef T                 value_type;
    typedef value_type*       pointer;
    typedef const value_type* const_pointer;
    typedef std::size_t       size_type;
    typedef value_type&       reference;
    typedef const value_type& const_reference;
    tAllocator()                                              { m_alloc = i_alloc = SIZE; }
    template <class U> tAllocator(const tAllocator<U, SIZE>&) { m_alloc = i_alloc = SIZE; }
    template <class U> struct rebind { typedef tAllocator<U, SIZE> other; };
    pointer allocate(size_type n, const_pointer = 0)
    {
        if (n > m_alloc) { i_alloc = m_alloc = n; }      // grow max size if request exceeds capacity
        if ((i_alloc + n) > m_alloc) i_alloc = m_alloc;  // dump current chunk if not enough room available
        if (i_alloc == m_alloc) { chunk = new T[m_alloc]; i_alloc = 0; } // allocate new chunk when needed
        T * mem = &chunk[i_alloc];
        i_alloc += n;
        return mem;
    }
    void deallocate(pointer, size_type) { /* memory is NOT released until process exits */ }
    void construct(pointer p, const value_type& x) { new(p)value_type(x); }
    void destroy(pointer p) { p->~value_type(); }
};

// a node in our search graph
class tNode {
    static tAllocator<tNode, NODE_CHUNK_SIZE> mem; // about 10% speed gain over a basic allocation
    tNode * parent;
public:
    tPoint pos;
    tPoint speed;
    static tNode * alloc (tPoint pos, tPoint speed, tNode * parent) { return new (mem.allocate(1)) tNode(pos, speed, parent); }
    tNode (tPoint pos = tPoint(), tPoint speed = tPoint(), tNode * parent = nullptr) : parent(parent), pos(pos), speed(speed) {}
    bool operator== (const tNode& n) const { return n.pos == pos && n.speed == speed; }
    void output(void)
    {
        std::string output;
        tPoint v = this->speed;
        for (tNode * n = this->parent ; n != nullptr ; n = n->parent)
        {
            tPoint a = v - n->speed;
            v = n->speed;
            std::ostringstream ss;  // a bit of shitty c++ text I/O to print elements in reverse order
            ss << a.x << ' ' << a.y << '\n';
            output = ss.str() + output;
        }
        std::cout << output;
    }
};
tAllocator<tNode, NODE_CHUNK_SIZE> tNode::mem;

// node queueing and storing
static int num_nodes = 0;
class tNodeJanitor {
    // set of already visited nodes. Block allocator improves speed by about 20%
    struct Hasher { size_t operator() (tNode * const n) const 
    {
        int64_t hash = // efficient hashing is the key of performances
            ((int64_t)n->pos.x   << (0 * HASH_POS_BITS))
          ^ ((int64_t)n->pos.y   << (1 * HASH_POS_BITS))
          ^ ((int64_t)n->speed.x << (2 * HASH_POS_BITS + 0 * HASH_SPD_BITS))
          ^ ((int64_t)n->speed.y << (2 * HASH_POS_BITS + 1 * HASH_SPD_BITS));
        return (size_t)((hash >> 32) ^ hash);
        //return (size_t)(hash);
    }
    };
    struct Equalizer { bool operator() (tNode * const n1, tNode * const n2) const
        { return *n1 == *n2; }};
    std::unordered_set<tNode *, Hasher, Equalizer, tAllocator<tNode *, VISIT_CHUNK_SIZE>> visited;
    std::queue<tNode *> queue; // currently explored nodes queue
public:
    bool empty(void) { return queue.empty();  }
    tNode * dequeue() { tNode * n = queue.front(); queue.pop(); return n; }
    tNode * enqueue_if_new (tPoint pos, tPoint speed = tPoint(0,0), tNode * parent = nullptr)
    {
        tNode signature (pos, speed);
        tNode * n = nullptr;
        if (visited.find (&signature) == visited.end()) // the classy way to check if an element is in a set
        {
            n = tNode::alloc(pos, speed, parent);
            queue.push(n);
            visited.insert (n);
num_nodes++;
        }
        return n;
    }
};

// map representation
class tMap {
    std::vector<char> cell;
    tPoint dim; // dimensions
public:
    void set_size(tCoord x, tCoord y) { dim = tPoint(x, y); cell.resize(x*y); }
    void set(tCoord x, tCoord y, char c) { cell[y*dim.x + x] = c; }
    char get(tPoint pos)
    {
        if (pos.x < 0 || pos.x >= dim.x || pos.y < 0 || pos.y >= dim.y) return MAP_WALL;
        return cell[pos.y*dim.x + pos.x];
    }
    void dump(void)
    {
        for (int y = 0; y != dim.y; y++)
        {
            for (int x = 0; x != dim.x; x++) fprintf(stderr, "%c", cell[y*dim.x + x]);
            fprintf(stderr, "\n");
        }
    }
};

// race manager
class tRace {
    tPoint start;
    tNodeJanitor border;
    static tPoint acceleration[9];
public:
    tMap map;
    tRace ()
    {
        int target;
        tCoord sx, sy;
        std::cin >> target >> sx >> sy;
        std::cin.ignore();
        map.set_size (sx, sy);
        std::string row;
        for (int y = 0; y != sy; y++)
        {
            std::getline(std::cin, row);
            for (int x = 0; x != sx; x++)
            {
                char c = row[x];
                if (c == MAP_START) start = tPoint(x, y);
                map.set(x, y, c);
            }
        }
    }

    // all the C++ crap above makes for a nice and compact solver
    tNode * solve(void)
    {
        tNode * initial = border.enqueue_if_new (start);
        while (!border.empty())
        {
            tNode * node = border.dequeue();
            tPoint p = node->pos;
            tPoint v = node->speed;
            for (tPoint a : acceleration)
            {
                tPoint nv = v + a;
                tPoint np = p + nv;
                char c = map.get(np);
                if (c == MAP_WALL) continue;
                if (c == MAP_GOAL) return new tNode (np, nv, node);
                border.enqueue_if_new (np, nv, node);
            }
        }
        return initial; // no solution found, will output nothing
    }
};
tPoint tRace::acceleration[] = {
    tPoint(-1,-1), tPoint(-1, 0), tPoint(-1, 1),
    tPoint( 0,-1), tPoint( 0, 0), tPoint( 0, 1),
    tPoint( 1,-1), tPoint( 1, 0), tPoint( 1, 1)};

#include <ctime>
int main(void)
{
    tRace race;
    clock_t start = clock();
    tNode * solution = race.solve();
    std::cerr << "time: " << (clock()-start)/(CLOCKS_PER_SEC/1000) << "ms nodes: " << num_nodes << std::endl;
    solution->output();
    return 0;
}

ผล

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
 23      290 x 290    1160   0.16466   Racer reached goal at ( 269, 265) in 191 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.66109

การแสดง

ภาษา C ++ ที่เส็งเคร็งนั้นมีความสามารถพิเศษที่จะทำให้คุณกระโดดผ่านห่วงเพื่อย้ายไม้ขีดไฟ อย่างไรก็ตามคุณสามารถชักมันลงในการสร้างรหัสที่ค่อนข้างรวดเร็วและมีประสิทธิภาพของหน่วยความจำ

คร่ำเครียด

นี่คือกุญแจสำคัญในการจัดทำตารางแฮชที่ดีสำหรับโหนด นั่นคือปัจจัยสำคัญสำหรับความเร็วในการทำงาน
การใช้งานสองอย่างของunordered_set(GNU และ Microsoft) ให้ความแตกต่างความเร็วในการดำเนินการ 30% (ในความโปรดปรานของ GNU, yay!)

unordered_setความแตกต่างไม่ได้จริงๆน่าแปลกใจสิ่งที่มีคอร์เบ็ตรหัสที่ซ่อนอยู่หลัง

จากความอยากรู้ฉันทำสถิติบางอย่างเกี่ยวกับสถานะสุดท้ายของตารางแฮช
อัลกอริธึมทั้งสองจบลงด้วยอัตราส่วนถัง / องค์ประกอบเท่ากัน แต่การแบ่งพาร์ติชันแตกต่างกัน:
สำหรับ 290x290 tie breaker GNU ได้รับค่าเฉลี่ย 1.5 องค์ประกอบต่อการฝากข้อมูลที่ไม่ว่างเปล่าในขณะที่ Microsoft อยู่ที่ 5.8 (!)

ดูเหมือนว่าฟังก์ชั่นการแฮ็กของฉันไม่ได้ถูกสุ่มอย่างดีจาก Microsoft ... ฉันสงสัยว่าพวกใน Redmond ได้สร้างมาตรฐาน STL ของพวกเขาหรืออาจเป็นกรณีการใช้งานของฉันเพียงแค่สนับสนุนการใช้ GNU ...

แน่นอนว่าฟังก์ชั่นการแฮ็ชของฉันไม่มีที่ใดใกล้ ฉันสามารถใช้การผสมจำนวนเต็มปกติตามหลายกะ / โมลแต่ฟังก์ชั่นแฮชที่มีประสิทธิภาพต้องใช้เวลาในการคำนวณ

ดูเหมือนว่าจำนวนการสอบถามตารางแฮชจะสูงมากเมื่อเทียบกับจำนวนการแทรก ตัวอย่างเช่นใน 290x290 tie breaker คุณมีการแทรกประมาณ 3.6 ล้านครั้งสำหรับการค้นหา 22.7 ล้านครั้ง
ในบริบทนี้การแฮชย่อยที่ไม่ได้ผล แต่เร็วจะให้ผลการทำงานที่ดีกว่า

การจัดสรรหน่วยความจำ

การจัดหาตัวจัดสรรหน่วยความจำที่มีประสิทธิภาพมาเป็นอันดับสอง มันปรับปรุงการแสดงประมาณ 30% ไม่ว่าจะเป็นมูลค่ารหัสอึที่เพิ่มจะเป็นที่ถกเถียงกัน :)

รุ่นปัจจุบันใช้ระหว่าง 40 และ 55 ไบต์ต่อโหนด
ข้อมูลการทำงานต้องการ 24 ไบต์สำหรับโหนด (4 พิกัดและ 2 พอยน์เตอร์)
เนื่องจากกรณีทดสอบขนาด 100.000 บรรทัดที่มีการวิกลจริตพิกัดจะต้องถูกเก็บไว้ใน 4 ไบต์คำหรือคุณอาจได้รับ 8 ไบต์โดยใช้กางเกงขาสั้น (มีค่าพิกัดสูงสุด 32767) ไบต์ที่เหลือส่วนใหญ่จะถูกใช้โดยตารางแฮชของชุดที่ไม่เรียงลำดับ มันหมายถึงการจัดการข้อมูลใช้งานมากกว่าบิต "ประโยชน์"

และผู้ชนะคือ...

บนพีซีของฉันภายใต้ Win7 ตัวแบ่ง tie (กรณี 23, 290x290) ได้รับการแก้ไขโดยรุ่นที่แย่ที่สุด (เช่น Microsoft เรียบเรียง) ในเวลาประมาณ 2.2 วินาทีโดยใช้หน่วยความจำประมาณ 185 Mb
สำหรับการเปรียบเทียบผู้นำปัจจุบัน (รหัสไพ ธ อนโดย user2357112) ใช้เวลามากกว่า 30 วินาทีและใช้ประมาณ 780 Mb

ปัญหาตัวควบคุม

ฉันไม่แน่ใจว่าฉันอาจจะเขียนโค้ดใน Ruby เพื่อช่วยชีวิตฉันได้
อย่างไรก็ตามฉันเห็นและแฮ็กสองปัญหาจากรหัสควบคุม:

1) การอ่านแผนที่ใน track.rb

เมื่อติดตั้ง ruby ​​1.9.3 แล้วตัวอ่านแทร็กจะshift.to_iไม่สามารถใช้งานstring.linesได้
หลังจากลุยกันนาน ๆ ในเอกสาร Ruby ออนไลน์ฉันยอมแพ้กับสตริงและใช้อาร์เรย์กลางแทนอย่างเช่น (ขวาที่จุดเริ่มต้นของไฟล์):

def initialize(string)
    @track = Array.new;
    string.lines.each do |line|
        @track.push (line.chomp)
    end

2) ฆ่าผีเข้า controller.rb

ดังที่ผู้โพสต์คนอื่น ๆ ระบุไว้แล้วบางครั้งผู้ควบคุมพยายามจะฆ่ากระบวนการที่ออกไปแล้ว เพื่อหลีกเลี่ยงผลลัพธ์ข้อผิดพลาดที่น่ารังเกียจเหล่านี้ฉันเพียงจัดการข้อยกเว้นออกไปเช่นนั้น (ประมาณบรรทัดที่ 134):

if silent
    begin # ;!;
        Process.kill('KILL', racer.pid)
    rescue Exception => e
    end

กรณีทดสอบ

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

ในตัวอย่างนี้แผนที่ 100x400 พร้อมเป้าหมายที่มุมซ้ายบนและจุดเริ่มต้นที่มุมล่างขวา

แผนที่นี้มีวิธีแก้ปัญหาใน 28 ตาร์ แต่ผู้แก้ปัญหา BFS จะสำรวจรัฐหลายล้านแห่งเพื่อค้นหา (สำรวจเหมืองจำนวน 10.022.658 รัฐที่เยี่ยมชมใช้เวลาประมาณ 12 วินาทีและสูงสุดที่ 600 Mb!)

ด้วยพื้นผิวของเบรกเกอร์ขนาด 290x290 น้อยกว่าครึ่งทำให้ต้องใช้การเข้าชมโหนดมากกว่า 3 ครั้ง ในทางกลับกันตัวแก้ปัญหาแบบฮิวริสติก / A * ควรเอาชนะได้อย่างง่ายดาย

30
100 400
*...................................................................................................
....................................................................................................
                          < 400 lines in all >
....................................................................................................
....................................................................................................
...................................................................................................S

โบนัส: เวอร์ชัน PHP เทียบเท่า (แต่ค่อนข้างมีประสิทธิภาพน้อยกว่า)

นี่คือสิ่งที่ฉันเริ่มต้นด้วยก่อนที่ความช้าของภาษาโดยธรรมชาติจะโน้มน้าวให้ฉันใช้ C ++
ตารางแฮช PHP ภายในดูเหมือนจะไม่มีประสิทธิภาพเท่ากับ Python อย่างน้อยก็ในกรณีนี้ :)

<?php

class Trace {
    static $file;
    public static $state_member;
    public static $state_target;
    static function msg ($msg)
    {
        fputs (self::$file, "$msg\n");
    }

    static function dump ($var, $msg=null)
    {
        ob_start();
        if ($msg) echo "$msg ";
        var_dump($var);
        $dump=ob_get_contents();
        ob_end_clean();
        fputs (self::$file, "$dump\n");
    }

    function init ($fname)
    {
        self::$file = fopen ($fname, "w");
    }
}
Trace::init ("racer.txt");

class Point {
    public $x;
    public $y;

    function __construct ($x=0, $y=0)
    {
        $this->x = (float)$x;
        $this->y = (float)$y;
    }

    function __toString ()
    {
        return "[$this->x $this->y]";
    }

    function add ($v)
    {
        return new Point ($this->x + $v->x, $this->y + $v->y);
    }

    function vector_to ($goal)
    {
        return new Point ($goal->x - $this->x, $goal->y - $this->y);
    }
}

class Node {
    public $posx  , $posy  ;
    public $speedx, $speedy;
    private $parent;

    public function __construct ($posx, $posy, $speedx, $speedy, $parent)
    {
        $this->posx = $posx;
        $this->posy = $posy;
        $this->speedx = $speedx;
        $this->speedy = $speedy;
        $this->parent = $parent;
    }

    public function path ()
    {
        $res = array();
        $v = new Point ($this->speedx, $this->speedy);
        for ($node = $this->parent ; $node != null ; $node = $node->parent)
        {
            $nv = new Point ($node->speedx, $node->speedy);
            $a = $nv->vector_to ($v);
            $v = new Point ($node->speedx, $node->speedy);
            array_unshift ($res, $a);
        }
        return $res;
    }
}

class Map {

    private static $target;       // maximal number of turns
    private static $time;         // time available to solve
    private static $sx, $sy;      // map dimensions
    private static $cell;         // cells of the map
    private static $start;        // starting point
    private static $acceleration; // possible acceleration values

    public static function init ()
    {
        // read map definition
        self::$target = trim(fgets(STDIN));
        list (self::$sx, self::$sy) = explode (" ", trim(fgets(STDIN)));
        self::$cell = array();
        for ($y = 0 ; $y != self::$sy ; $y++) self::$cell[] = str_split (trim(fgets(STDIN)));
        self::$time = trim(fgets(STDIN));

        // get starting point
        foreach (self::$cell as $y=>$row)
        {
            $x = array_search ("S", $row);
            if ($x !== false)
            {
                self::$start = new Point ($x, $y);
Trace::msg ("start ".self::$start);
                break;
            }
        }

        // compute possible acceleration values
        self::$acceleration = array();
        for ($x = -1 ; $x <= 1 ; $x++)
        for ($y = -1 ; $y <= 1 ; $y++)
        {
            self::$acceleration[] = new Point ($x, $y);
        }
    }

    public static function solve ()
    {
        $now = microtime(true);
        $res = array();
        $border = array (new Node (self::$start->x, self::$start->y, 0, 0, null));
        $present = array (self::$start->x." ".self::$start->y." 0 0" => 1);
        while (count ($border))
        {
if ((microtime(true) - $now) > 1)
{
Trace::msg (count($present)." nodes, ".round(memory_get_usage(true)/1024)."K");
$now = microtime(true);
}
            $node = array_shift ($border);
//Trace::msg ("node $node->pos $node->speed");
            $px = $node->posx;
            $py = $node->posy;
            $vx = $node->speedx;
            $vy = $node->speedy;
            foreach (self::$acceleration as $a)
            {
                $nvx = $vx + $a->x;
                $nvy = $vy + $a->y;
                $npx = $px + $nvx;
                $npy = $py + $nvy;
                if ($npx < 0 || $npx >= self::$sx || $npy < 0 || $npy >= self::$sy || self::$cell[$npy][$npx] == "#")
                {
//Trace::msg ("invalid position $px,$py $vx,$vy -> $npx,$npy");
                    continue;
                }
                if (self::$cell[$npy][$npx] == "*")
                {
Trace::msg ("winning position $px,$py $vx,$vy -> $npx,$npy");
                    $end = new Node ($npx, $npy, $nvx, $nvy, $node);
                    $res = $end->path ();
                    break 2;
                }
//Trace::msg ("checking $np $nv");
                $signature = "$npx $npy $nvx $nvy";
                if (isset ($present[$signature])) continue;
//Trace::msg ("*** adding $np $nv");
                $border[] = new Node ($npx, $npy, $nvx, $nvy, $node);
                $present[$signature] = 1;
            }
        }
        return $res;
    }
}

ini_set("memory_limit","1000M");
Map::init ();
$res = Map::solve();
//Trace::dump ($res);
foreach ($res as $a) echo "$a->x $a->y\n";
?>

เอ่อ ... ตัวจัดสรรแบร์โบนของฉันเป็นแบร์โบนน้อยเกินไป ฉันจะเพิ่มอึที่จำเป็นเพื่อให้มันทำงานกับ g ++ แล้ว ขอโทษสำหรับเรื่องนั้น.

ตกลงนั่นคือการแก้ไข รุ่น g ++ ใช้งานได้จริงเร็วขึ้นประมาณ 30% ตอนนี้มันแสดงผลสถิติบางอย่างใน stderr อย่าลังเลที่จะแสดงความคิดเห็น (จากบรรทัดสุดท้ายของแหล่งที่มา) ขออภัยในความผิดพลาดอีกครั้ง

เอาล่ะมันใช้งานได้แล้วและฉันทำซ้ำคะแนนของคุณ มันเร็วมาก! :) ฉันจะเพิ่มกรณีทดสอบของคุณไปที่เกณฑ์มาตรฐาน แต่ฉันจะเปลี่ยนเป้าหมาย400เป็นเพราะมันสอดคล้องกับที่ฉันกำหนดเป้าหมายอื่น ๆ ทั้งหมด (ยกเว้น tie breaker) ฉันจะอัปเดตโพสต์หลักเมื่อฉันอ่านใหม่ทั้งหมดที่ส่งมา
Martin Ender

อัปเดตผลลัพธ์ ไม่จำเป็นต้องมี tie breaker เนื่องจากการส่งอื่น ๆ ทั้งหมดเกินขีด จำกัด หน่วยความจำในแทร็กทดสอบของคุณ ขอแสดงความยินดีด้วย! :)
Martin Ender

ขอบคุณ ที่จริงความท้าทายนี้ทำให้ฉันมีโอกาสขุดลงในตารางแฮช STL เหล่านี้ แม้ว่าฉันจะเกลียดความกล้าหาญของ C ++ แต่ฉันก็อดไม่ได้ที่จะถูกฆ่าเพราะความอยากรู้ เหมียว! :)

10

C ++, 5.4 (ดีที่สุด, ดีที่สุด)

โซลูชันการเขียนโปรแกรมแบบไดนามิก เหมาะสมที่สุด เร็วมาก: แก้ปัญหาทั้ง 20 ชุดใน 0.2 วินาที ควรเร็วเป็นพิเศษในเครื่อง 64 บิต สมมติว่าบอร์ดมีน้อยกว่า 32,000 แห่งในแต่ละทิศทาง (ซึ่งหวังว่าจะเป็นจริง)

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

g++ -O3คอมไพล์ด้วย อาจต้องใช้ C ++ 11 (สำหรับ<unordered_map>) ในการรันเพียงเรียกใช้ไฟล์ประมวลผลที่คอมไพล์แล้ว (ไม่รองรับแฟล็กหรือตัวเลือกใด ๆ อินพุตทั้งหมดจะถูกนำมาใช้บน stdin)

#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

#include <cstdint>

#define MOVES_INF (1<<30)

union state {
    struct {
        short px, py, vx, vy;
    };
    uint64_t val;
};

struct result {
    int nmoves;
    short dvx, dvy;
};

typedef std::unordered_map<uint64_t, result> cache_t;
int target, n, m;
std::vector<std::string> track;
cache_t cache;

static int solve(uint64_t val) {
    cache_t::iterator it = cache.find(val);
    if(it != cache.end())
        return it->second.nmoves;

    // prevent recursion
    result res;
    res.nmoves = MOVES_INF;
    cache[val] = res;

    state cur;
    cur.val = val;
    for(int dvx = -1; dvx <= 1; dvx++) for(int dvy = -1; dvy <= 1; dvy++) {
        state next;
        next.vx = cur.vx + dvx;
        next.vy = cur.vy + dvy;
        next.px = cur.px + next.vx;
        next.py = cur.py + next.vy;
        if(next.px < 0 || next.px >= n || next.py < 0 || next.py >= m)
            continue;
        char c = track[next.py][next.px];
        if(c == '*') {
            res.nmoves = 1;
            res.dvx = dvx;
            res.dvy = dvy;
            break;
        } else if(c == '#') {
            continue;
        } else {
            int score = solve(next.val) + 1;
            if(score < res.nmoves) {
                res.nmoves = score;
                res.dvx = dvx;
                res.dvy = dvy;
            }
        }
    }

    cache[val] = res;
    return res.nmoves;
}

bool solve_one() {
    std::string line;
    float time;

    std::cin >> target;
    // std::cin >> time; // uncomment to use "time" control
    std::cin >> n >> m;
    if(!std::cin)
        return false;
    std::cin.ignore(); // skip newline at end of "n m" line

    track.clear();
    track.reserve(m);

    for(int i=0; i<m; i++) {
        std::getline(std::cin, line);
        track.push_back(line);
    }

    cache.clear();

    state cur;
    cur.vx = cur.vy = 0;
    for(int y=0; y<m; y++) for(int x=0; x<n; x++) {
        if(track[y][x] == 'S') {
            cur.px = x;
            cur.py = y;
            break;
        }
    }

    solve(cur.val);

    int sol_len = 0;
    while(track[cur.py][cur.px] != '*') {
        cache_t::iterator it = cache.find(cur.val);
        if(it == cache.end() || it->second.nmoves >= MOVES_INF) {
            std::cerr << "Failed to solve at p=" << cur.px << "," << cur.py << " v=" << cur.vx << "," << cur.vy << std::endl;
            return true;
        }

        int dvx = it->second.dvx;
        int dvy = it->second.dvy;
        cur.vx += dvx;
        cur.vy += dvy;
        cur.px += cur.vx;
        cur.py += cur.vy;
        std::cout << dvx << " " << dvy << std::endl;
        sol_len++;
    }

    //std::cerr << "Score: " << ((float)sol_len) / target << std::endl;

    return true;
}

int main() {
    /* benchmarking: */
    //while(solve_one())
    //    ;

    /* regular running */
    solve_one();
    std::string line;
    while(std::cin) std::getline(std::cin, line);

    return 0;
}

ผล

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2    38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3    33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5     9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6    15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7    17 x 8        16   0.31250   Racer reached goal at ( 15, 0) in 5 turns.
  8    19 x 13       18   0.27778   Racer reached goal at ( 1, 11) in 5 turns.
  9    60 x 10      107   0.14953   Racer reached goal at ( 2, 6) in 16 turns.
 10    31 x 31      106   0.25472   Racer reached goal at ( 28, 0) in 27 turns.
 11    31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12    50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13   100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14    79 x 63      242   0.26860   Racer reached goal at ( 3, 42) in 65 turns.
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17    50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18    10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19    55 x 55       45   0.17778   Racer reached goal at ( 52, 26) in 8 turns.
 20    50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:              5.40009

ใหม่ Testcase


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

นักแข่งรถของคุณทำงานอย่างไรในกรณีทดสอบของคุณ?
user2357112

0.14 (14 ย้าย)
nneonneo

เวลานั้นถูกย้ายหรือย้าย / เป้าหมายหรือไม่? ถ้ามันเคลื่อนที่ / เป้าหมายมันจะทำงานได้อย่างไรในแง่ของเวลา?
user2357112

1
ฉันคิดว่าฉันพบข้อผิดพลาดในรหัสป้องกันวัฏจักร ก็ถือว่าแต่ละรัฐต้นน้ำค้นหาจากสถานะ S ซึ่งเป็นเส้นทางที่ดีที่สุดไม่สามารถที่จะกลับไปเอสมันอาจจะดูว่าถ้าเส้นทางที่ดีที่สุดไม่กลับไปที่ S แล้วรัฐไม่สามารถจะอยู่ในเส้นทางที่ดีที่สุด (ไม่นับตั้งแต่ที่เราจะทำได้ เพียงลบลูปที่มันเปิดอยู่และหาเส้นทางที่สั้นกว่า) และเราไม่สนใจว่าเราจะได้ผลลัพธ์ที่สูงเกินไปสำหรับสถานะนั้นหรือไม่ อย่างไรก็ตามหากเส้นทางที่ดีที่สุดผ่านสถานะ A และ B ในลำดับนั้น แต่การค้นหาครั้งแรกพบว่าในขณะที่ B ยังคงอยู่ในสแต็คผลลัพธ์ของ A จะถูกวางยาพิษโดยการป้องกันการวนซ้ำ
user2357112

6

Python 2 ที่กำหนดได้ดีที่สุด

นี่คือนักแข่งรถของฉัน ฉันยังไม่ได้ทดสอบตามเกณฑ์มาตรฐาน (ยังคงตรวจสอบเกี่ยวกับรุ่นและตัวติดตั้งของ Ruby ที่จะติดตั้ง) แต่ควรแก้ไขทุกอย่างอย่างเหมาะสมและอยู่ภายใต้ขีด จำกัด เวลา python whateveryoucallthefile.pyคำสั่งในการเรียกใช้มันเป็น ต้องการ-sแฟล็กตัวควบคุม

# Breadth-first search.
# Future directions: bidirectional search and/or A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def default_bfs_stop_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * num_not_wall

def bfs(start, walls, goals, stop_threshold=None):
    if stop_threshold is None:
        stop_threshold = default_bfs_stop_threshold(walls, goals)

    # State representation is (x, y, vx, vy)
    x, y = start
    initial_state = (x, y, 0, 0)
    frontier = {initial_state}
    # Visited set is tracked by a map from each state to the last move taken
    # before reaching that state.
    visited = {initial_state: None}

    while len(frontier) < stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for x, y, vx, vy in frontier:
            for dvx, dvy in acceleration_options:
                new_vx, new_vy = vx+dvx, vy+dvy
                new_x, new_y = x+new_vx, y+new_vy
                new_state = (new_x, new_y, new_vx, new_vy)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = dvx, dvy

                if goals[new_x][new_y]:
                    return construct_path_from_bfs(new_state, visited)
        frontier = new_frontier

def construct_path_from_bfs(goal_state, best_moves):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)

        x, y, vx, vy = current_state
        dvx, dvy = move
        old_x, old_y = x-vx, y-vy # not old_vx or old_vy
        old_vx, old_vy = vx-dvx, vy-dvy
        current_state = (old_x, old_y, old_vx, old_vy)
    return reversed_path[::-1]

def main():
    t = time.time()

    start, walls, goals = read_input()
    path = bfs(start, walls, goals, float('inf'))
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

หลังจากตรวจสอบนักแข่งของ nneonneo (แต่ไม่ได้ทดสอบจริง ๆ เนื่องจากฉันยังไม่มีคอมไพเลอร์ C ++) ฉันพบว่าดูเหมือนว่าจะทำการค้นหาพื้นที่รัฐเกือบหมดจดไม่ว่าเป้าหมายจะสั้นหรือสั้นแค่ไหน พบเส้นทางแล้ว ฉันยังพบว่ากฎของเวลาหมายถึงการสร้างแผนที่ด้วยโซลูชันที่ซับซ้อนและยาวซึ่งต้องใช้เวลาที่ยาวนานและน่าเบื่อ ดังนั้นการส่งแผนที่ของฉันจึงค่อนข้างง่าย:

ใหม่ Testcase

(GitHub ไม่สามารถแสดงเส้นยาวแทร็กได้*S.......[and so on].....)


การส่งเพิ่มเติม: Python 2, การค้นหาแบบสองทิศทาง

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

# Bidirectional search.
# Future directions: A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def bfs_to_bidi_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * (num_not_wall - num_goals)

class GridBasedGoalContainer(object):
    '''Supports testing whether a state is a goal state with `in`.

    Does not perform bounds checking.'''
    def __init__(self, goal_grid):
        self.goal_grid = goal_grid
    def __contains__(self, state):
        x, y, vx, vy = state
        return self.goal_grid[x][y]

def forward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    new_vx, new_vy = vx+dvx, vy+dvy
    new_x, new_y = x+new_vx, y+new_vy

    return (new_x, new_y, new_vx, new_vy)

def backward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    old_x, old_y = x-vx, y-vy
    old_vx, old_vy = vx-dvx, vy-dvy

    return (old_x, old_y, old_vx, old_vy)

def bfs(start, walls, goals):
    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=float('inf'),
        step_function=forward_step
    )

    return construct_path_from_bfs(goal_state, visited)

def general_bfs(
        frontier,
        visited,
        walls,
        goalcontainer,
        stop_threshold,
        step_function):

    while len(frontier) <= stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for state in frontier:
            for accel in acceleration_options:
                new_state = new_x, new_y, new_vx, new_vy = \
                        step_function(state, accel)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = accel

                if new_state in goalcontainer:
                    return new_state, frontier, visited
        frontier = new_frontier
    return None, frontier, visited

def max_velocity_component(n):
    # It takes a distance of at least 0.5*v*(v+1) to achieve a velocity of
    # v in the x or y direction. That means the map has to be at least
    # 1 + 0.5*v*(v+1) rows or columns long to accomodate such a velocity.
    # Solving for v, we get a velocity cap as follows.
    return int((2*n-1.75)**0.5 - 0.5)

def solver(
        start,
        walls,
        goals,
        mode='bidi'):

    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}
    if mode == 'bidi':
        stop_threshold = bfs_to_bidi_threshold(walls, goals)
    elif mode == 'bfs':
        stop_threshold = float('inf')
    else:
        raise ValueError('Unsupported search mode: {}'.format(mode))

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=stop_threshold,
        step_function=forward_step
    )

    if goal_state is not None:
        return construct_path_from_bfs(goal_state, visited)

    # Switching to bidirectional search.

    not_walls_or_goals = []
    goal_list = []
    for x in xrange(len(walls)):
        for y in xrange(len(walls[0])):
            if not walls[x][y] and not goals[x][y]:
                not_walls_or_goals.append((x, y))
            if goals[x][y]:
                goal_list.append((x, y))
    max_vx = max_velocity_component(len(walls))
    max_vy = max_velocity_component(len(walls[0]))
    reverse_visited = {(goal_x, goal_y, goal_x-prev_x, goal_y-prev_y): None
                        for goal_x, goal_y in goal_list
                        for prev_x, prev_y in not_walls_or_goals
                        if abs(goal_x-prev_x) <= max_vx
                        and abs(goal_y - prev_y) <= max_vy}
    reverse_frontier = set(reverse_visited)
    while goal_state is None:
        goal_state, reverse_frontier, reverse_visited = general_bfs(
            frontier=reverse_frontier,
            visited=reverse_visited,
            walls=walls,
            goalcontainer=frontier,
            stop_threshold=len(frontier),
            step_function=backward_step
        )
        if goal_state is not None:
            break
        goal_state, frontier, visited = general_bfs(
            frontier=frontier,
            visited=visited,
            walls=walls,
            goalcontainer=reverse_frontier,
            stop_threshold=len(reverse_frontier),
            step_function=forward_step
        )
    forward_path = construct_path_from_bfs(goal_state, visited)
    backward_path = construct_path_from_bfs(goal_state,
                                            reverse_visited,
                                            step_function=forward_step)
    return forward_path + backward_path[::-1]

def construct_path_from_bfs(goal_state,
                            best_moves,
                            step_function=backward_step):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)
        current_state = step_function(current_state, move)
    return reversed_path[::-1]

def main():
    start, walls, goals = read_input()
    t = time.time()
    path = solver(start, walls, goals)
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

บางครั้งมันก็ล้มเหลวในกรณีที่ 12 และ 13 อย่ารู้ว่าทำไมเนื่องจากข้อความแสดงข้อผิดพลาดค่อนข้างไม่เป็นมิตร
Ray

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

@ m.buettner ฉันพบเหตุผลเพิ่ม -s แล้วมันจะตกลง
เรย์

@ เรย์ใช่ฉันทำอย่างนั้น ฉันยังคงได้รับข้อผิดพลาดในแทร็ก 13 และ 14 เมื่อคอนโทรลเลอร์พยายามฆ่ากระบวนการแม้ว่าผลลัพธ์จะมีอยู่แล้ว ฉันเดาว่าฉันควรจะดู แต่ก็ไม่ได้ส่งผลกระทบต่อการให้คะแนนดังนั้นฉันจึงไม่ได้สนใจ
Martin Ender

น่าเสียดายที่ฉันต้องเพิ่มกฎอื่น ดูเหมือนว่าหน่วยความจำจะมีข้อ จำกัด มากกว่าเวลาในการท้าทายนี้ดังนั้นฉันจึงต้องตั้งค่า จำกัด การใช้หน่วยความจำอย่างหนัก การรันใด ๆ ที่นักแข่งรถของคุณใช้หน่วยความจำมากกว่า 1GB จะถูกยกเลิกไปเป็นเอฟเฟกต์เดียวกันกับที่เกินเวลาที่กำหนด สำหรับชุดของแทร็กปัจจุบันคะแนนของคุณไม่ได้รับผลกระทบจากการเปลี่ยนแปลงนี้ (ฉันคิดว่าคุณจะไปถึงขีด จำกัด ของเบรกเกอร์รอบn = 400ข้าง) โปรดแจ้งให้เราทราบหากคุณใช้การเพิ่มประสิทธิภาพใด ๆ เพื่อที่ฉันจะได้ทำการทดสอบอีกครั้ง
Martin Ender

3

Python 3: 6.49643 (เหมาะสมที่สุด BFS)

สำหรับไฟล์เบนช์มาตรฐาน 20 เคสมันได้คะแนน 5.35643 วิธีแก้ปัญหาโดย @nneonneo ไม่เหมาะสมเนื่องจากได้ 5.4 ข้อบกพร่องบางอย่างอาจจะ

วิธีการแก้ปัญหานี้ใช้ BFS เพื่อค้นหากราฟแต่ละสถานะการค้นหาอยู่ในรูปแบบของ (x, y, dx, dy) จากนั้นฉันก็ใช้แผนที่เพื่อทำแผนที่จากอเมริกาถึงระยะทาง ในกรณีที่เลวร้ายที่สุดก็คือเวลาและความซับซ้อนของพื้นที่คือ O (n ^ 2 m ^ 2) สิ่งนี้จะเกิดขึ้นไม่บ่อยนักเนื่องจากความเร็วจะไม่สูงเกินไปหรือนักแข่งจะพัง ที่จริงแล้วมันมีค่าใช้จ่าย 3 วินาทีบนเครื่องของฉันเพื่อทำครบทั้ง 22 testcase

from collections import namedtuple, deque
import itertools

Field = namedtuple('Map', 'n m grids')

class Grid:
    WALL = '#'
    EMPTY = '.'
    START = 'S'
    END = '*'

def read_nums():
    return list(map(int, input().split()))

def read_field():
    m, n = read_nums()
    return Field(n, m, [input() for i in range(n)])

def find_start_pos(field):
    return next((i, j)
        for i in range(field.n) for j in range(field.m)
        if field.grids[i][j] == Grid.START)

def can_go(field, i, j):
    return 0 <= i < field.n and 0 <= j < field.m and field.grids[i][j] != Grid.WALL

def trace_path(start, end, prev):
    if end == start:
        return
    end, step = prev[end]
    yield from trace_path(start, end, prev)
    yield step

def solve(max_turns, field, time):
    i0, j0 = find_start_pos(field)
    p0 = i0, j0, 0, 0
    prev = {}
    que = deque([p0])
    directions = list(itertools.product((-1, 0, 1), (-1, 0, 1)))

    while que:
        p = i, j, vi, vj = que.popleft()
        for dvi, dvj in directions:
            vi1, vj1 = vi + dvi, vj + dvj
            i1, j1 = i + vi1, j + vj1
            if not can_go(field, i1, j1):
                continue
            p1 = i1, j1, vi1, vj1
            if p1 in prev:
                continue
            que.append(p1)
            prev[p1] = p, (dvi, dvj)
            if field.grids[i1][j1] == Grid.END:
                return trace_path(p0, p1, prev)
    return []

def main():
    for dvy, dvx in solve(int(input()), read_field(), float(input())):
        print(dvx, dvy)

main()

# ผล

± % time ruby controller.rb benchmark.txt python ../mybfs.py                                                                                                                                                                             !9349
["benchmark.txt", "python", "../mybfs.py"]

Running 'python ../mybfs.py' against benchmark.txt

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.49643

ruby controller.rb benchmark.txt python ../mybfs.py  3.06s user 0.06s system 99% cpu 3.146 total

ใช่ตามความคิดเห็นของ user2357112 มีข้อผิดพลาดในการป้องกันวัฏจักรของ nneonneo เท่าที่ฉันรู้ความเร็วถูก จำกัด โดยO(√n)ที่จะทำให้การใช้งานของคุณO(n³)ในตารางกริด (เช่นเดียวกับคนอื่น ๆ ฉันคิดว่า) ฉันจะเพิ่มตัวแบ่งไทเทิลเพื่อทำคะแนนการส่งของคุณกับผู้ใช้ในภายหลังวันนี้
Martin Ender

Btw คุณวางแผนที่จะเพิ่มกรณีทดสอบอีกหรือไม่
Martin Ender

@ m.buettner ไม่ฉันไม่มีความเข้าใจที่ดีพอสำหรับเกมนี้ ดังนั้นตู้ทดสอบของฉันจะไม่เป็นสิ่งที่น่าสนใจ
เรย์

น่าเสียดายที่ฉันต้องเพิ่มกฎอื่น ดูเหมือนว่าหน่วยความจำจะมีข้อ จำกัด มากกว่าเวลาในการท้าทายนี้ดังนั้นฉันจึงต้องตั้งค่า จำกัด การใช้หน่วยความจำอย่างหนัก การรันใด ๆ ที่นักแข่งรถของคุณใช้หน่วยความจำมากกว่า 1GB จะถูกยกเลิกไปเป็นเอฟเฟกต์เดียวกันกับที่เกินเวลาที่กำหนด ด้วยกฎนี้การส่งของคุณเป็นครั้งแรกที่เกินขีด จำกัด ดังกล่าวในขนาดไทเบรกเกอร์n=270ซึ่งเป็นสาเหตุที่คุณอยู่เบื้องหลังการส่ง "ดีที่สุด" อีกสองรายการ ดังที่กล่าวไปแล้วการส่งของคุณก็ช้าที่สุดในสามอันดับดังนั้นจะเป็นอันดับที่สามอยู่ดี
Martin Ender

โปรดแจ้งให้เราทราบหากคุณใช้การเพิ่มประสิทธิภาพใด ๆ เพื่อให้ฉันสามารถรันการทดสอบอีกครั้งได้
Martin Ender

1

RandomRacer ~ 40.0 (เฉลี่ยมากกว่า 10 รอบ)

ไม่ใช่ว่าบ็อตนี้จะไม่จบแทร็ก แต่น้อยกว่าหนึ่งครั้งใน 10 ครั้ง (ฉันได้รับคะแนนไม่ใช่กรณีเลวร้ายที่สุดทุก ๆ 20 ถึง 30 แบบจำลองหรือมากกว่านั้น)

นี่เป็นส่วนใหญ่เพื่อทำหน้าที่เป็นกรณีพื้นฐานและแสดงให้เห็นถึงการนำไปใช้ (Ruby) ที่เป็นไปได้สำหรับนักแข่ง:

# Parse initial input
target = gets.to_i
size = gets.split.map(&:to_i)
track = []
size[1].times do
    track.push gets
end
time_budget = gets.to_f

# Find start position
start_y = track.find_index { |row| row['S'] }
start_x = track[start_y].index 'S'

position = [start_x, start_y]
velocity = [0, 0]

while true
    x = rand(3) - 1
    y = rand(3) - 1
    puts [x,y].join ' '
    $stdout.flush

    first_line = gets
    break if !first_line || first_line.chomp.empty?

    position = first_line.split.map(&:to_i)
    velocity = gets.split.map(&:to_i)
    time_budget = gets.to_f
end

เรียกใช้ด้วย

ruby controller.rb benchmark.txt ruby randomracer.rb

1

Random Racer 2.0, ~ 31

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

นำมาใช้ใน Java รวบรวมด้วย java8 แต่ Java 6 ควรจะดี ไม่มีพารามิเตอร์บรรทัดคำสั่ง มีกลุ่มของลำดับชั้นที่ดีงามดังนั้นฉันคิดว่าฉันทำจาวาที่ถูกต้อง

import java.util.Scanner;
import java.util.Random;
import java.util.ArrayList;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class VectorRacing   {
    private static Scanner in = new Scanner(System.in);
    private static Random rand = new Random();
    private static Track track;
    private static Racer racer;
    private static int target;
    private static double time;
    public static void main(String[] args)  {
        init();
        main_loop();
    }
    private static void main_loop() {
        Scanner linescan;
        String line;
        int count = 0,
            x, y, u, v;

        while(!racer.lost() && !racer.won() && count < target)  {
            Direction d = racer.think();
            racer.move(d);
            count++;
            System.out.println(d);

            line = in.nextLine();
            if(line.equals("")) {
                break;
            }
            linescan = new Scanner(line);
            x = linescan.nextInt();
            y = linescan.nextInt();
            linescan = new Scanner(in.nextLine());
            u = linescan.nextInt();
            v = linescan.nextInt();
            time = Double.parseDouble(in.nextLine());

            assert x == racer.location.x;
            assert y == racer.location.y;
            assert u == racer.direction.x;
            assert v == racer.direction.y;
        }
    }
    private static void init()  {
        target = Integer.parseInt(in.nextLine());
        int width = in.nextInt();
        int height = Integer.parseInt(in.nextLine().trim());
        String[] ascii = new String[height];
        for(int i = 0; i < height; i++) {
            ascii[i] = in.nextLine();
        }
        time = Double.parseDouble(in.nextLine());
        track = new Track(width, height, ascii);
        for(int y = 0; y < ascii.length; y++)   {
            int x = ascii[y].indexOf("S");
            if( x != -1)    {
                racer = new RandomRacer(track, new Location(x, y));
                break;
            }
        }
    }

    public static class RandomRacer extends Racer   {
        public RandomRacer(Track t, Location l) {
            super(t, l);
        }
        public Direction think()    {
            ArrayList<Pair<Location, Direction> > possible = this.getLocationsCanMoveTo();
            if(possible.size() == 0)    {
                return Direction.NONE;
            }
            Pair<Location, Direction> ret = null;
            do  {
                ret = possible.get(rand.nextInt(possible.size()));
            }   while(possible.size() != 1 && ret.a.equals(this.location));
            return ret.b;
        }
    }

    // Base things
    public enum Direction   {
        NORTH("0 -1"), SOUTH("0 1"), EAST("1 0"), WEST("-1 0"), NONE("0 0"),
        NORTH_EAST("1 -1"), NORTH_WEST("-1 -1"), SOUTH_EAST("1 1"), SOUTH_WEST("-1 1");

        private final String d;
        private Direction(String d) {this.d = d;}
        public String toString()    {return d;}
    }
    public enum Cell    {
        WALL('#'), GOAL('*'), ROAD('.'), OUT_OF_BOUNDS('?');

        private final char c;
        private Cell(char c)    {this.c = c;}
        public String toString()    {return "" + c;}
    }

    public static class Track   {
        private Cell[][] track;
        private int target;
        private double time;
        public Track(int width, int height, String[] ascii) {
            this.track = new Cell[width][height];
            for(int y = 0; y < height; y++) {
                for(int x = 0; x < width; x++)  {
                    switch(ascii[y].charAt(x))  {
                        case '#':   this.track[x][y] = Cell.WALL; break;
                        case '*':   this.track[x][y] = Cell.GOAL; break;
                        case '.':
                        case 'S':   this.track[x][y] = Cell.ROAD; break;
                        default:    System.exit(-1);
                    }
                }
            }
        }
        public Cell atLocation(Location loc)    {
            if(loc.x < 0 || loc.x >= track.length || loc.y < 0 || loc.y >= track[0].length) return Cell.OUT_OF_BOUNDS;
            return track[loc.x][loc.y];
        }

        public String toString()    {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(bos);
            for(int y = 0; y < track[0].length; y++)    {
                for(int x = 0; x < track.length; x++)   {
                    ps.append(track[x][y].toString());
                }
                ps.append('\n');
            }
            String ret = bos.toString();
            ps.close();
            return ret;
        }
    }

    public static abstract class Racer  {
        protected Velocity tdir;
        protected Location tloc;
        protected Track track;
        public Velocity direction;
        public Location location;

        public Racer(Track track, Location start)   {
            this.track = track;
            direction = new Velocity(0, 0);
            location = start;
        }
        public boolean canMove() throws GoHereDammitException {return canMove(Direction.NONE);}
        public boolean canMove(Direction d) throws GoHereDammitException    {
            tdir = new Velocity(direction);
            tloc = new Location(location);
            tdir.add(d);
            tloc.move(tdir);
            Cell at = track.atLocation(tloc);
            if(at == Cell.GOAL) {
                throw new GoHereDammitException();
            }
            return at == Cell.ROAD;
        }
        public ArrayList<Pair<Location, Direction> > getLocationsCanMoveTo()    {
            ArrayList<Pair<Location, Direction> > ret = new ArrayList<Pair<Location, Direction> >(9);
            for(Direction d: Direction.values())    {
                try {
                    if(this.canMove(d)) {
                        ret.add(new Pair<Location, Direction>(tloc, d));
                    }
                }   catch(GoHereDammitException e)  {
                    ret.clear();
                    ret.add(new Pair<Location, Direction>(tloc, d));
                    return ret;
                }
            }
            return ret;
        }
        public void move()  {move(Direction.NONE);}
        public void move(Direction d)   {
            direction.add(d);
            location.move(direction);
        }
        public boolean won()    {
            return track.atLocation(location) == Cell.GOAL;
        }
        public boolean lost()   {
            return track.atLocation(location) == Cell.WALL || track.atLocation(location) == Cell.OUT_OF_BOUNDS;
        }
        public String toString()    {
            return location + ", " + direction;
        }
        public abstract Direction think();

        public class GoHereDammitException extends Exception    {
            public GoHereDammitException()  {}
        }
    }

    public static class Location extends Point  {
        public Location(int x, int y)   {
            super(x, y);
        }
        public Location(Location l) {
            super(l);
        }
        public void move(Velocity d)    {
            this.x += d.x;
            this.y += d.y;
        }
    }

    public static class Velocity extends Point  {
        public Velocity(int x, int y)   {
            super(x, y);
        }
        public Velocity(Velocity v) {
            super(v);
        }
        public void add(Direction d)    {
            if(d == Direction.NONE) return;
            if(d == Direction.NORTH || d == Direction.NORTH_EAST || d == Direction.NORTH_WEST)  this.y--;
            if(d == Direction.SOUTH || d == Direction.SOUTH_EAST || d == Direction.SOUTH_WEST)  this.y++;
            if(d == Direction.EAST || d == Direction.NORTH_EAST || d == Direction.SOUTH_EAST)   this.x++;
            if(d == Direction.WEST || d == Direction.NORTH_WEST || d == Direction.SOUTH_WEST)   this.x--;
        }
    }

    public static class Point   {
        protected int x, y;
        protected Point(int x, int y)   {
            this.x = x;
            this.y = y;
        }
        protected Point(Point c)    {
            this.x = c.x;
            this.y = c.y;
        }
        public int getX()   {return x;}
        public int getY()   {return y;}
        public String toString()    {return "(" + x + ", " + y + ")";}
        public boolean equals(Point p)  {
            return this.x == p.x && this.y == p.y;
        }
    }

    public static class Pair<T, U>  {
        public T a;
        public U b;
        public Pair(T t, U u)   {
            a=t;b=u;
        }
    }
}

ผลลัพธ์ (กรณีที่ดีที่สุดที่ฉันเคยเห็น)

Running 'java VectorRacing' against ruby-runner/benchmark.txt

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.38889   Racer reached goal at ( 36, 0) in 14 turns.
  2    38 x 1        37   0.54054   Racer reached goal at ( 37, 0) in 20 turns.
  3    33 x 1        32   0.62500   Racer reached goal at ( 32, 0) in 20 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 9, 8) in 4 turns.
  5     9 x 6         8   0.75000   Racer reached goal at ( 6, 2) in 6 turns.
  6    15 x 7        16   2.00000   Racer did not reach the goal within 16 turns.
  7    17 x 8        16   2.00000   Racer hit a wall at position ( 8, 2).
  8    19 x 13       18   0.44444   Racer reached goal at ( 16, 2) in 8 turns.
  9    60 x 10      107   0.65421   Racer reached goal at ( 0, 6) in 70 turns.
 10    31 x 31      106   2.00000   Racer hit a wall at position ( 25, 9).
 11    31 x 31      106   2.00000   Racer hit a wall at position ( 8, 1).
 12    50 x 20       50   2.00000   Racer hit a wall at position ( 27, 14).
 13   100 x 100    2600   2.00000   Racer went out of bounds at position ( 105, 99).
 14    79 x 63      242   2.00000   Racer went out of bounds at position (-2, 26).
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   2.00000   Racer went out of bounds at position (-2, 0).
 17    50 x 1        55   2.00000   Racer went out of bounds at position ( 53, 0).
 18    10 x 7        23   2.00000   Racer went out of bounds at position ( 10, 2).
 19    55 x 55       45   0.33333   Racer reached goal at ( 4, 49) in 15 turns.
 20    50 x 50      200   2.00000   Racer hit a wall at position ( 14, 7).
-------------------------------------------------------------------------------------
TOTAL SCORE:             26.45641

ใช่ฉันได้รับมันทำงานแม้ว่าฉันจะต้องเรียกใช้จากไดเรกทอรีที่.classไฟล์นั้นด้วยเหตุผลบางอย่าง (แทนไดเรกทอรีที่เป็นตัวควบคุม) Ping me (พร้อมความคิดเห็น) หากคุณตัดสินใจที่จะเพิ่ม testcase ดังนั้นฉันสามารถเพิ่มไปยังเกณฑ์มาตรฐานได้ คะแนนของคุณอยู่ที่ประมาณ 33 มากกว่า 10 ครั้ง (ดูลีดเดอร์บอร์ด) แต่สิ่งนี้อาจเปลี่ยนแปลงได้กับทุกแทร็กทดสอบใหม่ที่เพิ่มเข้ามาในเกณฑ์มาตรฐาน
Martin Ender

อ่าทำให้มันไปรันได้จากสารบบอื่นเช่นกัน สำหรับผู้ที่ไม่คุ้นเคยกับ Java บนบรรทัดคำสั่ง:java -cp path/to/class/file VectorRacing
มาร์ตินเอนเดอร์

อ๋อใช่ฉันทำชั้นเรียนมากมาย (13 อันแน่นอน) ฉันมักจะเรียกใช้สคริปต์ของคุณจากไดเรกทอรีคลาสของฉันดังนั้นฉันจึงไม่ได้ทดสอบสิ่งนั้น ฉันอาจทำแบบทดสอบ แต่ฉันคิดว่าฉันจะลองแข่งรถที่ไม่ใช่แบบสุ่มก่อน
นามแฝง

แน่ใจ หากคุณมีโปรดเพิ่มมันเป็นคำตอบที่แยกต่างหาก (และอย่าลังเลที่จะเพิ่มกรณีทดสอบหนึ่งกรณีสำหรับแต่ละกรณี)
Martin Ender

น่าเสียดายที่ฉันต้องเพิ่มกฎอื่น ดูเหมือนว่าหน่วยความจำจะมีข้อ จำกัด มากกว่าเวลาในการท้าทายนี้ดังนั้นฉันจึงต้องตั้งค่า จำกัด การใช้หน่วยความจำอย่างหนัก การรันใด ๆ ที่นักแข่งรถของคุณใช้หน่วยความจำมากกว่า 1GB จะถูกยกเลิกไปเป็นเอฟเฟกต์เดียวกันกับที่เกินเวลาที่กำหนด สำหรับชุดของแทร็กปัจจุบันคะแนนของคุณไม่ได้รับผลกระทบจากการเปลี่ยนแปลงนี้ (และอาจไม่เคยเกิดขึ้น)
Martin Ender
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.