โปรแกรมเมอร์ปริศนา: การเข้ารหัสสถานะกระดานหมากรุกตลอดทั้งเกม


96

ไม่ใช่คำถามอย่างเคร่งครัดปริศนามากกว่า ...

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

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

ดังนั้นฉันคิดว่าฉันจะโยนหนึ่งในคำถามของฉันให้กับผู้ชม Stack Overflow

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

ไม่ต้องใช้รหัสสำหรับคำตอบเพียงแค่คำอธิบายของอัลกอริทึมที่คุณจะใช้

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

EDIT2: เพื่อความชัดเจนเพิ่มเติม ... โปรดจำไว้ว่าตัวเข้ารหัส / ตัวถอดรหัสเป็นกฎที่รับรู้ สิ่งเดียวที่ต้องจัดเก็บจริงๆคือตัวเลือกของผู้เล่น - สิ่งอื่นใดที่สามารถสันนิษฐานได้โดยตัวเข้ารหัส / ตัวถอดรหัส

แก้ไข 3: การเลือกผู้ชนะที่นี่จะเป็นเรื่องยาก :) คำตอบที่ดีมากมาย!


4
สถานะเริ่มต้นของเกมหมากรุกไม่ได้กำหนดไว้อย่างชัดเจนหรือไม่? ทำไมถึงต้องเข้ารหัส? ฉันคิดว่ามันน่าจะเพียงพอที่จะเข้ารหัสความแตกต่างระหว่างแต่ละเทิร์น (= การเคลื่อนไหว) เท่านั้น
tanascius

1
เขาสันนิษฐานว่าเกมสามารถเริ่มต้นด้วยการตั้งค่าเริ่มต้นตามกฎหมาย (เช่นเดียวกับในเกมหมากรุกที่คุณสามารถหาได้จากหนังสือพิมพ์)
Aaron Digulla

6
เพื่อให้เข้มงวดคุณจะต้องเข้ารหัสตำแหน่งที่ผ่านมาทั้งหมดด้วยเพราะหากตำแหน่งเดียวกันปรากฏขึ้นสามครั้งจะเป็นการดึงen.wikipedia.org/wiki/Threefold_repetition
flybywire

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

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

คำตอบ:


132

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

ปัญหา

ภาพนี้แสดงตำแหน่งหมากรุกเริ่มต้น หมากรุกเกิดขึ้นบนกระดาน 8x8 โดยผู้เล่นแต่ละคนเริ่มต้นด้วยชุดที่เหมือนกัน 16 ชิ้นซึ่งประกอบด้วย 8 เบี้ย, 2 rooks, 2 อัศวิน, 2 บาทหลวง, 1 ราชินีและ 1 คิงดังที่แสดงไว้ที่นี่:

เริ่มตำแหน่งหมากรุก

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

  1. e4 e5
  2. Nf3 Nc6

ซึ่งแปลเป็น:

  1. White ย้ายเบี้ยของกษัตริย์จาก e2 เป็น e4 (เป็นชิ้นเดียวที่สามารถไปถึง e4 ได้ด้วยเหตุนี้ "e4");
  2. Black ย้ายเบี้ยของกษัตริย์จาก e7 เป็น e5;
  3. White ย้ายอัศวิน (N) ไปที่ f3;
  4. แบล็คย้ายอัศวินไปที่ c6

บอร์ดมีลักษณะดังนี้:

เปิดก่อน

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

มีอะไรหายไปหรือคลุมเครือ? มากตามที่ปรากฎ

สถานะบอร์ดเทียบกับสถานะเกม

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

คาสติ้ง

เกมได้ดำเนินการดังนี้:

  1. e4 e5
  2. Nf3 Nc6
  3. Bb5 a6
  4. Ba4 Bc5

คณะกรรมการมีลักษณะดังนี้:

เปิดในภายหลัง

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

มีกลยุทธ์หลายอย่างที่สามารถใช้ในการจัดการกับปัญหานี้

ประการแรกเราสามารถจัดเก็บข้อมูลเพิ่มเติมได้ 6 บิต (1 สำหรับแต่ละโร๊คและราชา) เพื่อระบุว่าชิ้นส่วนนั้นเคลื่อนที่หรือไม่ เราสามารถปรับปรุงสิ่งนี้ได้โดยการจัดเก็บเพียงเล็กน้อยสำหรับหนึ่งในหกสี่เหลี่ยมนี้ถ้าชิ้นส่วนที่ถูกต้องอยู่ในนั้น อีกวิธีหนึ่งคือเราสามารถถือว่าชิ้นส่วนที่ไม่มีการเคลื่อนไหวแต่ละชิ้นเป็นชิ้นส่วนประเภทอื่นดังนั้นแทนที่จะเป็น 6 ชิ้นในแต่ละด้าน (เบี้ย, โกง, อัศวิน, บิชอป, ราชินีและราชา) มี 8 ชิ้น (เพิ่มโร็คที่ไม่เคลื่อนไหวและราชาที่ไม่เคลื่อนไหว)

En Passant

ที่แปลกประหลาดและมักจะละเลยการปกครองในหมากรุกก็คือการกิน

en passant

เกมมีความคืบหน้า

  1. e4 e5
  2. Nf3 Nc6
  3. Bb5 a6
  4. Ba4 Bc5
  5. OO b5
  6. Bb3 b4
  7. c4

จำนำของ Black บน b4 ตอนนี้มีตัวเลือกในการย้ายเบี้ยของเขาบน b4 ไปที่ c3 โดยใช้ White pawn บน c4 สิ่งนี้จะเกิดขึ้นในโอกาสแรกเท่านั้นซึ่งหมายความว่าหาก Black ส่งผ่านตัวเลือกในตอนนี้เขาไม่สามารถก้าวต่อไปได้ ดังนั้นเราจำเป็นต้องจัดเก็บสิ่งนี้

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

โปรโมชั่น

โปรโมชั่นจำนำ

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

  1. h8 = Q

นี่เป็นสิ่งสำคัญในปัญหาของเราเพราะนั่นหมายความว่าเราไม่สามารถนับจำนวนชิ้นที่คงที่ในแต่ละด้านได้ เป็นไปได้ทั้งหมด (แต่ไม่น่าเป็นไปได้อย่างเหลือเชื่อ) ที่ฝ่ายใดฝ่ายหนึ่งจะลงเอยด้วยราชินี 9 ตัว 10 โร๊ค 10 บาทหลวงหรือ 10 อัศวินหากเบี้ยทั้ง 8 ได้รับการเลื่อนขั้น

ทางตัน

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

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

สุดท้ายมีเป็นกฎห้าสิบย้าย ผู้เล่นสามารถเรียกร้องการจับฉลากได้หากไม่มีการเคลื่อนย้ายเบี้ยและไม่มีชิ้นส่วนใดถูกนำมาใช้ในการเคลื่อนไหวห้าสิบครั้งก่อนหน้านี้ดังนั้นเราจะต้องจัดเก็บจำนวนการเคลื่อนไหวนับตั้งแต่มีการเคลื่อนย้ายเบี้ยหรือชิ้นส่วนที่ถูกยึด (ล่าสุดของสองสิ่งนี้ต้องการ 6 บิต (0-63)

ตาของใคร?

แน่นอนว่าเราจำเป็นต้องรู้ด้วยว่าใครเป็นใครและนี่คือข้อมูลเพียงเล็กน้อย

สองปัญหา

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

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

เนื้อหาง่ายๆ

มีหกประเภท (เบี้ย, โกง, อัศวิน, บิชอป, ราชินีและราชา) แต่ละชิ้นอาจเป็นสีขาวหรือดำดังนั้นสี่เหลี่ยมจัตุรัสอาจมีหนึ่งใน 12 ชิ้นที่เป็นไปได้หรืออาจว่างเปล่าจึงมีความเป็นไปได้ 13 แบบ 13 สามารถจัดเก็บเป็น 4 บิต (0-15) ดังนั้นวิธีแก้ปัญหาที่ง่ายที่สุดคือการจัดเก็บ 4 บิตสำหรับแต่ละตารางคูณ 64 กำลังสองหรือ 256 บิตของข้อมูล

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

แต่เราสามารถทำได้ดีกว่า

การเข้ารหัสฐาน 13

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

วิธีแก้ปัญหาแรกถือว่าตำแหน่งเป็นเลขฐาน 64 หลัก 16 แต่ตามที่แสดงให้เห็นว่ามีความซ้ำซ้อนในข้อมูลนี้ (เป็นความเป็นไปได้ที่ไม่ได้ใช้ 3 ตัวต่อ "หลัก") เพื่อให้เราสามารถลดพื้นที่ตัวเลขลงเหลือ 64 ฐาน 13 หลัก แน่นอนว่าสิ่งนี้ไม่สามารถทำได้อย่างมีประสิทธิภาพเท่ากับฐาน 16 แต่จะช่วยประหยัดความต้องการในการจัดเก็บข้อมูล (และการลดพื้นที่จัดเก็บเป็นเป้าหมายของเรา)

ในฐาน 10 จำนวน 234 เทียบเท่ากับ 2 x 10 2 + 3 x 10 1 + 4 x 10 0

ในฐาน 16 ตัวเลข 0xA50 จะเท่ากับ 10 x 16 2 + 5 x 16 1 + 0 x 16 0 = 2640 (ทศนิยม)

ดังนั้นเราจึงสามารถเข้ารหัสตำแหน่งของเราเป็น P 0 x 13 63 + P 1 x 13 62 + ... + P 63 x 13 0ที่หน้าฉันหมายถึงเนื้อหาของตารางฉัน

2 256เท่ากับ 1.16e77 โดยประมาณ 13 64เท่ากับประมาณ 1.96e71 ซึ่งต้องการพื้นที่จัดเก็บ 237 บิต การประหยัดเพียง 7.5% นั้นมาพร้อมกับต้นทุนการจัดการที่เพิ่มขึ้นอย่างมีนัยสำคัญ

การเข้ารหัสฐานตัวแปร

ในกระดานกฎหมายบางชิ้นไม่สามารถปรากฏในช่องสี่เหลี่ยมได้ ตัวอย่างเช่นเบี้ยไม่สามารถเกิดขึ้นในอันดับแรกหรืออันดับที่แปดการลดความเป็นไปได้ของสี่เหลี่ยมเหล่านั้นเหลือ 11 ซึ่งจะลดจำนวนบอร์ดที่เป็นไปได้ลงเหลือ 11 16 x 13 48 = 1.35e70 (โดยประมาณ) โดยต้องใช้พื้นที่จัดเก็บ 233 บิต

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

ตัวอักษรความกว้างตัวแปร

ก่อนหน้านี้สองวิธีทั้งสองจะสามารถอธิบายเป็นความกว้างคงที่ตามตัวอักษรการเข้ารหัส สมาชิกแต่ละตัวใน 11, 13 หรือ 16 ของตัวอักษรจะถูกแทนที่ด้วยค่าอื่น "อักขระ" แต่ละตัวมีความกว้างเท่ากัน แต่ประสิทธิภาพจะดีขึ้นเมื่อพิจารณาว่าอักขระแต่ละตัวมีโอกาสไม่เท่ากัน

รหัสมอร์ส

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

สังเกตว่าตัวอักษร E ( ตัวอักษรที่ใช้บ่อยที่สุดในภาษาอังกฤษ ) เป็นจุดเดียวซึ่งเป็นลำดับที่สั้นที่สุดในขณะที่ Z (ความถี่น้อยที่สุด) คือสองขีดและเสียงบี๊บสองครั้ง

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

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

สุดท้ายนี้มีส่วนที่เหลืออยู่สองประเภทในรหัสมอร์ส ส่วนที่เหลือสั้น ๆ (ความยาวของจุด) ใช้เพื่อแยกความแตกต่างระหว่างจุดและขีดกลาง ช่องว่างที่ยาวขึ้น (ความยาวของเส้นประ) ใช้เพื่อคั่นอักขระ

แล้วสิ่งนี้ใช้กับปัญหาของเราอย่างไร?

การเข้ารหัส Huffman

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

ต้นไม้รหัส Huffman

ในต้นไม้ข้างต้นตัวอักษร E จะถูกเข้ารหัสเป็น 000 (หรือซ้ายซ้ายซ้าย) และ S เป็น 1011 มันควรจะเป็นที่ชัดเจนว่าการเข้ารหัสรูปแบบนี้เป็นที่ชัดเจน

นี่คือความแตกต่างที่สำคัญจากรหัสมอร์ส รหัสมอร์สมีตัวคั่นอักขระดังนั้นจึงสามารถทำการแทนที่ที่คลุมเครือได้ (เช่น 4 จุดสามารถเป็น H หรือ 2 Is) แต่เรามีเพียง 1s และ 0 เท่านั้นดังนั้นเราจึงเลือกการแทนที่ที่ไม่ชัดเจน

ด้านล่างนี้เป็นการใช้งานง่ายๆ:

private static class Node {
  private final Node left;
  private final Node right;
  private final String label;
  private final int weight;

  private Node(String label, int weight) {
    this.left = null;
    this.right = null;
    this.label = label;
    this.weight = weight;
  }

  public Node(Node left, Node right) {
    this.left = left;
    this.right = right;
    label = "";
    weight = left.weight + right.weight;
  }

  public boolean isLeaf() { return left == null && right == null; }

  public Node getLeft() { return left; }

  public Node getRight() { return right; }

  public String getLabel() { return label; }

  public int getWeight() { return weight; }
}

ด้วยข้อมูลคงที่:

private final static List<string> COLOURS;
private final static Map<string, integer> WEIGHTS;

static {
  List<string> list = new ArrayList<string>();
  list.add("White");
  list.add("Black");
  COLOURS = Collections.unmodifiableList(list);
  Map<string, integer> map = new HashMap<string, integer>();
  for (String colour : COLOURS) {
    map.put(colour + " " + "King", 1);
    map.put(colour + " " + "Queen";, 1);
    map.put(colour + " " + "Rook", 2);
    map.put(colour + " " + "Knight", 2);
    map.put(colour + " " + "Bishop";, 2);
    map.put(colour + " " + "Pawn", 8);
  }
  map.put("Empty", 32);
  WEIGHTS = Collections.unmodifiableMap(map);
}

และ:

private static class WeightComparator implements Comparator<node> {
  @Override
  public int compare(Node o1, Node o2) {
    if (o1.getWeight() == o2.getWeight()) {
      return 0;
    } else {
      return o1.getWeight() < o2.getWeight() ? -1 : 1;
    }
  }
}

private static class PathComparator implements Comparator<string> {
  @Override
  public int compare(String o1, String o2) {
    if (o1 == null) {
      return o2 == null ? 0 : -1;
    } else if (o2 == null) {
      return 1;
    } else {
      int length1 = o1.length();
      int length2 = o2.length();
      if (length1 == length2) {
        return o1.compareTo(o2);
      } else {
        return length1 < length2 ? -1 : 1;
      }
    }
  }
}

public static void main(String args[]) {
  PriorityQueue<node> queue = new PriorityQueue<node>(WEIGHTS.size(),
      new WeightComparator());
  for (Map.Entry<string, integer> entry : WEIGHTS.entrySet()) {
    queue.add(new Node(entry.getKey(), entry.getValue()));
  }
  while (queue.size() > 1) {
    Node first = queue.poll();
    Node second = queue.poll();
    queue.add(new Node(first, second));
  }
  Map<string, node> nodes = new TreeMap<string, node>(new PathComparator());
  addLeaves(nodes, queue.peek(), &quot;&quot;);
  for (Map.Entry<string, node> entry : nodes.entrySet()) {
    System.out.printf("%s %s%n", entry.getKey(), entry.getValue().getLabel());
  }
}

public static void addLeaves(Map<string, node> nodes, Node node, String prefix) {
  if (node != null) {
    addLeaves(nodes, node.getLeft(), prefix + "0");
    addLeaves(nodes, node.getRight(), prefix + "1");
    if (node.isLeaf()) {
      nodes.put(prefix, node);
    }
  }
}

ผลลัพธ์ที่เป็นไปได้อย่างหนึ่งคือ:

         White    Black
Empty          0 
Pawn       110      100
Rook     11111    11110
Knight   10110    10101
Bishop   10100    11100
Queen   111010   111011
King    101110   101111

สำหรับตำแหน่งเริ่มต้นจะเท่ากับ 32 x 1 + 16 x 3 + 12 x 5 + 4 x 6 = 164 บิต

ความแตกต่างของรัฐ

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

สิ่งที่คุณทำคือ XOR ตำแหน่งบอร์ดปัจจุบัน 256 บิตพร้อมตำแหน่งเริ่มต้น 256 บิตจากนั้นเข้ารหัส (โดยใช้การเข้ารหัส Huffman หรือวิธีการเข้ารหัสความยาวรัน ) เห็นได้ชัดว่าสิ่งนี้จะมีประสิทธิภาพมากในการเริ่มต้นด้วย (64 0s อาจจะตรงกับ 64 บิต) แต่ต้องเพิ่มพื้นที่เก็บข้อมูลเมื่อเกมดำเนินไป

ตำแหน่งชิ้น

ดังที่ได้กล่าวไปแล้วอีกวิธีหนึ่งในการโจมตีปัญหานี้คือแทนที่จะเก็บตำแหน่งของแต่ละชิ้นส่วนที่ผู้เล่นมี สิ่งนี้ใช้ได้ดีโดยเฉพาะกับตำแหน่ง endgame ที่ช่องสี่เหลี่ยมส่วนใหญ่จะว่างเปล่า (แต่ในวิธีการเข้ารหัส Huffman ช่องสี่เหลี่ยมว่างจะใช้เพียง 1 บิตเท่านั้น)

แต่ละด้านจะมีราชาและ 0-15 ชิ้นอื่น ๆ เนื่องจากการส่งเสริมการขายส่วนประกอบที่แน่นอนของชิ้นส่วนเหล่านั้นอาจแตกต่างกันมากพอที่คุณจะไม่สามารถคิดว่าตัวเลขตามตำแหน่งเริ่มต้นคือ maxima

วิธีที่เป็นตรรกะในการแบ่งสิ่งนี้คือการจัดเก็บตำแหน่งที่ประกอบด้วยสองด้าน (สีขาวและสีดำ) แต่ละด้านมี:

  • ราชา: 6 บิตสำหรับตำแหน่ง;
  • มีเบี้ย: 1 (ใช่), 0 (ไม่ใช่);
  • ถ้าใช่จำนวนเบี้ย: 3 บิต (0-7 + 1 = 1-8);
  • ถ้าใช่ตำแหน่งของจำนำแต่ละตัวจะถูกเข้ารหัส: 45 บิต (ดูด้านล่าง);
  • จำนวนที่ไม่ใช่เบี้ย: 4 บิต (0-15);
  • สำหรับแต่ละชิ้น: ประเภท (2 บิตสำหรับราชินี, โร้ก, อัศวิน, บิชอป) และตำแหน่ง (6 บิต)

สำหรับสถานที่รับจำนำนั้นเบี้ยจะอยู่ใน 48 เหลี่ยมที่เป็นไปได้เท่านั้น (ไม่ใช่ 64 เหมือนที่อื่น ๆ ) ดังนั้นจึงเป็นการดีกว่าที่จะไม่เสียค่า 16 พิเศษที่ใช้ 6 บิตต่อตัวจำนำ ดังนั้นหากคุณมี 8 เบี้ยมีความเป็นไปได้48 8เท่ากับ 28,179,280,429,056 คุณต้องใช้ 45 บิตในการเข้ารหัสค่าจำนวนมากนั้น

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

ควรจะชี้ให้เห็นว่ามีความเป็นไปได้น้อยกว่า 48 8 ตัวเนื่องจากเบี้ยไม่สามารถอยู่ในรูปสี่เหลี่ยมจัตุรัสเดียวกันทั้งหมดครั้งแรกมีความเป็นไปได้ 48 ความเป็นไปได้ 47 ที่สองและอื่น ๆ 48 x 47 x … x 41 = 1.52e13 = พื้นที่เก็บข้อมูล 44 บิต

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

แนวทางผสมผสาน

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

ด้วยค่าใช้จ่ายที่เล็กสิ่งนี้จะเป็นแนวทางที่ดีที่สุด

สถานะเกม

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

คำอธิบายประกอบ

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

  1. บับ 5 !! Nc4?

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

นอกจากนี้คุณอาจต้องจัดเก็บข้อความอิสระตามที่อธิบายการเคลื่อนไหว

ฉันสมมติว่าการเคลื่อนไหวเพียงพอแล้วจึงไม่มีคำอธิบายประกอบ

สัญกรณ์เกี่ยวกับพีชคณิต

เราสามารถจัดเก็บข้อความของการย้ายได้ที่นี่ (“ e4”,“ Bxb5” ฯลฯ ) การรวมไบต์การยกเลิกที่คุณกำลังดูอยู่ที่ประมาณ 6 ไบต์ (48 บิต) ต่อการเคลื่อนไหว (กรณีที่แย่ที่สุด) นั่นไม่ได้มีประสิทธิภาพโดยเฉพาะ

สิ่งที่สองที่ต้องลองคือการจัดเก็บตำแหน่งเริ่มต้น (6 บิต) และตำแหน่งสิ้นสุด (6 บิต) ดังนั้น 12 บิตต่อการเคลื่อนที่ ที่ดีกว่าอย่างเห็นได้ชัด

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

สรุป

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

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

ตำแหน่งหมากรุกนำมาเป็นหน้าจอจากหมากรุกตำแหน่งเทรนเนอร์


3
และ gzip ผลลัพธ์หลังจากนั้น (ถ้าส่วนหัวไม่เพิ่มผลลัพธ์); ^)
คางคก

คุณไม่จำเป็นต้องเพิ่มช่องว่างเป็นสองเท่าเพื่อระบุว่าเป็นสีดำหรือสีขาว?
Daniel Elliott

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

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

2
ฉันแปลกใจที่บทความของคุณไม่ได้พูดถึง [FEN] [1] ซึ่งจัดการกับปราสาทความพร้อมของผู้สัญจร ฯลฯ [1] en.wikipedia.org/wiki/FEN
Ross

48

วิธีที่ดีที่สุดคือจัดเก็บเกมหมากรุกในรูปแบบมาตรฐานที่มนุษย์อ่านได้

แบบพกพาเกมโน้ตถือว่าตำแหน่งเริ่มต้นมาตรฐาน (แม้ว่าจะไม่ได้มีการ ) และเพียงแค่แสดงรายการเคลื่อนไหว, เปิดโดยเปิด รูปแบบมาตรฐานที่กะทัดรัดและมนุษย์สามารถอ่านได้

เช่น

[Event "F/S Return Match"]
[Site "Belgrade, Serbia Yugoslavia|JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} 3... a6
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8  10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2

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


23
ในการป้องกันของฉันต่อ 2 downvotes ที่ได้รับ: 1) มันทำในสิ่งที่คุณต้องการ 2) ผ่านการทดสอบthedailywtf.com/articles/riddle-me-an-interview.aspx : "... คนบางคนที่สามารถแก้ปัญหาได้ ปริศนาเหล่านี้เป็นประเภทของคนที่คุณไม่ต้องการเป็นโปรแกรมเมอร์คุณต้องการทำงานร่วมกับคนที่สร้างเครื่องชั่ง / เรือบรรทุกน้ำหรือไม่นั่งแท็กซี่ 747 ไปที่ท่าเทียบเรือแล้วชั่งน้ำหนักเครื่องบินจัมโบ้โดยใช้สิ่งนั้น แทนที่จะเรียกโบอิ้งในตอนแรก? " คุณไม่จ้างคนที่คิดค้นการเข้ารหัสแบบสุ่มในการสัมภาษณ์เพราะพวกเขาจะทำในรหัสของพวกเขาเช่นกัน
Rob Grant

1
ถ้าฉันขอให้พวกเขาแก้ปัญหาโดยเฉพาะเพื่อให้ได้เทคนิคการแก้ปัญหาของพวกเขาคุณสามารถสมมติว่าฉันจะครอบคลุมคำถามอื่น ๆ ...
Andrew Rollings

7
@reinier: ฉันไม่ได้บอกว่าฉันไม่มีความรู้เลยเกี่ยวกับปัญหาความหนาแน่นของข้อมูล (คุณวินิจฉัยคำตอบของฉันผิดว่าเป็นการยอมรับการไร้ความสามารถ) แน่นอนว่าคุณต้องการจ้างคนที่เขียนโค้ดให้เป็นมาตรฐานการจัดเก็บข้อมูลที่มีอยู่และผู้ที่ตระหนักว่าการใช้เครื่องมือที่มีอยู่อย่างเหมาะสมแทนที่จะหมุนตัวเองอาจเป็นความคิดที่ดี - "เราคิดค้น The Wheel 2.0 ตอนนี้มันกลมกว่าเดิม!" แน่นอนคุณไม่ต้องการจ้างคนที่คิดว่า - แปลกประหลาด - ว่าการใช้ฟังก์ชันห้องสมุดเป็นสัญญาณของความอ่อนแอ
Rob Grant

18
นี่เป็นคำตอบแรกของฉันสำหรับคำถามนี้ในการสัมภาษณ์ คุณต้องการแสดงให้เห็นว่าสัญชาตญาณแรกของคุณคือการมองหาทางออกที่พร้อม หากผู้สัมภาษณ์บอกคุณว่าพวกเขาต้องการฟังสิ่งที่คุณคิดได้ด้วยตัวคุณเองคุณสามารถเข้าไปหาวิธีแก้ปัญหาเล็กน้อย
Bill the Lizard

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

15

ปริศนาที่ยิ่งใหญ่!

ฉันเห็นว่าคนส่วนใหญ่กำลังจัดเก็บตำแหน่งของแต่ละชิ้น วิธีการเกี่ยวกับการใช้วิธีการที่เรียบง่ายใจกว้างมากขึ้นและการจัดเก็บเนื้อหาของแต่ละตาราง ? ที่ดูแลการส่งเสริมและจับชิ้นส่วนโดยอัตโนมัติ

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

เมื่อพิจารณาถึงความถี่ของแต่ละชิ้นฉันสร้างต้นไม้ Huffmanบนกระดาษซึ่งฉันจะไม่ทำซ้ำที่นี่ ผลลัพธ์ที่cหมายถึงสี (ขาว = 0, ดำ = 1):

  • 0 สำหรับสี่เหลี่ยมว่าง
  • 1c0 สำหรับจำนำ
  • 1c100 สำหรับ rook
  • 1c101 สำหรับอัศวิน
  • 1c110 สำหรับบิชอป
  • 1c1110 สำหรับราชินี
  • 1c1111 สำหรับกษัตริย์

สำหรับคณะกรรมการทั้งหมดในสถานการณ์เริ่มต้นเรามี

  • สี่เหลี่ยมว่าง: 32 * 1 บิต = 32 บิต
  • เบี้ย: 16 * 3 บิต = 48 บิต
  • rooks / knights / Bishops: 12 * 5 bits = 60 bits
  • ราชินี / กษัตริย์: 4 * 6 บิต = 24 บิต

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

ฉันดูเฉพาะตำแหน่งของชิ้นส่วนบนกระดาน สถานะเพิ่มเติม (ซึ่งเทิร์นผู้ที่ร่ายเวทย์ en passant การเคลื่อนไหวซ้ำ ฯลฯ ) จะต้องเข้ารหัสแยกกัน อาจจะมากที่สุดอีก 16 บิตดังนั้น180 บิตสำหรับสถานะเกมทั้งหมด การเพิ่มประสิทธิภาพที่เป็นไปได้:

  • ทิ้งชิ้นส่วนที่ไม่บ่อยและจัดเก็บตำแหน่งแยกจากกัน แต่นั่นจะช่วยไม่ได้ ... การแทนที่ราชาและราชินีด้วยสี่เหลี่ยมว่างจะช่วยประหยัด 5 บิตซึ่งเป็น 5 บิตที่คุณต้องเข้ารหัสตำแหน่งของพวกมันด้วยวิธีอื่น
  • "ไม่มีเบี้ยที่แถวหลัง" สามารถเข้ารหัสได้อย่างง่ายดายโดยใช้ตาราง Huffman อื่นสำหรับแถวหลัง แต่ฉันสงสัยว่ามันช่วยได้มาก คุณอาจจะยังคงลงเอยด้วยต้น Huffman เหมือนเดิม
  • "หนึ่งบาทหลวงสีขาวหนึ่งสีดำ" สามารถเข้ารหัสได้โดยการนำสัญลักษณ์พิเศษที่ไม่มีcบิตซึ่งสามารถอนุมานได้จากสี่เหลี่ยมจัตุรัสที่อธิการอยู่ (เบี้ยที่เลื่อนตำแหน่งเป็นบิชอปขัดขวางโครงการนี้ ... )
  • การทำซ้ำของช่องสี่เหลี่ยมว่างสามารถเข้ารหัสแบบรันไทม์ได้โดยการนำสัญลักษณ์พิเศษสำหรับพูดว่า "สี่เหลี่ยมว่าง 2 ช่องในแถว" และ "สี่เหลี่ยมว่าง 4 ช่องในแถว" แต่มันไม่ใช่เรื่องง่ายที่จะประมาณความถี่ของสิ่งเหล่านี้และถ้าคุณทำผิดมันจะเจ็บมากกว่าช่วย

ไม่มีเบี้ยในอันดับธนาคารที่ช่วยประหยัดได้เลย - คุณสามารถตัดบิต # 3 ออกจากรูปแบบอื่น ๆ ทั้งหมดได้ ดังนั้นคุณจะประหยัดหนึ่งบิตต่อชิ้นจริงในอันดับของธนาคาร
Loren Pechtel

2
คุณสามารถสร้างต้นฮัฟฟ์แมนแยกกันสำหรับแต่ละสี่เหลี่ยมทั้ง 64 อันเนื่องจากบางอันน่าจะมีชิ้นส่วนบ่อยกว่าต้นอื่น ๆ
Claudiu

9

แนวทางตารางการค้นหาที่ใหญ่มาก

ตำแหน่ง - 18 ไบต์
จำนวนตำแหน่งทางกฎหมายโดยประมาณคือ 10 43
เพียงแค่ระบุตำแหน่งทั้งหมดและตำแหน่งสามารถจัดเก็บได้ใน 143 บิต ต้องใช้อีก 1 บิตเพื่อระบุว่าจะเล่นข้างไหนต่อไป

แน่นอนว่าการแจงนับไม่สามารถใช้ได้จริง แต่แสดงว่าต้องมีอย่างน้อย 144 บิต

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

เรายังมีพื้นที่เหลือเฟือสำหรับการเคลื่อนไหวพิเศษเช่น 0xFF เพื่อแสดงถึงการลาออก


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

1
ฉันพบลิงก์ที่น่าสนใจเกี่ยวกับระยะเวลาในการสร้างพจนานุกรมดังกล่าว :) ioannis.virtualcomposer2000.com/math/EveryChess.html
Andrew Rollings

Shannons ประมาณการว่าค่อนข้างล้าสมัย :-) เขาไม่ได้รวมทั้งการโปรโมตหรือการจับภาพซึ่งทำให้จำนวนเพิ่มขึ้นด้วยจำนวนที่ยุติธรรม ขอบเขตบนของ 5x10 ^ 52 มอบให้โดย Victor Allis 1994
Gunther Piez

แน่นอนด้วยการเข้ารหัสความยาวตัวแปรเท่านั้นค่าเฉลี่ยอย่างน้อย 10 ^ 43? การเข้ารหัสที่เอนเอียงไปยังตำแหน่งที่มากขึ้นจะต้องลดสิ่งนี้โดยเฉพาะอย่างยิ่งเนื่องจากหลายตำแหน่งเป็นไปไม่ได้
Phil H

ลิงก์ EveryChess ตอนนี้ "ขาย" ลิงก์ archive.org: web.archive.org/web/20120303094654/http://…
oPless

4

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

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

สำหรับตำแหน่งเริ่มต้นแนวทางของ ralu จะได้ผล เราสามารถปรับแต่งมันด้วยการเขียนโค้ดเลขคณิตที่นั่นได้เช่นกันถ้าเรามีวิธีถ่วงน้ำหนักตัวเลือกตามความน่าจะเป็นเช่นชิ้นส่วนมักจะปรากฏในการกำหนดค่าที่ปกป้องซึ่งกันและกันไม่ใช่สุ่ม ยากที่จะมองเห็นวิธีง่ายๆในการนำความรู้นั้นมาใช้ แนวคิดอย่างหนึ่ง: ถอยกลับไปที่การเข้ารหัสการย้ายข้างต้นแทนโดยเริ่มจากตำแหน่งเปิดมาตรฐานและค้นหาลำดับที่สิ้นสุดในบอร์ดที่ต้องการ (คุณอาจลองค้นหา A * ด้วยระยะฮิวริสติกที่เท่ากับผลรวมของระยะทางของชิ้นส่วนจากตำแหน่งสุดท้ายหรือบางอย่างตามแนวเส้นเหล่านั้น) ซึ่งเป็นการแลกเปลี่ยนความไร้ประสิทธิภาพจากการระบุลำดับการเคลื่อนไหวมากเกินไปเทียบกับประสิทธิภาพจากการใช้ประโยชน์จากการเล่นหมากรุก ความรู้.

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


ความสะดวกสบายในการจัดเก็บข้อมูลนี้ลงในกลุ่มคือ O (n) ตรวจสอบคำตอบที่แก้ไขของฉัน
Luka Rahne

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

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

4

โจมตีปัญหาย่อยของการเข้ารหัสขั้นตอนหลังจากเข้ารหัสตำแหน่งเริ่มต้นแล้ว แนวทางคือการสร้าง "รายการที่เชื่อมโยง" ของขั้นตอน

แต่ละขั้นตอนในเกมจะถูกเข้ารหัสเป็นคู่ "ตำแหน่งเก่า -> ตำแหน่งใหม่" คุณรู้ตำแหน่งเริ่มต้นในจุดเริ่มต้นของเกมหมากรุก โดยการข้ามรายการขั้นตอนที่เชื่อมโยงคุณจะไปที่สถานะได้หลังจาก X เคลื่อนที่

สำหรับการเข้ารหัสแต่ละขั้นตอนคุณต้องมีค่า 64 เพื่อเข้ารหัสตำแหน่งเริ่มต้น (6 บิตสำหรับ 64 ช่องบนกระดาน - 8x8 สี่เหลี่ยม) และ 6 บิตสำหรับตำแหน่งสิ้นสุด 16 บิตสำหรับการเคลื่อนไหว 1 ครั้งในแต่ละด้าน

จำนวนพื้นที่ที่เข้ารหัสเกมหนึ่ง ๆ จะต้องใช้ตามสัดส่วนของจำนวนการเคลื่อนไหว:

10 x (จำนวนการเคลื่อนไหวสีขาว + จำนวนการเคลื่อนไหวสีดำ) บิต

UPDATE: ภาวะแทรกซ้อนที่อาจเกิดขึ้นกับเบี้ยที่เลื่อนขั้น ต้องสามารถระบุสิ่งที่จำนำได้รับการเลื่อนตำแหน่ง - อาจต้องใช้บิตพิเศษ (จะใช้รหัสสีเทาเพื่อประหยัดพื้นที่เนื่องจากการส่งเสริมการจำนำหายากมาก)

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

Pawn: 
   - 2 options for movement (e2e3 or e2e4) + 2 options for taking = 4 options to encode
   - 12 options for promotions - 4 promotions (knight, biship, rook, queen) times 3 squares (because you can take a piece on the last row and promote the pawn at the same time)
   - Total of 16 options, 4 bits
Knight: 8 options, 3 bits
Bishop: 4 bits
Rook: 4 bits
King: 3 bits
Queen: 5 bits

ดังนั้นความซับซ้อนเชิงพื้นที่ต่อการเคลื่อนที่ของสีดำหรือสีขาวจะกลายเป็น

6 บิตสำหรับตำแหน่งเริ่มต้น + (จำนวนบิตที่แปรผันตามประเภทของสิ่งที่ย้าย)


เพิ่งอัปเดตฉันหมายถึงชุดค่าผสม 128 ชุด - น้อยกว่า 128 บิตอย่างชัดเจน :) :)
Alex Weinstein

1
สถานะของเกมไม่เหมือนกับการเคลื่อนไหว ตำแหน่งใด ๆ ที่กำหนดสามารถคิดได้จากจุดยอดหรือโหนดและการเคลื่อนไหวตามกฎหมายสามารถคิดได้จากขอบหรือลูกศรที่กำหนดทิศทางซึ่งก่อให้เกิดกราฟ (กำหนดทิศทางแบบ acyclic)
Shaggy Frog

ฉันไม่แน่ใจว่าทำไมถึงได้รับการโหวตเชิงลบ - ฉันชอบที่จะรับฟังความคิดเห็นของผู้คนเกี่ยวกับแนวคิดที่อัปเดตนี้
Alex Weinstein

1
สิ่งนี้ไม่ส่งผลกระทบต่อการให้เหตุผลของคุณ แต่เป็นการแก้ไขเล็กน้อย: เบี้ยสามารถมีสี่การเคลื่อนไหวโดยไม่รวมการส่งเสริมการขายหรือ 12 การเคลื่อนไหวรวมถึงการโปรโมต ตัวอย่างจำนำที่ e2: e3, e4, exd3, exf3 ตัวอย่างจำนำที่ e7: e8Q, e8N, e8R, e8B, exd8Q, exd8N, exd8R, exd8B, exf8Q, exf8N, exf8R, exf8B
อ. เร็กซ์

1
ปัญหาเล็กน้อย - 5 บิตเข้ารหัส 32 ค่าเท่านั้น ในการระบุสี่เหลี่ยมบนกระดานคุณต้องมี 6 บิต
Chris Dodd

4

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

วิธีแก้ปัญหาเบื้องต้น

สมมติว่าเป็นเกมหมากรุกมาตรฐานและคุณไม่ได้เข้ารหัสกฎ (เช่น White จะเป็นอันดับแรกเสมอ) คุณสามารถประหยัดได้มากโดยการเข้ารหัสเฉพาะการเคลื่อนไหวที่แต่ละชิ้นทำ

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

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

  • จำนำ: 4 ตัวเลือก2 บิต (เดินหน้า 1 ก้าวเดินหน้า 2 ก้าว 1 เส้นทแยงมุม)
  • Rook: 14 ตัวเลือก4 บิต (สูงสุด 7 ตัวในแต่ละทิศทาง)
  • บิชอป: 13 ตัวเลือก4 บิต (ถ้าคุณมี 7 ในเส้นทแยงมุมหนึ่งคุณจะมีเพียง 6 ตัวเท่านั้น)
  • อัศวิน: 8 ตัวเลือก3 บิต
  • Queen: 27 ตัวเลือก5 บิต (Rook + Bishop)
  • King: 9 ตัวเลือก4 บิต (8 ท่าขั้นตอนเดียวบวกตัวเลือก castling)

สำหรับโปรโมชั่นมี 4 ชิ้นให้เลือก (Rook, Bishop, Knight, Queen) ดังนั้นในการย้ายเราจะเพิ่ม2 บิตเพื่อระบุสิ่งนั้น ฉันคิดว่ากฎอื่น ๆ ทั้งหมดจะครอบคลุมโดยอัตโนมัติ (เช่น en passant)

การเพิ่มประสิทธิภาพเพิ่มเติม

ขั้นแรกหลังจากจับภาพสีเดียวได้ 8 ชิ้นคุณสามารถลดการเข้ารหัสชิ้นส่วนเป็น 3 บิตจากนั้น 2 บิตสำหรับ 4 ชิ้นและอื่น ๆ

การเพิ่มประสิทธิภาพหลักคือการระบุเฉพาะการเคลื่อนไหวที่เป็นไปได้ในแต่ละจุดในเกม สมมติว่าเราเก็บท่าของ Pawn {00, 01, 10, 11}ไว้ 1 ก้าวเดินหน้า 2 ก้าวทแยงซ้ายและทแยงขวาตามลำดับ หากการเคลื่อนไหวบางอย่างไม่สามารถทำได้เราสามารถลบออกจากการเข้ารหัสสำหรับเทิร์นนี้

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

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


4

ในแต่ละตำแหน่งจะได้รับจำนวนการเคลื่อนไหวที่เป็นไปได้ทั้งหมด

การย้ายครั้งต่อไปจะถูกสร้างขึ้นเป็น

index_current_move =n % num_of_moves //this is best space efficiency
n=n/num_of_moves

ประสิทธิภาพของพื้นที่ที่ดีที่สุดสำหรับการจัดเก็บเกมที่สร้างแบบสุ่มและต้องการประมาณ 5 บิต / เคลื่อนที่โดยเฉลี่ยเนื่องจากคุณมีการเคลื่อนไหวที่เป็นไปได้ 30-40 ครั้ง การประกอบหน่วยเก็บข้อมูลเป็นเพียงการสร้าง n ในลำดับย้อนกลับ

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

แก้ไข:

จุดในการบันทึกการเคลื่อนไหวคือการจัดเก็บเฉพาะดัชนีของการย้าย แทนที่จะเก็บ Kc1-c2 และพยายามลดข้อมูลนี้เราควรเพิ่มเฉพาะดัชนีของการเคลื่อนไหวที่สร้างขึ้นจากตัวสร้างการเคลื่อนที่ที่กำหนด (ตำแหน่ง)

ในแต่ละครั้งเราจะเพิ่มข้อมูลขนาด

num_of_moves = get_number_of_possible_moves(postion) ;

ในสระว่ายน้ำและไม่สามารถลดจำนวนนี้ได้

การสร้างกลุ่มข้อมูลคือ

n=n*num_of_moves+ index_current_move

พิเศษ

หากมีการเคลื่อนไหวเพียงครั้งเดียวในตำแหน่งสุดท้ายให้บันทึกเป็นจำนวนการเคลื่อนไหวที่บังคับก่อนหน้านี้ ตัวอย่าง: หากตำแหน่งเริ่มต้นมีการบังคับ 1 ครั้งสำหรับแต่ละด้าน (2 ท่า) และเราต้องการบันทึกเป็นเกมการเคลื่อนไหวเดียวให้เก็บ 1 ในพูล n

ตัวอย่างการจัดเก็บลงในกลุ่มข้อมูล

สมมติว่าเรารู้จักตำแหน่งเริ่มต้นและเราทำ 3 ท่า

ในการย้ายครั้งแรกมีการเคลื่อนไหวที่พร้อมใช้งาน 5 ครั้งและเราใช้ดัชนีการเคลื่อนที่ 4 ในการเคลื่อนไหวครั้งที่สองมีการเคลื่อนไหวที่พร้อมใช้งาน 6 ท่าและเราใช้ดัชนีตำแหน่ง 3 และในการย้ายครั้งที่ 3 มีการเคลื่อนไหว 7 ครั้งสำหรับฝั่งนั้นและเขาเลือกที่จะเลือกดัชนีการเคลื่อนที่ 2.

แบบเวกเตอร์; ดัชนี = [4,3,2] n_moves = [5,6,7]

เรากำลังเข้ารหัสข้อมูลนี้ย้อนหลังดังนั้น n = 4 + 5 * (3 + 6 * (2)) = 79 (ไม่จำเป็นต้องคูณด้วย 7)

จะคลายสิ่งนี้ได้อย่างไร? อันดับแรกเรามีตำแหน่งและพบว่ามี 5 ท่า ดังนั้น

index=79%5=4
n=79/5=15; //no remainder

เราใช้ดัชนีการเคลื่อนที่ 4 และตรวจสอบตำแหน่งอีกครั้งและจากจุดนี้เราพบว่ามี 6 ท่าที่เป็นไปได้

index=15%6=3
n=15/6=2

และเราใช้ดัชนีการเคลื่อนที่ 3 ซึ่งทำให้เราอยู่ในตำแหน่งที่มี 7 ท่าที่เป็นไปได้

index=2%7=2
n=2/7=0

เราทำดัชนีการเคลื่อนไหวล่าสุด 2 และเราไปถึงตำแหน่งสุดท้าย

ดังที่คุณเห็นความซับซ้อนของเวลาคือ O (n) ความซับซ้อนของปริภูมิ ansd คือ O (n) แก้ไข: ความซับซ้อนของเวลาเป็น O (n ^ 2) เนื่องจากจำนวนที่คุณคูณเพิ่มขึ้น แต่ไม่น่าจะมีปัญหาในการจัดเก็บเกมได้ถึง 10,000 ครั้ง


ตำแหน่งประหยัด

สามารถทำได้ใกล้เคียงกับที่เหมาะสมที่สุด

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

เราต้องประหยัดอะไรบ้าง: 1. ตำแหน่งของความสงบแต่ละครั้ง 2. ตำแหน่งของการหล่อ 3. ความเป็นไปได้ของผู้สัญจรไปมา 4. ด้านที่มีการเคลื่อนย้ายได้

สมมติว่าทุกชิ้นสามารถยืนได้ทุกที่ แต่ไม่ใช่ 2 ชิ้นในที่เดียวกัน จำนวนวิธี 8 เบี้ยที่มีสีเดียวกันสามารถจัดเรียงบนกระดานคือ C (64/8) (ทวินาม) ซึ่งมีขนาด 32 บิตจากนั้น 2 รอก 2R-> C (56/2) 2B -> C (54/2) , 2N-> C (52/2), 1Q-> C (50/1), 1K -> C (49/1) และเหมือนกันสำหรับไซต์อื่น แต่เริ่มต้นด้วย 8P -> C (48/8) เป็นต้น .

การคูณสิ่งนี้เข้าด้วยกันสำหรับทั้งสองไซต์ทำให้เราได้รับหมายเลข 4634726695587809641192045982323285670400000 ซึ่งมีขนาดประมาณ 142 บิตเราต้องเพิ่ม 8 สำหรับ en-passant หนึ่งที่เป็นไปได้ (en-passant pawn สามารถอยู่ในหนึ่งใน 8 ตำแหน่ง), 16 (4 บิต) สำหรับข้อ จำกัด ในการหล่อและ หนึ่งบิตสำหรับไซต์ที่มีการเคลื่อนไหว เราลงท้ายด้วย 142 + 3 + 4 + 1 = 150bits

แต่ตอนนี้เรามาตามล่าหาความซ้ำซ้อนบนกระดานด้วย 32 ชิ้นและไม่มีการสละ

  1. เบี้ยสีดำและสีขาวทั้งสองอยู่บนเสาเดียวกันและหันหน้าเข้าหากัน เบี้ยแต่ละตัวจะหันหน้าไปทางเบี้ยอื่นนั่นหมายความว่าเบี้ยสีขาวสามารถอยู่ได้มากที่สุดในอันดับที่ 6 สิ่งนี้ทำให้เราได้ 8 * C (6/2) แทน C (64/8) * C (48/8) ซึ่งลดข้อมูลลง 56 บิต

  2. ความเป็นไปได้ในการ castling ก็ซ้ำซ้อนเช่นกัน หาก rooks ไม่ได้อยู่ในจุดเริ่มต้นก็จะไม่มีความเป็นไปได้ในการเหวี่ยงแหอีกต่อไป ดังนั้นเราจึงสามารถจินตนาการเพิ่ม 4 ช่องสี่เหลี่ยมบนกระดานเพื่อรับข้อมูลเพิ่มเติมหากการโยนหินนี้เป็นไปได้และลบ 4 บิตการร่ายออก ดังนั้นแทนที่จะเป็น C (56/2) * C (40/2) * 16 เรามี C (58/2) * C (42/2) และเราเสีย 3.76 บิต (เกือบทั้งหมด 4 บิต)

  3. en-passant: เมื่อเราเก็บหนึ่งใน 8 ตัวที่เป็นไปได้ของ passant เราจะรู้ตำแหน่งของเบี้ยสีดำและลดความซ้ำซ้อนในการให้ข้อมูล (ถ้าเป็นการย้ายสีขาวและมีตัวเบี้ยตัวที่ 3 ซึ่งหมายความว่าจำนำสีดำอยู่บน c5 และเบี้ยสีขาวก็เช่นกัน c2, c3 หรือ c4) ดังนั้นใน C (6/2) เรามี 3 และเราเสีย 2.3 บิต เราจะลดความซ้ำซ้อนบางส่วนหากเราเก็บหมายเลข en-passant ไว้ด้านข้างซึ่งสามารถทำได้ (3 ความเป็นไปได้ -> ซ้าย, ขวา, ทั้งสองอย่าง) และเรารู้ถึงความเป็นไปได้ของเบี้ยที่สามารถนำติดตัว (ตัวอย่างเช่นจากตัวอย่างเช่นสีดำทั่วไปใน c5 สิ่งที่สามารถอยู่ในซ้ายขวาหรือทั้งสองอย่างถ้าอยู่ในไซต์เดียวเรามี 2 * 3 (3 สำหรับการจัดเก็บ psissibilites และ 2 การเคลื่อนไหวที่เป็นไปได้สำหรับจำนำสีดำในอันดับที่ 7 หรือ 6 ) ใส่ C (6/2) และเราลดลง 1.3 บิตและถ้าทั้งสองด้านเราลดลง 4.2 บิตด้วยวิธีนี้เราสามารถลดได้ 2.3 + 1.3 = 3

  4. Bishops: Bisops สามารถอยู่ในช่องสี่เหลี่ยม opostite เท่านั้นซึ่งจะช่วยลดความซ้ำซ้อนลง 1 บิตสำหรับแต่ละไซต์

ถ้าเราสรุปเราต้อง 150-56-4-3.6-2 = 85 บิตเพื่อเก็บตำแหน่งหมากรุกหากไม่มีการลาก

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


แนวทางที่น่าสนใจ เพิ่มรายละเอียดเพิ่มเติม :)
Andrew Rollings

ฉันเพิ่มช่องว่างสำหรับตำแหน่งการบันทึกด้วย ฉันลงไปถึง 85 บิตในตำแหน่งที่ไม่ต้องทำอะไรและเป็นความรู้สึกที่ดีว่ามันไปได้ไกลแค่ไหน ฉันคิดว่าความคิดที่ดีที่สุดคือการประหยัดความเป็นไปได้ในการคาสติ้งโดยที่ 4 บิตเกือบทั้งหมดซ้ำซ้อน
Luka Rahne

3

คนส่วนใหญ่ได้เข้ารหัสสถานะของบอร์ด แต่เกี่ยวกับการเคลื่อนไหวของตัวเอง .. นี่คือคำอธิบายการเข้ารหัสบิต

บิตต่อชิ้น:

  • Piece-ID:สูงสุด 4 บิตเพื่อระบุ 16 ชิ้นต่อด้าน สีขาว / ดำสามารถอนุมานได้ มีคำสั่งที่กำหนดไว้ในชิ้นส่วน เมื่อจำนวนชิ้นลดลงต่ำกว่ากำลังสองตามลำดับให้ใช้บิตน้อยลงเพื่ออธิบายชิ้นส่วนที่เหลือ
  • Pawn: 3 ความเป็นไปได้ในการเคลื่อนที่ครั้งแรกดังนั้น +2 บิต (ไปข้างหน้าทีละหนึ่งหรือสองช่องทางและทางเดิน) การเคลื่อนไหวครั้งต่อ ๆ ไปไม่อนุญาตให้ก้าวไปข้างหน้าทีละสองดังนั้น +1 บิตจึงเพียงพอ การส่งเสริมการขายสามารถสรุปได้ในกระบวนการถอดรหัสโดยสังเกตว่าเมื่อเบี้ยเข้าสู่อันดับสุดท้าย หากทราบว่าจำนำได้รับการเลื่อนขั้นเครื่องถอดรหัสจะคาดหวังอีก 2 บิตเพื่อระบุว่าชิ้นส่วนใดใน 4 ชิ้นหลักที่ได้รับการเลื่อนขั้น
  • Bishop: +1 บิตสำหรับเส้นทแยงมุมที่ใช้สูงสุด +4 บิตสำหรับระยะทางตามแนวทแยงมุม (ความเป็นไปได้ 16) ตัวถอดรหัสสามารถอนุมานระยะทางสูงสุดที่ชิ้นส่วนสามารถเคลื่อนที่ไปตามแนวทแยงนั้นได้ดังนั้นหากเป็นเส้นทแยงมุมที่สั้นกว่าให้ใช้บิตน้อยลง
  • อัศวิน: 8 ท่าที่เป็นไปได้ +3 บิต
  • Rook: +1 บิตสำหรับแนวนอน / แนวตั้ง, +4 บิตสำหรับระยะทางตามเส้น
  • King: 8 ท่าที่เป็นไปได้ +3 บิต ระบุการร่ายด้วยการเคลื่อนไหวที่ 'เป็นไปไม่ได้' - เนื่องจากการร่ายจะทำได้เฉพาะในขณะที่กษัตริย์อยู่ในอันดับที่หนึ่งให้เข้ารหัสการเคลื่อนไหวนี้ด้วยคำสั่งให้ย้ายกษัตริย์ 'ถอยหลัง' นั่นคือออกจากกระดาน
  • ราชินี: 8 ทิศทางที่เป็นไปได้ + 3 บิต เพิ่มขึ้นถึง +4 บิตสำหรับระยะทางตามแนวเส้น / เส้นทแยงมุม (น้อยกว่าหากเส้นทแยงมุมสั้นกว่าเช่นในกรณีของอธิการ)

สมมติว่าชิ้นส่วนทั้งหมดอยู่บนกระดานนี่คือบิตต่อการเคลื่อนไหว: Pawn - 6 บิตในการเคลื่อนที่ครั้งแรก 5 ครั้งต่อมา 7 หากได้รับการเลื่อนตำแหน่ง Bishop: 9 bits (สูงสุด), Knight: 7, Rook: 9, King: 7, Queen: 11 (สูงสุด)


32 บิตเพื่อระบุชิ้นส่วน ??? ฉันคิดว่าคุณหมายถึง 5 (32 ชิ้น) หรือ 6 ถ้าคุณต้องการเข้ารหัสสถานะ 'สิ้นสุด'
Toad

เบี้ยยังสามารถเลื่อนขั้นเป็นโร๊คอัศวินหรือบิชอปได้ เป็นเรื่องปกติที่จะหลีกเลี่ยงทางตันหรือหลีกเลี่ยงการเผชิญหน้า
Kobi

สิ่งนี้ไม่ส่งผลกระทบต่อการให้เหตุผลของคุณ แต่เป็นการแก้ไขเล็กน้อย: เบี้ยสามารถมีสี่การเคลื่อนไหวโดยไม่รวมการส่งเสริมการขายหรือ 12 การเคลื่อนไหวรวมถึงการโปรโมต ตัวอย่างจำนำที่ e2: e3, e4, exd3, exf3 ตัวอย่างจำนำที่ e7: e8Q, e8N, e8R, e8B, exd8Q, exd8N, exd8R, exd8B, exf8Q, exf8N, exf8R, exf8B
อ. เร็กซ์

บางทีฉันอาจจะอ่านผิด แต่โรงรับจำนำไม่สามารถเดินผ่านไปได้ในตอนแรก จริงๆแล้วคุณไม่จำเป็นต้องมีสัญกรณ์ "en passant" แบบพิเศษเนื่องจากอยู่ในกฎของเกม - มันจะเป็นการเคลื่อนที่ในแนวทแยง การย้ายครั้งแรกเป็นหนึ่งใน 4 ตัวเลือกและการเคลื่อนไหวครั้งต่อ ๆ ไปมีถึง 3 ตัวเลือก
แพะไม่พอใจ

3

ปัญหาในการเข้ารหัสที่มีประสิทธิภาพสูงสุดสำหรับเกมหมากรุกทั่วไปหรือเกมที่มีการเข้ารหัสกรณีที่สั้นที่สุดคืออะไร?

สำหรับวิธีหลังวิธีที่มีประสิทธิภาพที่สุดก็เป็นวิธีที่ทึบที่สุดเช่นกัน: สร้างการแจงนับของคู่ที่เป็นไปได้ทั้งหมด (กระดานเริ่มต้นลำดับการเคลื่อนไหวตามกฎหมาย) ซึ่งมีการลากเส้นต่อสามตำแหน่งซ้ำและไม่เกิน -fifty-move นับตั้งแต่กฎการจำนำ - การย้ายหรือการจับครั้งสุดท้ายเป็นแบบวนซ้ำ จากนั้นดัชนีของตำแหน่งในลำดับ จำกัด นี้จะให้การเข้ารหัสกรณีที่สั้นที่สุดที่สั้นที่สุด แต่ยังและการเข้ารหัสที่ยาวเท่า ๆ กันสำหรับกรณีทั่วไปและฉันคาดว่าจะมีราคาแพงมากในการคำนวณ เกมหมากรุกที่ยาวที่สุดเท่าที่จะเป็นไปได้ควรจะมีมากกว่า 5,000 ท่าโดยปกติแล้วจะมีการเคลื่อนไหว 20-30 ครั้งในแต่ละตำแหน่งสำหรับผู้เล่นแต่ละคน (แม้ว่าจะน้อยลงเมื่อมีชิ้นส่วนเหลืออยู่ไม่กี่ชิ้น) - สิ่งนี้จะให้บางสิ่งเช่น 40000 บิตที่จำเป็นสำหรับการเข้ารหัสนี้

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

  1. 64 บิตเพื่อแสดงว่าสี่เหลี่ยมใดถูกครอบครอง (เมทริกซ์การครอบครอง) รวมทั้งรายการว่าชิ้นส่วนใดอยู่ในแต่ละตารางที่ถูกครอบครอง (สามารถมี 3 บิตสำหรับเบี้ยและ 4 บิตสำหรับชิ้นอื่น ๆ ): ให้ 190 บิตสำหรับตำแหน่งเริ่มต้น เนื่องจากมีชิ้นส่วนบนบอร์ดไม่เกิน 32 ชิ้นการเข้ารหัสของเมทริกซ์การเข้าพักจึงซ้ำซ้อนและบางอย่างเช่นตำแหน่งบอร์ดทั่วไปสามารถเข้ารหัสได้เช่น 33 บิตชุดบวกดัชนีของบอร์ดจากรายการบอร์ดทั่วไป

  2. 1 บิตเพื่อบอกว่าใครเคลื่อนไหวก่อน

  3. การย้ายโค้ดตามคำแนะนำของเฮงค์: โดยทั่วไป 10 บิตต่อการเคลื่อนไหวสีขาว / ดำแม้ว่าการเคลื่อนไหวบางอย่างจะใช้ 0 บิต แต่เมื่อผู้เล่นไม่มีการเคลื่อนไหวอื่น

สิ่งนี้แนะนำ 490 บิตในการเขียนโค้ดเกม 30-move ทั่วไปและจะเป็นตัวแทนที่มีประสิทธิภาพพอสมควรสำหรับเกมทั่วไป

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


สมมติว่าฉันเลือกเกมจำนวนมากและหาผลการแข่งขันโดยเฉลี่ย
Andrew Rollings

3

ตำแหน่งบนบอร์ดสามารถกำหนดได้ 7 บิต (0-63 และ 1 ค่าระบุว่าไม่ได้อยู่บนบอร์ดอีกต่อไป) ดังนั้นสำหรับทุกชิ้นบนกระดานให้ระบุตำแหน่งที่อยู่

32 ชิ้น * 7 บิต = 224 บิต

แก้ไข: ตามที่ Cadrian ชี้ให้เห็น ... นอกจากนี้เรายังมีคดี 'เลื่อนขั้นจำนำเป็นราชินี' ฉันขอแนะนำให้เราเพิ่มบิตพิเศษในตอนท้ายเพื่อระบุว่าจำนำใดได้รับการเลื่อนขั้น

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

เคสที่น้อยที่สุด (ไม่มีโปรโมชั่น) คือ 224 บิต + 5 (ไม่มีโปรโมชั่น) สำหรับทุกเบี้ยที่เลื่อนขั้นให้เพิ่ม 5 บิต

แก้ไข: ในขณะที่กบขนดกชี้ให้เห็นเราต้องการอีกเล็กน้อยในตอนท้ายเพื่อระบุว่าตาของใคร; ^)


และ gzip ผลลัพธ์หลังจากนั้น (หากส่วนหัวไม่เพิ่มผลลัพธ์); ^)
คางคก

คุณสามารถปรับปรุงได้หรือไม่โดยคำนึงว่าบางชิ้นจะไม่พบในสี่เหลี่ยมจัตุรัสบางสี?
Andrew Rollings

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

ฉันเห็นว่าบาทหลวงขาวดำสามารถกำหนดร่วมกันได้อย่างไร ฉันสงสัยเกี่ยวกับอัศวินว่า ..
int3

1
คุณพลาดการโปรโมตที่ไม่ใช่ราชินี
Loren Pechtel

2

ฉันจะใช้การเข้ารหัสความยาวรัน บางชิ้นไม่ซ้ำกัน (หรือมีเพียงสองครั้ง) ดังนั้นฉันจึงสามารถละเว้นความยาวหลังจากนั้นได้ เช่นเดียวกับ cletus ฉันต้องการสถานะที่ไม่ซ้ำกัน 13 สถานะดังนั้นฉันจึงสามารถใช้ nibble (4 บิต) เพื่อเข้ารหัสชิ้นส่วนได้ บอร์ดเริ่มต้นจะมีลักษณะดังนี้:

White Rook, W. Knight, W. Bishop, W. Queen, W. King, W. Bishop, W. Knight, W. Rook,
W. Pawn, 8,
Empty, 16, Empty, 16
B. Pawn, 8,
B. Rook, B. Knight, B. Bishop, B. Queen, B. King, B. Bishop, B. Knight, B. Rook

ซึ่งทำให้ฉันมี 8 + 2 + 4 + 2 + 8 nibbles = 24 nibbles = 96 bits ฉันไม่สามารถเข้ารหัส 16 ด้วยการแทะ แต่เนื่องจาก "ว่างเปล่า 0" ไม่สมเหตุสมผลฉันจึงถือว่า "0" เป็น "16" ได้

หากกระดานว่างเปล่า แต่สำหรับเบี้ยตัวเดียวที่มุมซ้ายบนฉันจะได้รับ "เบี้ย 1, ว่าง, 16, ว่าง, 16, ว่าง 16, ว่างเปล่า, 15" = 10 เม็ด = 40 บิต

กรณีที่แย่ที่สุดคือเมื่อฉันมีช่องว่างระหว่างแต่ละชิ้น แต่สำหรับการเข้ารหัสชิ้นส่วนนั้นฉันต้องการเพียง 13 จาก 16 ค่าดังนั้นฉันอาจใช้อีกค่าหนึ่งเพื่อพูดว่า "Empty1" ก็ได้ จากนั้นฉันต้องการ 64 nibbles == 128bits

สำหรับการเคลื่อนไหวฉันต้องการ 3 บิตสำหรับชิ้นส่วน (สีจะได้รับจากการที่สีขาวเคลื่อนที่ก่อนเสมอ) บวก 5 บิต (0..63) สำหรับตำแหน่งใหม่ = หนึ่งไบต์ต่อการเคลื่อนที่ ส่วนใหญ่แล้วฉันไม่ต้องการตำแหน่งเก่าเพราะมีเพียงชิ้นเดียวเท่านั้นที่จะอยู่ในระยะ สำหรับกรณีที่แปลกฉันต้องใช้รหัสเดียวที่ไม่ได้ใช้ (ฉันต้องการรหัส 7 ตัวเพื่อเข้ารหัสชิ้นส่วน) จากนั้น 5 บิตสำหรับเก่าและ 5 บิตสำหรับตำแหน่งใหม่

สิ่งนี้ทำให้ฉันสามารถเข้ารหัส castling ใน 13 bites (ฉันสามารถย้าย King ไปยัง Rook ซึ่งเพียงพอที่จะพูดในสิ่งที่ฉันตั้งใจ)

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

[EDIT2] ซึ่งออกจากการเปลี่ยนแปลงของจำนำ ถ้าจำนำมาถึงแถวสุดท้ายฉันสามารถย้ายไปแทนที่เพื่อพูดว่า "แปลงร่าง" จากนั้นเพิ่ม 3 บิตสำหรับชิ้นส่วนที่ถูกแทนที่ด้วย (คุณไม่ต้องใช้ราชินีคุณสามารถแทนที่เบี้ยด้วยอะไรก็ได้ แต่พระมหากษัตริย์).


ตัวเข้ารหัสอัจฉริยะไม่สามารถสันนิษฐานได้ว่าเป็นเกมทั้งหมด มันอาจเป็นส่วนหนึ่งของเกม ฉันคิดว่าคุณยังต้องเข้ารหัสตำแหน่งเริ่มต้น
Andrew Rollings

ในกรณีที่เลวร้ายที่สุดฉันต้องการ 128 บิตหรือถ้าเกมยังอยู่ในช่วงเริ่มต้นฉันสามารถใช้การเคลื่อนไหวได้ถึง 15 ครั้งเพื่อนำมันเข้าสู่ตำแหน่งเริ่มต้น = 120 บิต
Aaron Digulla

เนื่องจากสถานะใด ๆ ต้องเข้ารหัสไม่ใช่แค่สถานะบอร์ดเริ่มต้นเท่านั้นคุณยังต้องเข้ารหัสชิ้นส่วนนั้นด้วย ดังนั้นคุณจะต้องต่อชิ้นอย่างน้อย 5 บิต ดังนั้นนี่จะให้คุณเพิ่มอย่างน้อย 32 * 5 บิต
Toad

@reiner: คุณคิดผิด ฉันต้องการแค่สี่บิตต่อชิ้น / สี่เหลี่ยมเปล่า และฉันได้กล่าวถึงสิ่งนี้แล้วในส่วนแรกของคำตอบดังนั้นจึงไม่มี "32 * 5 บิตพิเศษ" สำหรับสถานะเริ่มต้นฉันต้องการ 96 บิตและสำหรับสถานะเริ่มต้นอื่น ๆ ฉันต้องการไม่เกิน 128 บิต
Aaron Digulla

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

2

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

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

การเข้ารหัสที่คล้ายกันใช้สำหรับรถไฟขัดขวางในระบบประสาทคอมพิวเตอร์ (AER)

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


มันอาจเป็นเพียงส่วนหนึ่งของเกม คุณไม่สามารถคาดเดาการเคลื่อนไหวของสีขาวได้
Andrew Rollings

2

มีตำแหน่งบอร์ดที่เป็นไปได้ 64 ตำแหน่งดังนั้นคุณต้องมี 6 บิตต่อตำแหน่ง มีชิ้นส่วนเริ่มต้น 32 ชิ้นดังนั้นเราจึงมีทั้งหมด 192 บิตโดยที่ทุกๆ 6 บิตจะระบุตำแหน่งของชิ้นส่วนที่กำหนด เราสามารถกำหนดลำดับของชิ้นส่วนที่ปรากฏล่วงหน้าได้ดังนั้นเราจึงไม่ต้องบอกว่าชิ้นไหนเป็นชิ้นไหน

จะเกิดอะไรขึ้นถ้าชิ้นส่วนหลุดจากกระดาน? เราสามารถวางชิ้นส่วนในจุดเดียวกันกับอีกชิ้นหนึ่งเพื่อระบุว่ามันหลุดจากกระดานเนื่องจากจะผิดกฎหมาย แต่เราก็ไม่รู้เหมือนกันว่าชิ้นแรกจะอยู่บนกระดานหรือไม่ ดังนั้นเราจึงเพิ่ม 5 บิตเพื่อระบุว่าชิ้นส่วนใดเป็นชิ้นแรก (ความเป็นไปได้ 32 บิต = 5 บิตเพื่อแทนชิ้นส่วนแรก) จากนั้นเราสามารถใช้จุดนั้นสำหรับชิ้นต่อไปที่หลุดจากกระดาน นั่นทำให้เราได้ทั้งหมด 197 บิต ต้องมีอย่างน้อยหนึ่งชิ้นบนกระดานจึงจะใช้งานได้

จากนั้นเราต้องการบิตหนึ่งที่ถึงคราวของใคร - นำเราไปสู่198 บิตบิต

โปรโมชั่นจำนำล่ะ? เราสามารถทำมันในทางที่ไม่ดีได้โดยการเพิ่ม 3 บิตต่อตัวจำนำและเพิ่มเป็น 42 บิต แต่เราจะสังเกตได้ว่าส่วนใหญ่เบี้ยไม่ได้รับการเลื่อนตำแหน่ง

ดังนั้นสำหรับเบี้ยทุกตัวที่อยู่บนกระดานบิต '0' แสดงว่าไม่ได้รับการเลื่อนขั้น หากเบี้ยไม่ได้อยู่บนกระดานเราก็ไม่ต้องการเงินเลย จากนั้นเราสามารถใช้สตริงบิตความยาวผันแปรที่เขามีโปรโมชั่น ส่วนใหญ่มักจะเป็นราชินีดังนั้น "10" จึงหมายถึงราชินี จากนั้น "110" หมายถึง rook ส่วน "1110" หมายถึงบิชอปและ "1111" หมายถึงอัศวิน

สถานะเริ่มต้นจะใช้เวลา 198 + 16 = 214 บิตเนื่องจากเบี้ยทั้ง 16 ตัวอยู่บนกระดานและไม่ได้รับการส่งเสริม ตอนท้ายเกมที่มีตัวจำนำที่ได้รับการเลื่อนตำแหน่งสองตัวอาจใช้เวลาบางอย่างเช่น 198 + 4 + 4 ซึ่งหมายถึง 4 เบี้ยที่มีชีวิตอยู่และไม่ได้รับการเลื่อนขั้นและเบี้ยราชินี 2 ตัวสำหรับ206 บิตรวม ดูเหมือนจะค่อนข้างแข็งแกร่ง!

===

การเข้ารหัส Huffman ตามที่คนอื่น ๆ ชี้ให้เห็นจะเป็นขั้นตอนต่อไป หากคุณสังเกตเกมสองสามล้านเกมคุณจะสังเกตเห็นว่าแต่ละชิ้นมีแนวโน้มที่จะอยู่ในช่องสี่เหลี่ยม ตัวอย่างเช่นส่วนใหญ่เบี้ยจะอยู่ในแนวตรงหรือไปทางซ้ายหรือทางขวา กษัตริย์มักจะเกาะอยู่รอบ ๆ ฐานบ้าน

ดังนั้นจึงคิดรูปแบบการเข้ารหัส Huffman สำหรับแต่ละตำแหน่งแยกกัน เบี้ยจะใช้เวลาเฉลี่ยเพียง 3-4 บิตแทนที่จะเป็น 6 ราชาก็ควรใช้บิตเช่นกัน

นอกจากนี้ในโครงการนี้ให้ใส่ "ถ่าย" เป็นตำแหน่งที่เป็นไปได้ สิ่งนี้สามารถจัดการกับ castling ได้อย่างแข็งแกร่งเช่นกัน - rook และ king แต่ละตัวจะมีสถานะ "ตำแหน่งเดิม, ย้าย" เพิ่มเติม คุณยังสามารถเข้ารหัส en passant ในเบี้ยด้วยวิธีนี้ - "ตำแหน่งเดิมสามารถ en passant"

ด้วยข้อมูลที่เพียงพอแนวทางนี้ควรให้ผลลัพธ์ที่ดีจริงๆ


2
เพียงกำหนดชิ้นส่วนที่ถอดออกให้เป็นสี่เหลี่ยมจัตุรัสเดียวกับราชา เนื่องจากกษัตริย์ไม่สามารถลบออกได้จึงไม่คลุมเครือ
John La Rooy

นั่นเป็นความคิดเห็นที่ดี :) แง่มุมที่ดีในการแก้ปัญหานี้ด้วย ฉันไม่รู้เลยว่าการเลือกผู้ชนะจะเป็นเรื่องยากมาก
Andrew Rollings

2

ฉันจะลองใช้การเข้ารหัส Huffmanการเข้ารหัสทฤษฎีที่อยู่เบื้องหลังสิ่งนี้คือ - ในเกมหมากรุกทุกเกมจะมีบางส่วนที่เคลื่อนไหวได้มากและบางส่วนที่ไม่เคลื่อนไหวมากหรือถูกกำจัดไปก่อน หากตำแหน่งเริ่มต้นมีชิ้นส่วนบางชิ้นหลุดออกไปแล้วยิ่งดี เช่นเดียวกับสแควร์ส - บางสแควร์สสามารถเห็นการกระทำทั้งหมดในขณะที่บางสแควร์สไม่ได้สัมผัสมากนัก

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

ทุกชิ้นจะมีรหัสที่กำหนด เนื่องจากมี 32 ชิ้นที่แตกต่างกันฉันจึงต้องการเพียง 5 บิตสำหรับ ID ชิ้น รหัสชิ้นส่วนจะไม่เปลี่ยนจากเกมเป็นเกม เช่นเดียวกันกับรหัสสี่เหลี่ยมซึ่งฉันต้องการ 6 บิต

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

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

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

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

สรุปได้ในรูปแบบกราฟิก:

<Game> := <Pieces huffman tree> <squares huffman tree> <promotion table> <initial position> (<moves> | <1 bit for next move - see Added 2 below>)

<Pieces huffman tree> := <pieces entry 1> <pieces entry 2> ... <pieces entry N>
<pieces entry> := "0" | "1" <5 bits with piece ID>

<squares huffman tree> := <squares entry 1> <squares entry 2> ... <squares entry N>
<Squares entry> := "0" | "1" <6 bits with square ID>

<promotion table> := <4 bits with count of promotions> <promotion 1> <promotion 2> ... <promotion N>
<promotion> := <huffman-encoded piece ID> <2 bits with what it becomes>

<initial position> := <position entry 1> <position entry 2> ... <position entry N>
<moves> := <position entry 1> <position entry 2> ... <position entry N>
<position entry> := <huffman-encoded piece ID> <huffman-encoded squre ID> (<2 bits specifying the upgrade - optional>)

เพิ่ม:สิ่งนี้ยังสามารถปรับให้เหมาะสมได้ ทุกชิ้นมีการเคลื่อนไหวทางกฎหมายเพียงเล็กน้อยเท่านั้น แทนที่จะเข้ารหัสตารางเป้าหมายเพียงอย่างเดียวอาจให้รหัสที่ใช้ 0 สำหรับการเคลื่อนไหวที่เป็นไปได้ของทุกชิ้น ID เดียวกันจะถูกนำมาใช้ซ้ำสำหรับทุกชิ้นดังนั้นโดยรวมแล้วจะมี ID ที่แตกต่างกันไม่เกิน 21 ID (ราชินีสามารถมีตัวเลือกการย้ายที่แตกต่างกันได้มากที่สุด 21 ตัวเลือก) ใส่สิ่งนี้ในตาราง huffman แทนฟิลด์

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

หรือสามารถวางโดยใช้รหัสสี่เหลี่ยม 6 บิตที่ไม่มีการบีบอัด

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

เพิ่ม 2:อีกหนึ่งกรณีพิเศษ หากสถานะของเกมไม่มีการเคลื่อนไหวสิ่งสำคัญคือต้องแยกแยะว่าใครเคลื่อนไหวต่อไป เพิ่มอีกหนึ่งบิตในตอนท้ายสำหรับสิ่งนั้น :)


2

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

ลองเรียกสถานะบอร์ดเริ่มต้นว่าบิตเดียว "0"

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

มีการเปิด 24 ครั้งสำหรับด้านใดด้านหนึ่งซึ่งแต่ละด้านสามารถใส่ได้ 5 บิต การเคลื่อนไหวครั้งต่อ ๆ ไปอาจต้องใช้บิตมากหรือน้อย แต่การเคลื่อนไหวทางกฎหมายนั้นสามารถแจกแจงได้เสมอดังนั้นความกว้างของการเคลื่อนไหวแต่ละครั้งสามารถเติบโตหรือขยายได้อย่างมีความสุข ฉันไม่ได้คำนวณ แต่ฉันคิดว่าตำแหน่ง 7 บิตจะหายาก

เมื่อใช้ระบบนี้เกมครึ่งจังหวะ 100 เกมสามารถเข้ารหัสได้ประมาณ 500 บิต อย่างไรก็ตามควรใช้หนังสือเปิด สมมติว่ามีล้านลำดับ จากนั้นให้ 0 เริ่มต้นแสดงถึงการเริ่มต้นจากบอร์ดมาตรฐานและ 1 ตามด้วยหมายเลข 20 บิตแสดงถึงการเริ่มต้นจากลำดับการเปิดนั้น เกมที่มีช่องเปิดที่ค่อนข้างธรรมดาอาจถูกย่อให้สั้นลงโดยพูดว่า 20 half-move หรือ 100 bits

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

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


ไม่จำเป็นต้องทราบตำแหน่งเริ่มต้น อาจเป็นส่วนหนึ่งของเกม
Andrew Rollings

@ แอนดรูว์: ใช่ ความผิดพลาดของฉัน. ฉันได้แก้ไขเพื่ออนุญาตให้ใช้ชิ้นส่วนเกมได้
Douglas Bagnall

2

หากเวลาในการคำนวณไม่ใช่ปัญหาคุณสามารถใช้ตัวสร้างตำแหน่งที่กำหนดได้เพื่อกำหนดรหัสเฉพาะให้กับตำแหน่งที่กำหนด

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

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

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

สมมติว่าเรารู้ว่าใครเป็นเทิร์น (แต่ละเทิร์นจะเปลี่ยนจากสีขาวเป็นสีดำ) ตัวสร้างที่กำหนดจะมีลักษณะดังนี้:

for each row
    for each column
        add to list ( get list of possible moves( current piece, players turn) )

'รับรายการการเคลื่อนไหวที่เป็นไปได้' จะทำสิ่งที่ต้องการ:

if current piece is not null 
    if current piece color is the same as the players turn
        switch( current piece type )
            king - return list of possible king moves( current piece )
            queen - return list of possible queen moves( current piece )
            rook - return list of possible rook moves( current piece )
            etc.

หากกษัตริย์อยู่ในการตรวจสอบ 'รายการของการเคลื่อนไหว xxx ที่เป็นไปได้' แต่ละรายการจะส่งคืนเฉพาะการเคลื่อนไหวที่ถูกต้องซึ่งเปลี่ยนสถานการณ์การตรวจสอบ


เป็นวิธีการแก้ปัญหาที่ส่อเสียด ... ดังนั้น ... ในกรณีนี้ให้อธิบายอัลกอริทึมของคุณสำหรับการสร้างจำนวนที่กำหนด
Andrew Rollings

ฉันพบลิงค์ที่น่าสนใจว่าจะใช้เวลานานแค่ไหนในการสร้างทุกตำแหน่งบนกระดานหมากรุก :) ioannis.virtualcomposer2000.com/math/EveryChess.html
Andrew Rollings

2

คำตอบส่วนใหญ่มองข้ามการทำซ้ำ 3 เท่า น่าเสียดายสำหรับการทำซ้ำ 3 เท่าคุณต้องจัดเก็บตำแหน่งทั้งหมดที่เล่นจนถึงตอนนี้ ...

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

บนกระดานมี 64 สี่เหลี่ยม 64 = 2 ^ 6 หากเราเก็บเฉพาะกำลังสองเริ่มต้นและสี่เหลี่ยมสุดท้ายของการเคลื่อนไหวแต่ละครั้งซึ่งจะใช้เวลา 12 บิต (การเลื่อนขั้นจะถูกแก้ไขในภายหลัง) โปรดทราบว่าโครงร่างนี้ครอบคลุมผู้เล่นในการเคลื่อนที่, เอาใจใส่, ชิ้นส่วนที่ถูกจับ, การร่าย ฯลฯ ด้วยเหตุนี้สามารถสร้างได้จากการเล่นซ้ำรายการย้าย

สำหรับการโปรโมตเราสามารถเก็บเวกเตอร์แยกต่างหากซึ่งจะมีข้อความว่า "เมื่อย้าย N เลื่อนไปที่ชิ้น XYZ" เราสามารถเก็บเวกเตอร์ของ (int, byte)

มันเป็นเรื่องที่น่าดึงดูดที่จะเพิ่มประสิทธิภาพเวกเตอร์ (To, From) ด้วยเนื่องจากเวกเตอร์ (ถึงจาก) จำนวนมากเหล่านี้ไม่สามารถมองเห็นได้ในหมากรุก เช่น. จะไม่มีการย้ายจาก e1 ไป d8 เป็นต้น แต่ฉันไม่สามารถหารูปแบบใด ๆ ได้ ยินดีรับความคิดเห็นเพิ่มเติม


2

ฉันคิดเรื่องนั้นมานานแล้ว (+ - 2 ชั่วโมง) และไม่มีคำตอบที่ชัดเจน

สมมติว่า:

  1. ไม่สนใจสถานะเวลา (ผู้เล่นไม่ได้ใช้การ จำกัด เวลาดังนั้นจึงสามารถบังคับให้จับฉลากได้โดยไม่ต้องเล่น)
  2. เล่นเกมเมื่อไหร่!? เป็นสิ่งสำคัญเนื่องจากกฎมีการเปลี่ยนแปลงอยู่ตลอดเวลา (ดังนั้นจะถือว่าเกมสมัยใหม่ในจุดต่อมาเป็นเกมสมัยใหม่ ... ) โปรดดูกฎจำนำที่ตายแล้ว (วิกิพีเดียมีปัญหาที่มีชื่อเสียงมากในการแสดง) และหากคุณต้องการ เพื่อย้อนเวลากลับไปโชคดีบิชอปใช้เพียงแค่เคลื่อนไหวช้าๆและใช้ลูกเต๋า ฮ่า ๆ.

... ดังนั้นมันจึงเป็นกฎที่ทันสมัย ขั้นแรกโดยไม่คำนึงถึงการทำซ้ำและย้ายขีด จำกัด การทำซ้ำ

-C 25 ไบต์ปัดเศษ (64b + 32 * 4b + 5b = 325b)

= 64 bits (something / nothing) + 32 * 4 bits [1bit = color {black / withe} + 3bit = type of piece {King, Queen, Bishop, kNight, Rook, Pawn, MovedPawn} NB: Moved pawn ... เช่นถ้ามันเป็นเบี้ยที่ย้ายครั้งสุดท้ายในเทิร์นก่อนหน้านี้แสดงว่า 'en passant' เป็นไปได้ ] + 5 บิตสำหรับสถานะจริง (ใครเทิร์น, en passant, ความเป็นไปได้ที่จะหมุนหรือไม่ในแต่ละด้าน)

จนถึงตอนนี้ดีมาก อาจจะสามารถปรับปรุงได้ แต่จะมีความยาวผันแปรและการส่งเสริมการขายที่ต้องพิจารณา!?

ตอนนี้กฎต่อไปนี้มีผลบังคับใช้เมื่อผู้เล่นเสมอกันมันไม่ได้เป็นไปโดยอัตโนมัติ! ดังนั้นลองพิจารณาการเคลื่อนไหว 90 ครั้งนี้โดยไม่มีการจับหรือการย้ายเบี้ยเป็นไปได้หากไม่มีผู้เล่นเรียกร้องให้เสมอ! หมายความว่าต้องบันทึกการเคลื่อนไหวทั้งหมด ... และพร้อมใช้งาน

-D การซ้ำตำแหน่ง ... เช่นสถานะของกระดานตามที่กล่าวไว้ข้างต้น (ดู C) หรือไม่ ... (ดูต่อไปนี้เกี่ยวกับกฎ FIDE) -E ทำให้ปัญหาที่ซับซ้อนของค่าเผื่อการเคลื่อนย้าย 50 ครั้งโดยไม่ต้องจับหรือจำนำมี เคาน์เตอร์จำเป็น ... อย่างไรก็ตาม

แล้วคุณจะจัดการกับมันอย่างไร? ... ไม่มีทางจริงๆ เนื่องจากผู้เล่นทั้งสองอาจไม่ต้องการวาดภาพหรือตระหนักว่ามันเกิดขึ้น ตอนนี้ในกรณีที่ E ตัวนับอาจพอเพียง ... แต่นี่คือเคล็ดลับและแม้แต่การอ่านกฎ FIDE (http://www.fide.com/component/handbook/?id=124&view=article) ฉันไม่พบ คำตอบ ... แล้วการสูญเสียความสามารถในการสัญจร นั่นคือการทำซ้ำหรือไม่? ฉันคิดว่าไม่ใช่ แต่นี่เป็นเรื่องเบลอที่ไม่ได้รับการกล่าวถึงไม่ได้รับการชี้แจง

ดังนั้นนี่คือกฎสองข้อที่ซับซ้อนสองข้อหรือไม่ได้กำหนดไว้แม้จะพยายามเข้ารหัส ... ไชโย

ดังนั้นวิธีเดียวที่จะเข้ารหัสเกมได้อย่างแท้จริงคือการบันทึกทั้งหมดตั้งแต่เริ่มต้น ... ซึ่งจะขัดแย้งกับคำถาม "สถานะบอร์ด" (หรือไม่?)

หวังว่านี่จะช่วยได้ ... ไม่ใช่คณิตศาสตร์มากเกินไป :-) เพียงเพื่อแสดงให้เห็นว่าบางคำถามไม่ง่ายเหมือนเปิดกว้างสำหรับการตีความหรือความรู้ล่วงหน้าเพื่อให้ถูกต้องและมีประสิทธิภาพ ไม่มีใครที่ฉันจะพิจารณาให้สัมภาษณ์เพราะมันเปิดหนอนมากเกินไป


2

การปรับปรุงตำแหน่งเริ่มต้นที่เป็นไปได้ในโซลูชันของ Yacoby

ไม่มีตำแหน่งทางกฎหมายที่มีมากกว่า 16 ชิ้นของแต่ละสี จำนวนวิธีในการวางชิ้นส่วนสีดำ 16 ชิ้นและสีขาว 16 ชิ้นบนสี่เหลี่ยม 64 ช่องมีค่าประมาณ 3.63e27 Log2 (3.63e27) = 91.55 ซึ่งหมายความว่าคุณสามารถเข้ารหัสตำแหน่งและสีของชิ้นส่วนทั้งหมดได้ใน 92 บิต ซึ่งน้อยกว่า 64 บิตสำหรับตำแหน่ง + สูงสุด 32 บิตสำหรับสีที่โซลูชันของ Yacoby ต้องการ คุณสามารถบันทึก 4 บิตในกรณีที่เลวร้ายที่สุดโดยเสียค่าใช้จ่ายที่ซับซ้อนมากในการเข้ารหัส

ในทางกลับกันจะเพิ่มขนาดสำหรับตำแหน่งที่ขาด 5 ชิ้นขึ้นไป ตำแหน่งเหล่านี้เป็นตัวแทนเพียง <4% ของตำแหน่งทั้งหมด แต่อาจเป็นกรณีส่วนใหญ่ที่คุณต้องการบันทึกตำแหน่งเริ่มต้นที่แตกต่างจากตำแหน่ง inital

สิ่งนี้นำไปสู่โซลูชันที่สมบูรณ์

  1. เข้ารหัสตำแหน่งและสีของชิ้นงานตามวิธีการด้านบน 92 บิต
  2. ในการระบุประเภทของแต่ละชิ้นให้ใช้ Huffman Code: pawn: '0', rook: '100', knight: '101', bishop: '110', queen: '1110', king: '1111' สิ่งนี้ต้องการ (16 * 1 + 12 * 3 + 4 * 4) = 68 บิตสำหรับชิ้นงานทั้งชุด ตำแหน่งบอร์ดเต็มสามารถเข้ารหัสได้สูงสุด 92 + 68 = 160 บิตบิตสูงสุด
  3. เพิ่มสถานะเกมเพิ่มเติม: เทิร์น: 1 บิตซึ่งการร่ายเป็นไปได้: 4 บิต "en passant" เป็นไปได้: สูงสุด 4 บิต (1 บิตบอกว่าเป็นกรณีและ 3 บิตบอกว่าอันไหน) ตำแหน่งเริ่มต้นถูกเข้ารหัสเป็น = 160 + 9 = 169 บิต
  4. สำหรับรายการการเคลื่อนไหวให้ระบุการเคลื่อนไหวที่เป็นไปได้ทั้งหมดสำหรับตำแหน่งที่กำหนดและจัดเก็บตำแหน่งของการเคลื่อนไหวในรายการ รายการการเคลื่อนไหวรวมถึงกรณีพิเศษทั้งหมด (การเหวี่ยงแห, คนเดินผ่านและการลาออก) ใช้เฉพาะบิตเท่าที่จำเป็นเพื่อจัดเก็บตำแหน่งสูงสุด โดยเฉลี่ยแล้วไม่ควรเกิน 7 บิตต่อการเคลื่อนไหว (โดยเฉลี่ย 16 ชิ้นและ 8 ท่าต่อชิ้นโดยเฉลี่ย) ในบางกรณีเมื่อมีการบังคับย้ายต้องใช้เพียง 1 บิต (ย้ายหรือลาออก)

1

มี 32 ชิ้นบนกระดาน แต่ละชิ้นมีตำแหน่ง (หนึ่งใน 64 ช่อง) คุณก็ต้องมีจำนวนเต็มบวก 32 ตัว

ฉันรู้ว่า 64 ตำแหน่งถือเป็น 6 บิต แต่ฉันจะไม่ทำอย่างนั้น ฉันจะเก็บเศษสุดท้ายไว้สำหรับแฟล็กสองสามอัน (ชิ้นส่วนที่หลุดออกไปเบี้ยของราชินี)


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

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

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

ฉันคิดว่าถ้าเบี้ยยังคงเป็นเบี้ยหรือได้รับการเลื่อนขั้นเป็นราชินีก็แทบจะไม่เกี่ยวข้องกับเกมที่เหลือ ถ้าคุณไม่คิดอย่างนั้นฉันชอบเล่นเกมหมากรุกกับคุณ; ^)
คางคก

@reinier: เขาอ้างว่ามันไม่เกี่ยวข้องหากราชินีองค์ปัจจุบันเดิมเป็นราชินีหรือเป็นเบี้ย
อ. เร็กซ์

1

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

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


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

2
คุณไม่จำเป็นต้องเข้ารหัสกฎในข้อมูลจริง คุณสามารถสันนิษฐานได้ว่าตัวเข้ารหัสรู้กฎใด ๆ ที่จำเป็น
Andrew Rollings

อ่า .. ไม่ได้คำนึงถึงเวลาเล่น Good call ... :)
Andrew Rollings

@Andrew Rollings: กฎขึ้นอยู่กับสถานะเช่นเดียวกับในจะทริกเกอร์เมื่อตรงตามเงื่อนไขที่กำหนดเท่านั้น การติดตามสถานะของเงื่อนไขเบื้องต้นนั้นก็เป็นส่วนหนึ่งของ ... :)
Shaggy Frog

ไม่เกี่ยวข้องในกรณีนี้ หากจำเป็นตัวถอดรหัสสามารถตรวจสอบสถานะเพื่อหาผู้ชนะได้ โปรดจำไว้ว่าตัวเข้ารหัส / ตัวถอดรหัสเป็นกฎที่รับรู้ สิ่งเดียวที่ต้องเข้ารหัสจริงๆคือตัวเลือกของผู้เล่น - สิ่งอื่นใดที่สามารถสันนิษฐานได้ว่าเป็นตัวเข้ารหัส / ตัวถอดรหัส
Andrew Rollings

1

ดังที่คนอื่น ๆ กล่าวไว้ว่าคุณทำได้สำหรับ 32 ชิ้นแต่ละชิ้นคุณสามารถจัดเก็บสี่เหลี่ยมจัตุรัสที่วางไว้และถ้าอยู่บนกระดานหรือไม่สิ่งนี้จะให้ 32 * (log2 (64) + 1) = 224 บิต

อย่างไรก็ตามบิชอปสามารถครอบครองได้เฉพาะช่องสี่เหลี่ยมสีดำหรือสีขาวดังนั้นคุณต้องใช้ log2 (32) บิตสำหรับตำแหน่งซึ่งให้ 28 * 7 + 4 * 6 = 220 บิต

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


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

6 บิตสำหรับอธิการคือ log2 (32) + 1 = 6 แต่นี่เป็นคำถามที่ซับซ้อนเมื่อคุณพิจารณารายละเอียดทั้งหมด :)
Andreas Brinck

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

1

บอร์ดมี 64 ช่องสี่เหลี่ยมและสามารถแทนได้ด้วย 64 บิตแสดงว่าสี่เหลี่ยมว่างหรือไม่ เราต้องการข้อมูลชิ้นส่วนหากสี่เหลี่ยมมีชิ้นส่วน เนื่องจากผู้เล่น + ชิ้นส่วนใช้เวลา 4 บิต (ดังที่แสดงไว้ก่อนหน้านี้) เราจึงได้สถานะปัจจุบันเป็น 64 + 4 * 32 = 192 บิต โยนในเทิร์นปัจจุบันและคุณมี 193 บิต

อย่างไรก็ตามเราต้องเข้ารหัสการเคลื่อนไหวทางกฎหมายสำหรับแต่ละชิ้นด้วย ขั้นแรกเราคำนวณจำนวนการเคลื่อนไหวทางกฎหมายสำหรับแต่ละชิ้นและต่อท้ายบิตจำนวนมากหลังตัวระบุชิ้นส่วนของสี่เหลี่ยมเต็ม ฉันคำนวณดังนี้:

จำนำ: ไปข้างหน้าก่อนเปิดสองไปข้างหน้า en passant * 2 โปรโมชั่น = 7 บิต คุณสามารถรวมการหมุนครั้งแรกและการเลื่อนตำแหน่งเป็นบิตเดียวเนื่องจากไม่สามารถเกิดขึ้นจากตำแหน่งเดียวกันได้ดังนั้นคุณจึงมี 6. Rook: 7 เหลี่ยมแนวตั้ง 7 สี่เหลี่ยมแนวนอน = 14 บิตอัศวิน: 8 เหลี่ยม = 8 บิตบิชอป: 2 เส้นทแยงมุม * 7 = 14 บิตราชินี: 7 แนวตั้ง, 7 แนวนอน, 7 ทแยงมุม, 7 ทแยงมุม = 28 บิตคิง: 8 สี่เหลี่ยมรอบ ๆ

นี่ยังคงหมายความว่าคุณจะต้องแมปสี่เหลี่ยมเป้าหมายตามตำแหน่งปัจจุบัน แต่มัน (ควร) เป็นการคำนวณง่ายๆ

เนื่องจากเรามีเบี้ย 16 ตัว 4 rooks / knights / Bishop และ 2 queens / kings นี่คือ 16 * 6 + 4 * 14 + 4 * 8 + 4 * 14 + 2 * 28 + 2 * 8 = อีก 312 บิต รวมเป็น 505 บิตโดยรวม

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

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

แก้ไข 1: ลืมเกี่ยวกับการส่งเสริมการหล่อและการจำนำชิ้นส่วนใด ๆ สิ่งนี้สามารถนำผลรวมที่มีตำแหน่งที่ชัดเจนไปสู่การเคลื่อนไหว 557 ครั้ง (อีก 3 บิตสำหรับเบี้ย, 2 สำหรับราชา)


1

แต่ละชิ้นแสดงได้ 4 บิต (จำนำถึงราชา 6 แบบ) สีดำ / ขาว = 12 ค่า

แต่ละตารางบนกระดานสามารถแสดงด้วย 6 บิต (พิกัด x, พิกัด y)

ตำแหน่งเริ่มต้นต้องการสูงสุด 320 บิต (32 ชิ้น, 4 + 6 บิต)

การเคลื่อนไหวที่ตามมาแต่ละครั้งสามารถแสดงด้วย 16 บิต (จากตำแหน่งไปยังตำแหน่งชิ้นส่วน)

Castling ต้องการ 16 บิตพิเศษเนื่องจากเป็นการเคลื่อนที่แบบคู่

เบี้ยที่ได้รับการราชินีสามารถแสดงด้วยค่าสำรองหนึ่งใน 4 ค่าจาก 4 บิต

โดยไม่ต้องทำรายละเอียดทางคณิตศาสตร์สิ่งนี้จะเริ่มประหยัดพื้นที่หลังจากการย้ายครั้งแรกเมื่อเทียบกับการจัดเก็บ 32 * 7 บิต (อาร์เรย์ของชิ้นส่วนที่กำหนดไว้ล่วงหน้า) หรือ 64 * 4 บิต (การกำหนดกำลังสองที่กำหนดไว้ล่วงหน้า)

หลังจาก 10 ครั้งทั้งสองด้านพื้นที่สูงสุดที่ต้องการคือ 640 บิต

... แต่อีกครั้งถ้าเราระบุแต่ละชิ้นโดยไม่ซ้ำกัน (5 บิต) และเพิ่มบิตที่หกสำหรับการตั้งค่าสถานะเบี้ยราชินีเราต้องใช้ชิ้นส่วนรหัส + ไปยังตำแหน่งสำหรับการเคลื่อนไหวแต่ละครั้ง สิ่งนี้เปลี่ยนการคำนวณเป็น ...

ตำแหน่งเริ่มต้น = สูงสุด 384 บิต (32 ชิ้น, 6 + 6 บิต) การเคลื่อนที่แต่ละครั้ง = 12 บิต (ไปยังตำแหน่ง, ชิ้นส่วน -ID)

หลังจากนั้น 10 ครั้งในแต่ละด้านพื้นที่สูงสุดที่ต้องการคือ 624 บิต


ตัวเลือกที่สองมีข้อได้เปรียบเพิ่มเติมที่การจัดเก็บสามารถอ่านเป็นระเบียน 12 บิตแต่ละระเบียน = ตำแหน่งและชิ้นส่วน การเคลื่อนย้ายครั้งแรกถูกตรวจพบโดยข้อเท็จจริงที่ว่าชิ้นส่วนนั้นมีการเข้าก่อน
Steve De Caux

สำหรับช่วงเวลาระหว่างการเคลื่อนไหวให้เพิ่ม x บิตสำหรับตัวนับในแต่ละเร็กคอร์ด สำหรับบันทึกการตั้งค่านี้จะตั้งค่าเป็น 0
Steve De Caux

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

ดังนั้นสำหรับเกมทั่วไปหลังจาก 10 ท่าคุณจะอยู่ที่ 121 บิตโดยถือว่าไม่มีการโปรโมต เกมผิดปกติจะต้องใช้ 1 บิตสำหรับธงชิ้นส่วน * 10 บิตและอีกบิตเพื่อระบุผู้เล่นคนแรก นอกจากนี้การเคลื่อนไหวแต่ละครั้งจะต้องใช้เพียง 12 บิตเนื่องจากชิ้นส่วนบนสี่เหลี่ยมที่กำหนดนั้นมีนัยจากการเคลื่อนไหวก่อนหน้านี้ในเกม วิธีนี้อาจมีประสิทธิภาพน้อยกว่าวิธีที่แนะนำ แต่ค่อนข้างสะอาดและมีประสิทธิภาพพอสมควรสำหรับเกม "มาตรฐาน"
kyoryu

@kyoro - ฉันไม่มั่นใจว่า 12 บิตต่อการเคลื่อนไหวสามารถเอาชนะได้ - โดยใช้ความคิดของคุณเกี่ยวกับโมฆะสำหรับการตั้งค่ามาตรฐาน (ฉันจะยังคงใช้ 12 บิตที่ตั้งค่าเป็น bin ศูนย์) - หลังจาก 1,000 การเคลื่อนไหวในแต่ละด้านนี่คือ 24012 บิตหรือที่รู้จัก 3002 ไบต์ (ปัดเศษขึ้น) แม้จะใช้การบีบอัดรูปแบบบางอย่างคุณก็ต้องโกงเพื่อเอาชนะสิ่งนี้โดยการประกาศฮาร์ดโค้ดในพจนานุกรมของคุณ (หรือหาค่าได้ในเชิงตรรกะเช่นเดียวกัน)
Steve De Caux

1

เช่นเดียวกับ Robert G ฉันมักจะใช้ PGN เนื่องจากเป็นมาตรฐานและสามารถใช้ได้กับเครื่องมือหลากหลายประเภท

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

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

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

เราเข้ารหัสปลายทางของชิ้นส่วนส่วนใหญ่โดยการกำหนดหมายเลขสี่เหลี่ยมตามเส้นตามลำดับต่อไปนี้: W, NW, N, NE (ด้านดำคือ N) เส้นเริ่มต้นในช่องสี่เหลี่ยมที่ไกลที่สุดในทิศทางที่กำหนดซึ่งถูกต้องตามกฎหมายที่จะย้ายไปและดำเนินการต่อไปยัง. สำหรับราชาที่ไม่มีภาระผูกพันรายการการเคลื่อนไหวคือ W, E, NW, SE, N, S, NE, SW สำหรับอัศวินเราเริ่มต้นด้วย 2W1N และดำเนินการตามเข็มนาฬิกา ปลายทาง 0 คือปลายทางแรกที่ถูกต้องในคำสั่งซื้อนี้

  • เบี้ย: โรงรับจำนำที่ไม่มีการเคลื่อนไหวมีทางเลือก 2 ทางดังนั้นจึงต้องใช้ 1 บิต หากจำนำสามารถจับตัวอื่นได้ไม่ว่าจะเป็นแบบปกติหรือแบบ en passant (ซึ่งตัวถอดรหัสสามารถระบุได้เนื่องจากมีการติดตามสถานะ) ก็มีตัวเลือกการเคลื่อนไหว 2 หรือ 3 แบบ นอกเหนือจากนั้นเบี้ยสามารถมีได้ 1 ทางเลือกเท่านั้นโดยไม่ต้องใช้บิต เมื่อเบี้ยอยู่ใน 7 THอันดับของเรายังตรึงอยู่กับทางเลือกโปรโมชั่น เนื่องจากเบี้ยมักจะได้รับการเลื่อนตำแหน่งให้เป็นราชินีตามด้วยอัศวินเราจึงเข้ารหัสตัวเลือกเป็น:
    • ราชินี: 0
    • อัศวิน: 10
    • อธิการ: 110
    • rook: 111
  • บิชอป: ปลายทางมากที่สุด 13 แห่งเมื่ออยู่ที่ {d, e} {4,5} สำหรับ 4 บิต
  • Rook: มากที่สุด 14 จุดหมายปลายทาง 4 บิต
  • อัศวิน: มากที่สุด 8 จุดหมายปลายทาง 3 บิต
  • Kings: เมื่อ castling เป็นตัวเลือกราชาจะกลับไปที่ S และไม่สามารถเคลื่อนที่ลงไปข้างล่างได้ ซึ่งมีจุดหมายปลายทางทั้งหมด 7 แห่ง ช่วงเวลาที่เหลือราชามีท่ามากที่สุด 8 ท่าโดยให้มากที่สุด 3 บิต
  • Queen: เหมือนกับตัวเลือกสำหรับบิชอปหรือโร้คทั้งหมด 27 ตัวเลือกซึ่งเป็น 5 บิต

เนื่องจากจำนวนตัวเลือกไม่ได้เป็นสองส่วนเสมอไปตัวเลือกข้างต้นยังคงสิ้นเปลืองบิต สมมติว่าจำนวนตัวเลือกคือCและตัวเลือกเฉพาะคือเลขcและn = ceil (lg ( C )) (จำนวนบิตที่ต้องใช้ในการเข้ารหัสตัวเลือก) เราใช้ประโยชน์จากค่าที่สูญเปล่าเหล่านี้โดยการตรวจสอบบิตแรกของตัวเลือกถัดไป ถ้าเป็น 0 ไม่ต้องทำอะไรเลย ถ้าเป็นที่ 1 และC + C <2 nแล้วเพิ่มCเพื่อค การถอดรหัสตัวเลขจะย้อนกลับสิ่งนี้: ถ้าได้รับc > = Cให้ลบCและตั้งค่าบิตแรกสำหรับตัวเลขถัดไปเป็น 1 ถ้า c<2n - Cจากนั้นตั้งค่าบิตแรกสำหรับตัวเลขถัดไปเป็น 0 ถ้า 2 n - C <= c < Cแล้วไม่ต้องทำอะไรเลย เรียกโครงการนี้ว่า "ความอิ่มตัว"

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

ด้วยความอิ่มตัวการเข้ารหัสการจับภาพอาจไม่ได้เปรียบ เราสามารถอนุญาตสำหรับทั้งสองตัวเลือกโดยระบุในสถานะเริ่มต้นที่ใช้ หากไม่ได้ใช้ความอิ่มตัวการเข้ารหัสเกมยังสามารถใช้หมายเลขตัวเลือกที่ไม่ได้ใช้ ( C <= c <2 n ) เพื่อเปลี่ยนตัวเลือกระหว่างเกม เมื่อใดก็ตามที่C ยกกำลังสองเราไม่สามารถเปลี่ยนตัวเลือกได้


1

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

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

โดยทั่วไปการเคลื่อนไหวจะใช้เวลา 5 หรือ 6 บิต แต่อาจแตกต่างกันไปตั้งแต่ 1 ถึง 8

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


1

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

  • 2 บาทหลวง 13 จุดหมายปลายทางแต่ละแห่ง = 26
  • 2 rooks จุดหมายปลายทาง 14 แห่ง = 28
  • 2 อัศวินแต่ละ 8 จุดหมายปลายทาง = 16
  • ราชินี 27 จุดหมายปลายทาง
  • ราชา 8 จุดหมายปลายทาง

8 อุ้งเท้าทั้งหมดอาจกลายเป็นราชินีได้ในกรณีที่เลวร้ายที่สุด (การแจงนับ) ดังนั้นจึงมีจำนวนจุดหมายปลายทางที่เป็นไปได้มากที่สุด 9 * 27 + 26 + 28 + 16 + 8 = 321 ดังนั้นปลายทางทั้งหมดสำหรับการเคลื่อนไหวใด ๆ สามารถระบุได้ด้วยตัวเลข 9 บิต

จำนวนการเคลื่อนไหวสูงสุดของทั้งสองฝ่ายคือ 100 (ถ้าฉันไม่ผิดไม่ใช่ผู้เล่นหมากรุก) ดังนั้นเกมใด ๆ จึงสามารถบันทึกได้ใน 900 บิต บวกตำแหน่งเริ่มต้นแต่ละชิ้นสามารถบันทึกโดยใช้ตัวเลข 6 บิตซึ่งรวมเป็น 32 * 6 = 192 บิต บวกหนึ่งบิตสำหรับบันทึก "ใครย้ายก่อน" ดังนั้นเกมใด ๆ สามารถบันทึกได้โดยใช้ 900 + 192 + 1 = 1093 บิต


1

การจัดเก็บสถานะบอร์ด

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

ถัดไปแทนตัวหมากรุกทุกตัวตามลำดับตำแหน่ง การใช้ 4 บิตต่อชิ้นจะใช้เวลา 32 * 4 บิต (รวม 128 ชิ้น) ซึ่งมันสิ้นเปลืองจริงๆ.

การใช้ต้นไม้ไบนารีเราสามารถแทนเบี้ยในหนึ่งไบต์อัศวินและโร้กและบิชอปใน 3 และราชาและราชินีใน 4 เนื่องจากเราจำเป็นต้องเก็บสีของชิ้นส่วนซึ่งจะใช้เวลาเพิ่มอีกหนึ่งไบต์ เป็น (ยกโทษให้ฉันถ้าผิดฉันไม่เคยดู Huffman codingในรายละเอียดใด ๆ มาก่อน):

  • จำนำ: 2
  • Rook: 4
  • อัศวิน: 4
  • บิชอป: 4
  • กษัตริย์: 5
  • ราชินี: 5

รับผลรวม:

2*16 + 4*4 + 4*4 + 4*4 + 2*5 + 2*5 = 100

ซึ่งเต้นโดยใช้ชุดบิตขนาดคงที่ 28 บิต

ดังนั้นวิธีที่ดีที่สุดที่ฉันพบคือเก็บไว้ในอาร์เรย์8 2 + 100 บิต

8*8 + 100 = 164



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

น่าเสียดายที่มีกฎพิเศษหลายอย่างเช่นการเหวี่ยงแหหรือโค่นล้มกษัตริย์และสร้างสาธารณรัฐ (ข้อมูลอ้างอิงของ Terry Pratchett) ดังนั้นก่อนที่เราจะจัดเก็บชิ้นส่วนเพื่อเคลื่อนย้ายเราจำเป็นต้องมีการระบุเพียงเล็กน้อยว่าเป็นการเคลื่อนไหวพิเศษหรือไม่

ดังนั้นสำหรับการเคลื่อนไหวปกติแต่ละครั้งเรามี1 + 5 = 6บิตที่จำเป็น (ประเภท 1 บิต 5 บิตสำหรับชิ้นส่วน)

หลังจากถอดรหัสหมายเลขชิ้นส่วนแล้วเราจะทราบประเภทของชิ้นส่วนและแต่ละชิ้นควรแสดงถึงการเคลื่อนไหวอย่างมีประสิทธิภาพสูงสุด ตัวอย่างเช่น (หากกฎหมากรุกของฉันเป็นเพียงการขีดข่วน) เบี้ยมีการเคลื่อนไหวที่เป็นไปได้ทั้งหมด 4 ท่า (เลี้ยวซ้ายเลี้ยวขวาเดินหน้าหนึ่งก้าวไปข้างหน้า 2 ครั้ง)
ดังนั้นในการแสดงการย้ายจำนำเราต้องมีบิต '6 + 2 = 8' (6 บิตสำหรับส่วนหัวการย้ายเริ่มต้น 2 บิตสำหรับสิ่งที่ย้าย)

การเคลื่อนที่เพื่อราชินีจะมีความซับซ้อนมากขึ้นดังนั้นจึงเป็นการดีที่สุดที่จะมีทิศทาง (8 ทิศทางที่เป็นไปได้ดังนั้น 3 บิต) และกำลังสองทั้งหมด 8 ช่องที่จะย้ายไปสำหรับแต่ละทิศทาง (ดังนั้นอีก 3 บิต) ดังนั้นเพื่อแสดงถึงการเคลื่อนย้ายราชินีจึงต้องใช้6 + 3 + 3 = 12บิต

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



Resultant Format
ดังนั้นรูปแบบไฟล์จะเป็นแบบนี้

[64 บิต] ตำแหน่งชิ้นส่วนเริ่มต้น
[สูงสุด 100 บิต] ชิ้นส่วนเริ่มต้น [1 บิต] เทิร์นของผู้เล่น
[n บิต] การเคลื่อนไหว

โดยที่การเคลื่อนไหวคือ
[1 บิต] ประเภทการเคลื่อนไหว (พิเศษหรือปกติ)
[n บิต] รายละเอียดการย้าย

หากการเคลื่อนไหวเป็นการเคลื่อนไหวปกติรายละเอียดการเคลื่อนไหวจะมีลักษณะคล้ายกับการย้าย
ชิ้นส่วน
[ 5 บิต] ชิ้น[n บิต] (โดยปกติจะอยู่ในช่วง 2 ถึง 6 บิต)

หากเป็นการย้ายแบบพิเศษ
ควรมีประเภทจำนวนเต็มแล้วจึงมีข้อมูลเพิ่มเติมใด ๆ (เช่นหากเป็นการร่าย) ฉันจำจำนวนท่าพิเศษไม่ได้ดังนั้นมันอาจจะโอเคแค่บ่งบอกว่ามันเป็นท่าพิเศษ (ถ้ามีเพียงท่าเดียว)


1

ในกรณีฐานของกระดานเริ่มต้นบวกการเคลื่อนไหวที่ตามมาให้พิจารณาสิ่งต่อไปนี้

ใช้โปรแกรมหมากรุกเพื่อกำหนดความน่าจะเป็นของการเคลื่อนไหวที่เป็นไปได้ทั้งหมด ตัวอย่างเช่น 40% สำหรับ e2-e4 20% สำหรับ d2-d4 เป็นต้น หากการเคลื่อนไหวบางอย่างถูกกฎหมาย แต่ไม่ได้รับการพิจารณาจากโปรแกรมนั้นให้มีความเป็นไปได้ต่ำ ใช้การเข้ารหัสเลขคณิตเพื่อบันทึกว่าตัวเลือกใดถูกเลือกซึ่งจะเป็นตัวเลขระหว่าง 0 ถึง 0.4 สำหรับการย้ายครั้งแรก 0.4 และ 0.6 สำหรับครั้งที่สองเป็นต้น

ทำเช่นเดียวกันกับอีกด้านหนึ่ง ตัวอย่างเช่นหากมีโอกาส 50% ที่ e7-e5 เป็นการตอบสนองต่อ e2-e4 ตัวเลขที่เข้ารหัสจะอยู่ระหว่าง 0 ถึง 0.2 ทำซ้ำจนกว่าเกมจะเสร็จสิ้น ผลลัพธ์ที่ได้คือช่วงที่มีขนาดเล็กมาก ค้นหาเศษส่วนไบนารีที่มีฐานที่เล็กที่สุดซึ่งเหมาะกับช่วงนั้น นั่นคือการเข้ารหัสทางคณิตศาสตร์

สิ่งนี้ดีกว่า Huffman เนื่องจากสามารถคิดว่าเป็นการเข้ารหัสบิตแบบเศษส่วน (บวกบางส่วนในตอนท้ายของเกมเพื่อปัดเศษขึ้นเป็นบิตทั้งหมด)

ผลลัพธ์ควรมีขนาดกะทัดรัดกว่า Huffman และไม่มีกรณีพิเศษสำหรับการเลื่อนตำแหน่งผู้สัญจรการย้ายกฎ 50 ข้อและรายละเอียดอื่น ๆ เนื่องจากได้รับการจัดการโดยโปรแกรมการประเมินผลหมากรุก

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

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

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

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