โครงสร้างข้อมูลสำหรับเกมกระดานสองมิติในภาษาที่ใช้งานได้


9

ฉันกำลังสร้างการใช้งานMiniMaxอย่างง่ายในภาษาโปรแกรม Elixir เนื่องจากมีเกมความรู้ที่สมบูรณ์แบบมากมาย (tic toe, connect-four, หมากรุก, หมากรุกเป็นต้น) การใช้งานนี้อาจเป็นกรอบในการสร้างเกม AIs สำหรับเกมเหล่านี้

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

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

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

  • รายการที่เชื่อมโยงมีเวลาค้นหา O (n) (เชิงเส้น) นอกจากนี้เนื่องจากเราไม่สามารถ 'สแกนและอัปเดตบอร์ด' ในการกวาดบนกระดานเพียงครั้งเดียวการใช้รายการดูเหมือนจะไม่สามารถทำได้
  • Tuplesมีเวลาค้นหา O (1) (คงที่) อย่างไรก็ตามการแสดงบอร์ดเป็น tuple ที่มีขนาดคงที่ทำให้ยากในการย้ำผ่านอันดับไฟล์ไฟล์ diagonals หรือสี่เหลี่ยมต่อเนื่องชนิดอื่น ๆ นอกจากนี้ทั้ง Elixir และ Haskell (ซึ่งเป็นภาษาที่ใช้งานได้สองภาษาที่ฉันรู้จัก) ไม่มีไวยากรณ์ในการอ่านองค์ประกอบที่nของ tuple สิ่งนี้จะทำให้เป็นไปไม่ได้ที่จะเขียนโซลูชันแบบไดนามิกที่สามารถใช้ได้กับบอร์ดที่มีขนาดตามอำเภอใจ

Elixir มีโครงสร้างข้อมูลแผนที่ในตัว (และ Haskell มีData.Map) ที่ให้การเข้าถึงองค์ประกอบ O (log n) (ลอการิทึม) (ลอการิทึม) ตอนนี้ฉันใช้แผนที่พร้อมสิ่งx, yอันดับที่เป็นตัวแทนของตำแหน่ง

สิ่งนี้ 'ใช้งานได้' แต่รู้สึกผิดที่ทำผิดกฎเกี่ยวกับแผนที่ด้วยวิธีนี้ถึงแม้ว่าฉันจะไม่รู้ว่าทำไม ฉันกำลังมองหาวิธีที่ดีกว่าในการจัดเก็บบอร์ดเกมสองมิติในภาษาโปรแกรมที่ใช้งานได้


1
ฉันไม่สามารถพูดเกี่ยวกับแพรคซิสได้ แต่มีสองสิ่งที่มาจากใจของฉันจากแฮสเค็ลล์: ซิปช่วยให้ "ขั้นตอน" คงที่เหนือโครงสร้างข้อมูลและโคโมดซึ่งเกี่ยวข้องกับรูดซิปโดยทฤษฎีบางอย่างที่ฉันจำไม่ได้ )
phipsgabler

บอร์ดนี้มีขนาดใหญ่แค่ไหน? Big O แสดงลักษณะของอัลกอริธึมที่ปรับขนาดได้รวดเร็วแค่ไหน บนกระดานขนาดเล็ก (พูดน้อยกว่า 100 ในแต่ละทิศทาง), O (1) เทียบกับ O (n) ไม่น่าจะมีความสำคัญเท่าไหร่ถ้าคุณแตะแต่ละตารางเพียงครั้งเดียว
Robert Harvey

@ RobertHarvey มันจะแตกต่างกันไป แต่เพื่อยกตัวอย่าง: ในหมากรุกเรามีกระดาน 64x64 แต่การคำนวณทั้งหมดเพื่อตรวจสอบว่าการเคลื่อนไหวใดที่เป็นไปได้และเพื่อกำหนดมูลค่าการเรียนรู้ของตำแหน่งปัจจุบัน (ความแตกต่างในเนื้อหาพระราชาในการตรวจสอบหรือไม่ผ่านเบี้ย ฯลฯ ) ทุกคนจำเป็นต้องเข้าถึงช่องสี่เหลี่ยมของกระดาน
Qqwy

1
คุณมีกระดานหมากรุกขนาด 8x8 ชิ้น ในภาษาที่ใช้หน่วยความจำเช่น C คุณสามารถทำการคำนวณทางคณิตศาสตร์เพื่อรับที่อยู่ที่แน่นอนของเซลล์ แต่นั่นไม่เป็นความจริงในภาษาที่มีการจัดการหน่วยความจำ มันจะไม่ทำให้ฉันประหลาดใจถ้ากระโดดข้าม (สูงสุด) 14 โหนดใช้เวลาประมาณเดียวกันในการจัดการกับองค์ประกอบอาร์เรย์ในภาษาที่จัดการหน่วยความจำ
Robert Harvey

คำตอบ:


6

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

filterWithKey (\k _ -> (snd k) == column) -- All pieces in given column
filterWithKey (\k _ -> (fst k) == row)    -- All pieces in given row
mapKeys (\(x, y) -> (-x, y))              -- Mirror

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

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

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


4

ฉันทำสิ่งนี้เมื่อไม่นานมานี้ใน F # และลงเอยด้วยการใช้รายการหนึ่งมิติ (ใน F # นั่นคือรายการที่ลิงก์เดียว) ในทางปฏิบัติความเร็วของตัวทำดัชนีรายการ O (n) ไม่ใช่คอขวดสำหรับขนาดของบอร์ดที่มนุษย์สามารถใช้งานได้ ฉันทดลองกับอาเรย์ประเภทอื่นเช่น 2d แต่ในท้ายที่สุดมันเป็นการแลกเปลี่ยนของการเขียนโค้ดมูลค่าการตรวจสอบความเท่าเทียมกันของฉันเองหรือการแปลอันดับและไฟล์เพื่อจัดทำดัชนีและย้อนกลับ หลังนั้นง่ายกว่า ฉันจะบอกว่าทำให้มันใช้งานได้ก่อนแล้วจึงเพิ่มประสิทธิภาพประเภทข้อมูลของคุณในภายหลังหากจำเป็น มันไม่น่าจะสร้างความแตกต่างอย่างมีนัยสำคัญพอ

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

let pos r f = {Rank = r; File = f} // immutable record type
// or
let pos r f = OnBoard (r, f) // algebraic type
...
let testBoard =
    Board.createEmpty ()
    |> Board.setPiece p (pos 1 2)
    |> ...

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


สวัสดีคุณมีรหัสเผยแพร่ที่ใดที่หนึ่งหรือไม่ ขณะนี้ฉันกำลังทำงานกับเกมหมากรุกใน F # (สำหรับโครงการที่สนุก) และแม้ว่าฉันจะใช้ Map <Square, Piece> เพื่อเป็นตัวแทนของบอร์ดฉันชอบที่จะเห็นว่าคุณใส่มันลงในกระดานอย่างไร ประเภทและโมดูล
asibahi

ไม่มันไม่ได้เผยแพร่ที่ไหนเลย
Kasey Speakman

ดังนั้นคุณจะพิจารณาการใช้งานปัจจุบันของฉันและแนะนำว่าจะปรับปรุงได้อย่างไร
asibahi

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