Minimax สำหรับ Bomberman


11

ฉันกำลังพัฒนาโคลนเกม Bomberman และฉันกำลังทดลองกับ AI ประเภทต่าง ๆ ก่อนอื่นฉันใช้การค้นหาผ่านพื้นที่ของรัฐด้วย A * และตอนนี้ฉันต้องการลองวิธีที่แตกต่างกับอัลกอริทึม Minimax ปัญหาของฉันคือทุกบทความ minimax ฉันพบว่าผู้เล่นสลับกัน แต่ใน Bomberman ผู้เล่นทุกคนทำการกระทำบางอย่างในเวลาเดียวกัน ฉันคิดว่าฉันสามารถสร้างสถานะที่เป็นไปได้ทั้งหมดสำหรับเกมหนึ่งเห็บ แต่มีผู้เล่นสี่คนและการกระทำพื้นฐาน 5 รายการ (การเคลื่อนไหว 4 ครั้งและการวางระเบิด) มันให้สถานะ 5 ^ 4 ในระดับแรกของทรีเกม ค่านั้นจะเพิ่มขึ้นแบบทวีคูณในทุกระดับ ฉันพลาดอะไรไปรึเปล่า? มีวิธีใดบ้างที่จะนำไปใช้หรือฉันควรใช้อัลกอริทึมที่แตกต่างอย่างสิ้นเชิง? ขอบคุณสำหรับคำแนะนำใด ๆ


1
ในขณะที่นี่เป็นหัวข้อที่ค่อนข้างปิดบังสิ่งหนึ่งที่ฉันชอบเกี่ยวกับ AI ก็คือใช้เป้าหมายหรือบุคลิกภาพสำหรับ AI มันอาจเป็นสิ่งต่าง ๆ เช่นการสะสมพลัง, ไม่ก้าวร้าว, หาทางแก้แค้น, เร่งรีบ ฯลฯ ด้วยเป้าหมายอย่างที่คุณสามารถบอกได้อย่างชัดเจนว่าทิศทางใดที่คุณควรจะเข้าไปและทิ้งระเบิดถ้ามันก้าวหน้าไปถึงเป้าหมาย (ถ้า มันใกล้กับผู้เล่นที่คุณกำลังตามล่าหรือบล็อกที่คุณต้องการทำลาย)
Benjamin Danger Johnson

2
ใช่คุณพลาดบางสิ่ง แต่คุณจะไม่ขอบคุณที่ชี้ให้พวกเขาเห็นเพราะสิ่งเหล่านั้นทำให้แย่ลง ไม่มีการกระทำขั้นพื้นฐาน 5 ประการ บางช่องมี 5 "การเคลื่อนไหว" (4 ทิศทางและยังคงอยู่); อื่น ๆ มี 3 (เนื่องจากถูกบล็อกในสองทิศทาง); โดยเฉลี่ยแล้วมันคือ 4 แต่คุณสามารถทิ้งระเบิดได้ในขณะที่วิ่งดังนั้นโดยเฉลี่ยแล้วปัจจัยการแตกแขนงคือ 8 และคนที่มีการเพิ่มพลังความเร็วสูงสามารถเข้ากับการเคลื่อนไหวได้มากขึ้น
Peter Taylor

ฉันให้คำตอบในคำถามของคุณโดยใช้การค้นหาต้นไม้ monte carlo
SDwarfs

Minimax นั้นไม่มีประโยชน์ในสถานการณ์ที่มีตัวเลือกมากมายเท่ากับ Bomberman คุณจะหมดความสามารถในการค้นหาก่อนที่จะไปไกลพอที่จะดูว่าการเคลื่อนไหวมีเหตุผลหรือไม่
Loren Pechtel

คำตอบ:


8

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

หาก AI นั้นสมบูรณ์แบบผู้เล่นของคุณจะหงุดหงิด อาจเป็นเพราะพวกเขาแพ้หรือคุณได้รับ. 3 เฟรมต่อวินาที

หากมันไม่ฉลาดพอผู้เล่นของคุณจะเบื่อ

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

คุณสามารถปรับเปลี่ยนฟังก์ชันเหล่านี้เพื่อปรับปรุงหรือลดความยากได้ทั้งนี้ขึ้นอยู่กับความยากลำบาก


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

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

4

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

แทนที่จะเป็นเช่นนั้นคุณควรใช้วิธีการเชิงกลยุทธ์มากขึ้น

คุณควรถามตัวเองว่า: ผู้เล่นของมนุษย์ตัดสินใจได้อย่างไรในขณะที่เล่น Bomberman? โดยปกติผู้เล่นควรทำตามลำดับความสำคัญสี่ประการ:

  1. หลีกเลี่ยงพื้นที่ระเบิดของระเบิด
  2. วางระเบิดเพื่อให้คนอื่นไม่สามารถหลีกเลี่ยงบริเวณที่ระเบิดได้
  3. รวบรวม powerups
  4. วางระเบิดเพื่อระเบิดหิน

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

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

สิ่งนี้ควรสร้าง AI การป้องกันที่สมเหตุสมผลแล้ว แล้วความผิดล่ะ?

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


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

4

ฉันคิดว่าฉันสามารถสร้างสถานะที่เป็นไปได้ทั้งหมดสำหรับเกมหนึ่งเห็บ แต่มีผู้เล่นสี่คนและการกระทำพื้นฐาน 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;
}

โครงสร้างต้นไม้ชนิดนี้เร็วกว่ามากเนื่องจากการจัดสรรหน่วยความจำแบบไดนามิกช้ามากจริงๆ! แต่การเก็บแผนผังการค้นหาค่อนข้างช้าเช่นกัน ... ดังนั้นนี่จึงเป็นแรงบันดาลใจมากกว่า


0

มันจะช่วยให้จินตนาการว่าทุกคนผลัดกันหรือไม่

ในทางเทคนิคแล้วในระบบพื้นฐานพวกเขาทำจริง ๆ แต่เนื่องจากสิ่งต่าง ๆ ถูกแทรกและซ้อนทับพวกเขาดูเหมือนจะทำงานพร้อมกัน

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


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