ฉันคิดว่าฉันสามารถสร้างสถานะที่เป็นไปได้ทั้งหมดสำหรับเกมหนึ่งเห็บ แต่มีผู้เล่นสี่คนและการกระทำพื้นฐาน 5 รายการ (การเคลื่อนไหว 4 ครั้งและการวางระเบิด) มันให้สถานะ 5 ^ 4 ในระดับแรกของทรีเกม
แก้ไข! คุณจำเป็นต้องค้นหาการดำเนินการทั้งหมด 5 ^ 4 (หรือแม้กระทั่ง 6 ^ 4 ในขณะที่คุณสามารถเดินไปใน 4 ทิศทางหยุดและ "วางระเบิด"?) การกระทำสำหรับแต่ละเกมติ๊ก แต่เมื่อผู้เล่นตัดสินใจที่จะย้ายมันต้องใช้เวลาสักครู่จนกว่าการย้ายจะดำเนินการ (เช่น 10 เห็บเกม) ในช่วงเวลานี้จำนวนความเป็นไปได้ลดลง
ค่านั้นจะเพิ่มขึ้นแบบทวีคูณในทุกระดับ ฉันพลาดอะไรไปรึเปล่า? มีวิธีใดบ้างที่จะนำไปใช้หรือฉันควรใช้อัลกอริทึมที่แตกต่างอย่างสิ้นเชิง?
คุณสามารถใช้ Hash-Table เพื่อคำนวณสถานะ "ทรีย่อย" ของเกมเดียวกันเพียงครั้งเดียว ลองนึกภาพผู้เล่น A เดินขึ้นและลงในขณะที่ผู้เล่นคนอื่น ๆ "รอ" คุณจะอยู่ในสถานะเกมเดียวกัน เหมือนกับ "ซ้ายขวา" หรือ "ซ้ายขวา" นอกจากนี้การย้าย "ขึ้นแล้วซ้าย" และ "ซ้ายแล้วขึ้น" ผลลัพธ์ในสถานะเดียวกัน ใช้ Hash-Table คุณสามารถ "ใช้ซ้ำ" คะแนนที่คำนวณได้สำหรับสถานะเกมที่ได้รับการประเมินแล้ว ซึ่งจะช่วยลดความเร็วในการเติบโตค่อนข้างมาก ในทางคณิตศาสตร์มันลดฐานของฟังก์ชันการเติบโตแบบเอ็กซ์โปเนนเชียลของคุณ เพื่อให้เข้าใจว่ามันช่วยลดความซับซ้อนได้มากน้อยเพียงใดให้เราดูการเคลื่อนไหวที่เป็นไปได้สำหรับผู้เล่นเพียงคนเดียวเมื่อเทียบกับตำแหน่งที่สามารถเข้าถึงได้บนแผนที่ (= สถานะเกมที่แตกต่างกัน) หากผู้เล่นอาจเลื่อนขึ้น / ลง / ซ้าย / ขวา / หยุด .
ความลึก 1: 5 การเคลื่อนไหว 5 สถานะที่ต่างกัน 5 สถานะเพิ่มเติมสำหรับการเรียกซ้ำครั้งนี้
ความลึก 2: 25 การเคลื่อนไหว, 13 สถานะที่แตกต่าง, 8 สถานะเพิ่มเติมสำหรับการเรียกซ้ำครั้งนี้
ความลึก 3: การเคลื่อนไหว 6125, 25 รัฐที่แตกต่างกัน, 12 สถานะเพิ่มเติมสำหรับการเรียกซ้ำครั้งนี้
หากต้องการเห็นภาพนั้นให้ตอบตัวเองว่า: สามารถเข้าถึงฟิลด์ใดบนแผนที่ได้ด้วยการเลื่อนครั้งเดียวสองครั้งและสามครั้ง คำตอบคือ: ฟิลด์ทั้งหมดที่มีระยะทางสูงสุด = 1, 2 หรือ 3 จากตำแหน่งเริ่มต้น
เมื่อใช้ HashTable คุณจะต้องประเมินแต่ละสถานะของเกมที่สามารถเข้าถึงได้ (ในตัวอย่างของเราที่ 25 ที่ความลึก 3) หนึ่งครั้ง ในขณะที่ไม่มี HashTable คุณจะต้องประเมินหลาย ๆ ครั้งซึ่งจะหมายถึง 6125 การประเมินแทนที่จะเป็น 25 ที่ระดับความลึก 3 ที่ดีที่สุด: เมื่อคุณคำนวณรายการ HashTable คุณสามารถนำมันกลับมาใช้ในขั้นตอนต่อไป ...
นอกจากนี้คุณยังสามารถใช้การเพิ่มความลึกที่เพิ่มขึ้นและการตัดทอนแบบ "ตัด" ที่ไม่คุ้มค่ากับการค้นหาความลึกที่มากขึ้น สำหรับหมากรุกสิ่งนี้จะลดจำนวนของโหนดที่ค้นหาลงไปประมาณ 1% ข้อมูลเบื้องต้นเกี่ยวกับการตัดแต่งกิ่งอัลฟาเบต้าสามารถพบได้ในวิดีโอที่นี่: http://www.teachingtree.co/cs/watch?concept_name=Alpha-beta+Pruning
การเริ่มต้นที่ดีสำหรับการศึกษาต่อคือhttp://chessprogramming.wikispaces.com/Search http://chessprogramming.wikispaces.com/Searchหน้านี้เกี่ยวข้องกับหมากรุก แต่อัลกอริธึมการค้นหาและการปรับให้เหมาะสมนั้นค่อนข้างเหมือนกัน
อัลกอริธึม AI อื่น (แต่ซับซ้อน) - ซึ่งจะเหมาะกับเกมมากขึ้น - คือ "การเรียนรู้ความแตกต่างชั่วคราว"
ความนับถือ
สเตฟาน
PS: ถ้าคุณลดจำนวนสถานะเกมที่เป็นไปได้ (เช่นแผนที่ขนาดเล็กมากระเบิดเพียงครั้งเดียวต่อผู้เล่นไม่มีอะไรอื่น) มีโอกาสที่จะคำนวณการประเมินผลล่วงหน้าสำหรับทุกสถานะของเกม
--edit--
คุณสามารถใช้ผลลัพธ์ที่คำนวณแบบออฟไลน์ของการคำนวณ minimax เพื่อฝึกอบรมเครือข่ายประสาท หรือคุณสามารถใช้มันเพื่อประเมิน / เปรียบเทียบกลยุทธ์ที่ดำเนินการด้วยมือ ตัวอย่างเช่นคุณสามารถใช้ "บุคลิกลักษณะ" ที่แนะนำบางส่วนและฮิวริสติกบางอย่างที่ตรวจพบซึ่งในสถานการณ์ใดกลยุทธ์ที่ดี ดังนั้นคุณควร "จำแนก" สถานการณ์ (เช่นสถานะเกม) สิ่งนี้สามารถจัดการได้โดยเครือข่ายประสาท: ฝึกอบรมเครือข่ายประสาทเพื่อคาดการณ์ว่ากลยุทธ์การเข้ารหัสด้วยมือใดที่เล่นได้ดีที่สุดในสถานการณ์ปัจจุบันและดำเนินการ สิ่งนี้ควรสร้างการตัดสินใจแบบเรียลไทม์ที่ดีมากสำหรับเกมจริง ดีกว่าการค้นหาในระดับความลึกต่ำที่สามารถทำได้เป็นอย่างอื่นเนื่องจากไม่สำคัญว่าการคำนวณแบบออฟไลน์ใช้เวลานานเท่าใด (จะมาก่อนเกม)
- แก้ไข # 2 -
หากคุณคำนวณการเคลื่อนไหวที่ดีที่สุดของคุณทุก ๆ 1 วินาทีคุณสามารถลองทำการวางแผนในระดับที่สูงขึ้นได้ ฉันหมายความว่าอย่างไร คุณรู้ว่าคุณสามารถเคลื่อนไหวได้กี่ครั้งใน 1 วินาที ดังนั้นคุณสามารถทำรายการตำแหน่งที่สามารถเข้าถึงได้ (เช่นถ้าเป็น 3 การเคลื่อนไหวใน 1 วินาทีคุณจะมีตำแหน่งที่สามารถเข้าถึงได้ 25 ตำแหน่ง) จากนั้นคุณสามารถวางแผนเช่น: ไปที่ "ตำแหน่ง x และวางระเบิด" ตามที่คนอื่นแนะนำคุณสามารถสร้างแผนที่ "อันตราย" ซึ่งใช้สำหรับอัลกอริทึมการเราต์ (จะไปยังตำแหน่งที่ x ได้อย่างไรควรเลือกพา ธ ใด [มีความผันแปรที่เป็นไปได้ในกรณีส่วนใหญ่]) นี่เป็นการใช้หน่วยความจำน้อยกว่าเมื่อเปรียบเทียบกับ HashTable ขนาดใหญ่ แต่ให้ผลลัพธ์ที่ดีที่สุดน้อยลง แต่เนื่องจากใช้หน่วยความจำน้อยลงอาจเร็วขึ้นเนื่องจากเอฟเฟกต์แคช (ควรใช้แคชหน่วยความจำ L1 / L2 ของคุณ)
เพิ่มเติม: คุณสามารถทำการค้นหาล่วงหน้าซึ่งมีเพียงการเคลื่อนไหวสำหรับผู้เล่นหนึ่งคนแต่ละคนเพื่อจัดเรียงรูปแบบต่าง ๆ ที่ทำให้เกิดการสูญเสีย ดังนั้นนำผู้เล่นคนอื่น ๆ ออกจากเกม ... จัดเก็บซึ่งผู้เล่นแต่ละคนสามารถเลือกได้โดยไม่เสีย หากมีเพียงการเคลื่อนไหวที่หายไปให้มองหาชุดค่าผสมการเคลื่อนไหวที่ผู้เล่นยังมีชีวิตอยู่เป็นเวลานานที่สุด ในการจัดเก็บ / ประมวลผลโครงสร้างต้นไม้ชนิดนี้คุณควรใช้อาร์เรย์ที่มีดัชนีพอยน์เตอร์ดังนี้:
class Gamestate {
int value;
int bestmove;
int moves[5];
};
#define MAX 1000000
Gamestate[MAX] tree;
int rootindex = 0;
int nextfree = 1;
แต่ละรัฐจะมีการประเมิน "ค่า" และเชื่อมโยงไปยัง Gamestates ต่อไปเมื่อมีการเคลื่อนไหว (0 = หยุด, 1 = ขึ้น, 2 = ขวา, 3 = ลง, 4 = ซ้าย) โดยจัดเก็บดัชนีอาร์เรย์ภายใน "ต้นไม้" ในการเคลื่อนไหว [0 ] เพื่อย้าย [4] เพื่อสร้างต้นไม้ของคุณซ้ำ ๆ สิ่งนี้อาจมีลักษณะเช่นนี้:
const int dx[5] = { 0, 0, 1, 0, -1 };
const int dy[5] = { 0, -1, 0, 1, 0 };
int search(int x, int y, int current_state, int depth_left) {
// TODO: simulate bombs here...
if (died) return RESULT_DEAD;
if (depth_left == 0) {
return estimate_result();
}
int bestresult = RESULT_DEAD;
for(int m=0; m<5; ++m) {
int nx = x + dx[m];
int ny = y + dy[m];
if (m == 0 || is_map_free(nx,ny)) {
int newstateindex = nextfree;
tree[current_state].move[m] = newstateindex ;
++nextfree;
if (newstateindex >= MAX) {
// ERROR-MESSAGE!!!
}
do_move(m, &undodata);
int result = search(nx, ny, newstateindex, depth_left-1);
undo_move(undodata);
if (result == RESULT_DEAD) {
tree[current_state].move[m] = -1; // cut subtree...
}
if (result > bestresult) {
bestresult = result;
tree[current_state].bestmove = m;
}
}
}
return bestresult;
}
โครงสร้างต้นไม้ชนิดนี้เร็วกว่ามากเนื่องจากการจัดสรรหน่วยความจำแบบไดนามิกช้ามากจริงๆ! แต่การเก็บแผนผังการค้นหาค่อนข้างช้าเช่นกัน ... ดังนั้นนี่จึงเป็นแรงบันดาลใจมากกว่า