ปรับปรุง:ฉันชอบเรื่องนี้มากผมเขียนโปรแกรมปริศนาตำแหน่งหมากรุกและ Huffman Coding หากคุณอ่านสิ่งนี้ฉันได้พิจารณาแล้วว่าวิธีเดียวในการจัดเก็บสถานะเกมที่สมบูรณ์คือการจัดเก็บรายการการเคลื่อนไหวทั้งหมด อ่านต่อไปว่าทำไม ดังนั้นฉันจึงใช้ปัญหาในเวอร์ชันที่เรียบง่ายขึ้นเล็กน้อยสำหรับการจัดวางชิ้นส่วน
ปัญหา
ภาพนี้แสดงตำแหน่งหมากรุกเริ่มต้น หมากรุกเกิดขึ้นบนกระดาน 8x8 โดยผู้เล่นแต่ละคนเริ่มต้นด้วยชุดที่เหมือนกัน 16 ชิ้นซึ่งประกอบด้วย 8 เบี้ย, 2 rooks, 2 อัศวิน, 2 บาทหลวง, 1 ราชินีและ 1 คิงดังที่แสดงไว้ที่นี่:
โดยทั่วไปตำแหน่งจะถูกบันทึกเป็นตัวอักษรสำหรับคอลัมน์ตามด้วยหมายเลขสำหรับแถวดังนั้น White's queen จึงอยู่ที่ d1 การเคลื่อนไหวส่วนใหญ่มักถูกจัดเก็บในรูปแบบพีชคณิตซึ่งไม่คลุมเครือและโดยทั่วไปจะระบุข้อมูลเพียงเล็กน้อยที่จำเป็นเท่านั้น พิจารณาการเปิดนี้:
- e4 e5
- Nf3 Nc6
- …
ซึ่งแปลเป็น:
- White ย้ายเบี้ยของกษัตริย์จาก e2 เป็น e4 (เป็นชิ้นเดียวที่สามารถไปถึง e4 ได้ด้วยเหตุนี้ "e4");
- Black ย้ายเบี้ยของกษัตริย์จาก e7 เป็น e5;
- White ย้ายอัศวิน (N) ไปที่ f3;
- แบล็คย้ายอัศวินไปที่ c6
- …
บอร์ดมีลักษณะดังนี้:
ความสามารถที่สำคัญสำหรับโปรแกรมเมอร์ใดที่จะสามารถได้อย่างถูกต้องและชัดเจนระบุปัญหา
มีอะไรหายไปหรือคลุมเครือ? มากตามที่ปรากฎ
สถานะบอร์ดเทียบกับสถานะเกม
สิ่งแรกที่คุณต้องพิจารณาคือคุณจัดเก็บสถานะของเกมหรือตำแหน่งของชิ้นส่วนบนกระดาน การเข้ารหัสเพียงตำแหน่งของชิ้นส่วนเป็นสิ่งหนึ่ง แต่ปัญหากล่าวว่า "การเคลื่อนไหวทางกฎหมายที่ตามมาทั้งหมด" ปัญหายังบอกว่าไม่มีอะไรเกี่ยวกับการรู้ว่าการย้ายมาถึงจุดนี้ นั่นเป็นปัญหาจริงๆตามที่ฉันจะอธิบาย
คาสติ้ง
เกมได้ดำเนินการดังนี้:
- e4 e5
- Nf3 Nc6
- Bb5 a6
- Ba4 Bc5
คณะกรรมการมีลักษณะดังนี้:
สีขาวมีตัวเลือกของcastling ข้อกำหนดส่วนหนึ่งคือราชาและโร๊คที่เกี่ยวข้องไม่สามารถเคลื่อนไหวได้ดังนั้นไม่ว่าราชาหรือโร๊คของแต่ละฝ่ายจะย้ายไปแล้วก็จำเป็นต้องเก็บไว้ เห็นได้ชัดว่าถ้าพวกเขาไม่ได้อยู่ในตำแหน่งเริ่มต้นพวกเขาได้ย้ายไปเป็นอย่างอื่นจำเป็นต้องระบุ
มีกลยุทธ์หลายอย่างที่สามารถใช้ในการจัดการกับปัญหานี้
ประการแรกเราสามารถจัดเก็บข้อมูลเพิ่มเติมได้ 6 บิต (1 สำหรับแต่ละโร๊คและราชา) เพื่อระบุว่าชิ้นส่วนนั้นเคลื่อนที่หรือไม่ เราสามารถปรับปรุงสิ่งนี้ได้โดยการจัดเก็บเพียงเล็กน้อยสำหรับหนึ่งในหกสี่เหลี่ยมนี้ถ้าชิ้นส่วนที่ถูกต้องอยู่ในนั้น อีกวิธีหนึ่งคือเราสามารถถือว่าชิ้นส่วนที่ไม่มีการเคลื่อนไหวแต่ละชิ้นเป็นชิ้นส่วนประเภทอื่นดังนั้นแทนที่จะเป็น 6 ชิ้นในแต่ละด้าน (เบี้ย, โกง, อัศวิน, บิชอป, ราชินีและราชา) มี 8 ชิ้น (เพิ่มโร็คที่ไม่เคลื่อนไหวและราชาที่ไม่เคลื่อนไหว)
En Passant
ที่แปลกประหลาดและมักจะละเลยการปกครองในหมากรุกก็คือการกิน
เกมมีความคืบหน้า
- e4 e5
- Nf3 Nc6
- Bb5 a6
- Ba4 Bc5
- OO b5
- Bb3 b4
- c4
จำนำของ Black บน b4 ตอนนี้มีตัวเลือกในการย้ายเบี้ยของเขาบน b4 ไปที่ c3 โดยใช้ White pawn บน c4 สิ่งนี้จะเกิดขึ้นในโอกาสแรกเท่านั้นซึ่งหมายความว่าหาก Black ส่งผ่านตัวเลือกในตอนนี้เขาไม่สามารถก้าวต่อไปได้ ดังนั้นเราจำเป็นต้องจัดเก็บสิ่งนี้
หากเราทราบการเคลื่อนไหวก่อนหน้านี้เราสามารถตอบได้อย่างแน่นอนว่า En Passant เป็นไปได้หรือไม่ อีกทางเลือกหนึ่งที่เราสามารถจัดเก็บได้ว่าเบี้ยแต่ละตัวในอันดับที่ 4 เพิ่งย้ายไปที่นั่นหรือไม่ด้วยการเดินหน้าสองครั้ง หรือเราสามารถดูตำแหน่ง En Passant ที่เป็นไปได้บนกระดานและมีธงเพื่อระบุว่าเป็นไปได้หรือไม่
โปรโมชั่น
มันคือการเคลื่อนไหวของไวท์ ถ้า White ย้ายเบี้ยของเขาใน h7 ถึง h8 มันจะสามารถเลื่อนขั้นเป็นชิ้นส่วนอื่นได้ (แต่ไม่ใช่ราชา) 99% ของเวลาที่ได้รับการเลื่อนตำแหน่งเป็นราชินี แต่บางครั้งก็ไม่เป็นเช่นนั้นโดยทั่วไปแล้วเพราะอาจบังคับให้เป็นทางตันเมื่อไม่เช่นนั้นคุณจะชนะ สิ่งนี้เขียนเป็น:
- 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 สร้างการแทนที่โค้ดความยาวตัวแปรโดยทั่วไปจะใช้ความถี่ที่คาดหวังของสัญลักษณ์เพื่อกำหนดค่าที่สั้นกว่าให้กับสัญลักษณ์ทั่วไป
ในต้นไม้ข้างต้นตัวอักษร 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(), "");
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 ที่ดีที่สุดจากนั้นเข้ารหัสตัวเลือกโครงร่างในสองบิตแรกจากนั้นจึงจัดเก็บข้อมูลเฉพาะแบบแผนหลังจากนั้น
ด้วยค่าใช้จ่ายที่เล็กสิ่งนี้จะเป็นแนวทางที่ดีที่สุด
สถานะเกม
ฉันกลับไปที่ปัญหาของการจัดเก็บที่เกมมากกว่าตำแหน่ง เนื่องจากการทำซ้ำสามเท่าเราจึงต้องจัดเก็บรายการการเคลื่อนไหวที่เกิดขึ้นจนถึงจุดนี้
คำอธิบายประกอบ
สิ่งหนึ่งที่คุณต้องพิจารณาคือคุณเพียงแค่จัดเก็บรายการการเคลื่อนไหวหรือคุณกำลังใส่คำอธิบายประกอบเกมอยู่? เกมหมากรุกมักจะมีคำอธิบายประกอบเช่น:
- บับ 5 !! Nc4?
การเคลื่อนไหวของ White ถูกทำเครื่องหมายด้วยเครื่องหมายอัศเจรีย์สองจุดว่ายอดเยี่ยมในขณะที่ Black ถูกมองว่าเป็นความผิดพลาด ดูหมากรุกเครื่องหมายวรรคตอน
นอกจากนี้คุณอาจต้องจัดเก็บข้อความอิสระตามที่อธิบายการเคลื่อนไหว
ฉันสมมติว่าการเคลื่อนไหวเพียงพอแล้วจึงไม่มีคำอธิบายประกอบ
สัญกรณ์เกี่ยวกับพีชคณิต
เราสามารถจัดเก็บข้อความของการย้ายได้ที่นี่ (“ e4”,“ Bxb5” ฯลฯ ) การรวมไบต์การยกเลิกที่คุณกำลังดูอยู่ที่ประมาณ 6 ไบต์ (48 บิต) ต่อการเคลื่อนไหว (กรณีที่แย่ที่สุด) นั่นไม่ได้มีประสิทธิภาพโดยเฉพาะ
สิ่งที่สองที่ต้องลองคือการจัดเก็บตำแหน่งเริ่มต้น (6 บิต) และตำแหน่งสิ้นสุด (6 บิต) ดังนั้น 12 บิตต่อการเคลื่อนที่ ที่ดีกว่าอย่างเห็นได้ชัด
อีกวิธีหนึ่งคือเราสามารถกำหนดการเคลื่อนไหวทางกฎหมายทั้งหมดจากตำแหน่งปัจจุบันด้วยวิธีและสถานะที่คาดเดาได้และกำหนดได้ตามที่เราเลือกไว้ จากนั้นกลับไปที่การเข้ารหัสฐานตัวแปรที่กล่าวถึงข้างต้น ขาวและดำมีการเคลื่อนไหวที่เป็นไปได้ 20 ครั้งในการเคลื่อนไหวครั้งแรกและในครั้งที่สองและอื่น ๆ
สรุป
ไม่มีคำตอบที่ถูกต้องสำหรับคำถามนี้ มีหลายแนวทางที่เป็นไปได้ซึ่งข้างต้นเป็นเพียงบางส่วน
สิ่งที่ฉันชอบเกี่ยวกับเรื่องนี้และปัญหาที่คล้ายกันคือต้องการความสามารถที่สำคัญสำหรับโปรแกรมเมอร์เช่นการพิจารณารูปแบบการใช้งานการกำหนดความต้องการอย่างถูกต้องและการคิดเกี่ยวกับกรณีมุม
ตำแหน่งหมากรุกนำมาเป็นหน้าจอจากหมากรุกตำแหน่งเทรนเนอร์