پیاده سازی هوش مصنوعی شطرنج – قسمت دوم – بازی tic-tac-toe

پیاده سازی هوش مصنوعی شطرنج – قسمت دوم – بازی tic-tac-toe

پیاده سازی هوش مصنوعی شطرنج – قسمت دوم – بازی tic-tac-toe
پیاده سازی هوش مصنوعی شطرنج – قسمت دوم – بازی tic-tac-toe

tic-tac-toe یا دوز رو احتمالا خیلی از دوستان بشناسند، یک بازی پرطرف دار قدیمی است که مکانیسم ساده ای دارد. این بازی ۲ نفره است و در آن بازیکن ها سعی می‌کنند سه مهره خود را به ردیف در جایگاه ها بنشانند و در این صورت پیروز خواهند بود. ردیف های افقی یا عمودی یا مورب فرقی نخواهد داشت و هر کدام باعث پیروزی در بازی خواهد شد. حریف سعی خواهد کرد با قرار دادن مهره های خود جلوی ردیف شدن ۳ مهره از شما را بگیرد. در قسمت اول از آموزش هوش مصنوعی شطرنج الگوریتم minmax را توضیح دادیم و به شکل فرضی نحوه عملکرد آن را توضیح دادیم. در این قسمت سعی خواهیم کرد به شکل عملی با پیاده سازی عملی بازی دوز (tic-tac-toe) نشان دهیم چطور می‌توان با استفاده از این الگوریتم یک رقیب خوب (در واقع رقیبی که حرکات حساب شده دارد) پیاده سازی کرد. با ما همراه باشید!

 

مختصری در باره بازی tic-tac-toe

بازی tic-tac-toe مدل‌های مختلفی دارد، مدل ۱۲ تایی یا ۶ تایی یا ۹ تایی سری ۱۲ تایی از همه سخت‌تر است و نیاز به تمرکز زیادی دارد، در این آموزش ما ساده‌ترین نوع دوز را پیاده سازی خواهیم کرد! چرا که هم تعداد حالت‌هایی که نیاز به پردازش دارند کم‌تر هستند و مدل برنامه ساده‌تر و قابل‌درک‌تر خواهد بود مخصوصاً برای هدف ما که آشنایی بیشتر با الگوریتم minmax است و هم این که مرسوم‌تر است و احتمالاً اغلب دوستان یک بار این بازی را تجربه کرده‌اند.

 

بازی دوز

همان‌طور که در تصویر مشخص است، در این بازی ۹ جای خالی وجود دارد که هر بازیکن مهره خود را به نوبت در هر کدام از موقعیت‌ها که دوست دارد قرار می‌دهد بازیکن‌ها باید سعی کنند که مهره‌های خود (سه مهره) را در یک راستای عمودی یا افقی یا مورب قرار دهند، در صورت بروز چنین حالتی بازیکن برنده بازی خواهد بود مثل عکس زیر:

تیک تاک تویی نام های دیگری مثل ایکس او یا Noughts and Crosses نیز دارد.

همین‌طور می‌توانید به‌صورت آنلاین آن را بازی کنید.

 

از کجا شروع کنیم؟ زمین!

قرار است با استفاده از الگوریتم minmax یک هوش مصنوعی برای بازی tic-tac-toe پیاده سازی کنیم که حالت‌های ممکن را در نظر گرفته و بهترین حرکت ممکن را انجام دهد، برای این کار اول لازم است که کامپیوتر یا میکروکنترلر از زمین بازی درک درستی داشته باشد، از آنجایی که ما قرار است ۹ مکان برای نگهداری مهره‌ها داشته باشیم برای تعریف زمین بازی می‌توانیم به سادگی از یک آرایه ۳ در ۳ استفاده کنیم، البته روش بهینه‌تری برای ذخیره زمین بازی وجود دارد ولی از آنجایی که مقاله بیشتر جنبه آموزشی دارد ما سعی خواهیم کرد ساده‌ترین حالت ممکن را در نظر بگیریم.

میدانیم که هر خانه از زمین بازی می‌تواند ۳ حالت داشته باشد، یا خالی است یا مهره بازیکن X در آن است یا مهره بازیکن O در خانه‌های آن قرار دارد. پس قبل از تعریف زمین لازم است متغیری را تعریف کنیم که وضعیت هر خانه از زمین را نمایش دهد. برای این کار از enum استفاده می‌کنیم. در نهایت تعریف زمین بازی به شکل زیر خواهد بود.

حالا که زمین بازی را تعریف کردیم، نیاز است که بتوانیم برد را چاپ کنیم (نمایش دهیم) برای این کار هم تابع زیر را به شکلی که می‌بینید می‌نویسم:

فکر می‌کنم تابع فوق عملکردش به اندازه کافی روشن است و نیاز به توضیح خاصی ندارد. این تابع متغیری تحت عنوان زمین بازی از ما دریافت می‌کنید و آن را به شکلی که قابل درک برای ما باشد نمایش می‌دهد. در نهایت تابع خروجی به شکل زیر خواهد داشت.

 

آیا بازی تمام شده است؟

گام بعدی برای پیاده سازی هوش مصنوعی این است که چک کنیم ببینیم آیا بازی تمام شده است یا نه، همان‌طور که قبلاً توضیح داده‌ام در الگوریتم minmax ما باید تمام حالت‌های ممکن را چک کنیم و حالتی که بیشترین امتیاز را نسیب ما می‌کنید را انتخاب کنیم، برای این که بتوانیم تمام حالت‌های ممکن را چک کنیم نیاز است بدانیم آیا زمین بازی جای خالی برای حرکت جدید دارد یا نه! خوب پیاده سازی این تابع هم مثل قبلی‌ها ساده است تنها کافی است چک کنیم و ببینیم آیا موقعیت خالی در زمان بازی وجود دارد یا نه! تابع آن به شکل زیر خواهد بود.

عملکرد تابع بسیار ساده است، تک‌تک خانه‌های بازی را چک می‌کند، اگر خانه‌ای خالی مانده باشد پس هنوز می‌توان حرکت جدیدی انجام داد و به همین دلیل تابع مقدار true برمی‌گرداند.

 

آیا کسی برنده شده است؟

همان‌طور که در قسمت قبل گفتیم، در الگوریتم minmax ما دو بازیکن داریم یکی حداکثرکننده و دیگری حداقل‌کننده، در انجام حرکت‌های مختلفی که هرکدام از بازیکن‌ها انجام می‌دهند باید قادر باشند که بررسی کنند آیا حرکتی که انجام داده‌اند فارغ از این که باعث تمام شدن بازی شده است یا خیر باعث برد یا باخت رقیب شده است یا نه،‌ یعنی نیاز داریم که تابعی داشته باشیم که با بررسی زمین بازی بتواند بگوید کدام بازیکن برنده شده است و یا این که هیچ کدام برنده نشده است.

در واقع این تابع با توجه به وضعیت مهره‌ها ارزیابی می‌کند کدام بازیکن برنده شده است یا این که هنوز بازی هیچ برنده‌ای ندارد، این تابع بسته به نوع بازی به شکل‌های مختلفی نوشته می‌شود و اتفاقاً نوع نوشتن این تابع در هوش نمایش داده شده از برنامه تأثیر مستقیم دارد. ایده اولیه پشت این تابع آن است که برای حداکثرکننده مقدار مثبت خروجی دهد و برای بازیکن حداقل‌کننده مقدار منفی، و اگر هیچ کدام از بازیکن‌ها برنده نبودند خروجی صفر (بی طرف) برگرداند. در این سناریو ما فرض می‌کنیم که بازیکن X یک بازیکن حداکثرکننده است و بازیکن O بازیکن حداقل‌کننده است. خوب بیایید تابع رو بنویسیم:

 

۱. اگر بازیکن X (حداکثر کننده) برنده بازی باشد تابع ما مقدر مثبت ۱۰ را بر می‌گرداند.

۲. اگر بازیکن O (حداقل‌کننده) برنده بازی باشد تابع ما مقدار منفی ۱۰ را بر می‌گرداند.

 

۳. اگر هیچ کدام از بازیکن‌ها برنده نشده باشند، آنگاه تابع ارزیابی کننده مقدار صفر را بر می‌گرداند

ما می‌توانستم هر مقداری مثبت یا منفی دلخواهی استفاده کنیم تنها برای سادگی مقدار ۱۰ را انتخاب کردیم. تابع بورد ۳ در ۳ را ورودی می‌گیرد و تک تک ردیف‌ها و ستون را بررسی می‌کند و چک می‌کند که آیا بازیکنی موفق شده است سه مهره را پشت سر هم قرار دهد یا نه البته دو ردیف مورب هم وجود دارد که آنها را نیز چک می‌کند.

 

تابع حرکت ساز

تا اینجا فکر می‌کنم همه چیز روشن باشد حالا با توجه به ماهیت عملکردی تابع مینمکس نیاز است که بتوانیم حرکت‌های مختلفی را ایجاد کنیم بعد با استفاده از تابع ارزیابی امتیاز آن حرکت را ارزیابی کنیم. تابع حرکت ساز نقش مهمی در میزان هوش برنامه بازی می‌کند، البته در بازی tic-tac-toe پیاده سازی این تابع خیلی ساده است چرا که هیچ قاعده خاصی برای حرکت مهره‌ها وجود ندارد و تنها کافی است که جای خالی را پیدا کنیم و مهره مورد نظر را در آن قرار دهیم ولی در بازی‌های پیچیده‌تر مثل شطرنج یکی از مهم‌ترین توابعی که وجود خواهد داشت همین تابع است. در بازی شطرنج مهره‌ها باید طبق قاعده بازی حرکت کنند و هر مهره الگو (pattern) حرکتی منحصر به فرد خود را دارد و آنجاست که اهمیت این تابع مشخص می‌شود.

با توجه به این که عملکرد این تابع به عملکرد تابع minmax گره خورده است چرا که باید هر حرکت محتمل را وزن دهی کنیم، تابع را تنها به شکل شبه برنامه پیاده سازی کنیم و در ادامه کد دقیق آن را بنویسیم.

همان‌طور که مشخص است عملکرد این تابع زیاد هم پیچیده نیست، تمام حرکت‌های ممکن را می‌سنجیم و می‌بینیم که کدام حرکت امتیاز بیشتری در بر خواهد داشت که نهایتاً همان حرکت را انجام خواهیم داد.

 

محاسبه امتیاز هر حرکت (minmax)

خوب حالا می‌رسیم به قسمت چالشی هوش مصنوعی، چطور امتیاز یک حرکت را باید حساب کنیم؟، برای این کار نیاز است برنامه به نوعی با خودش بازی را ادامه دهد یعنی به سعی کند عکس‌العمل شما را حدس بزند و ببینید آیا این حرکتی که انجام داده است آیا باعث پیروزی‌اش در بازی می‌شود یا خیر! این کار به شکل تابع برگشتی انجام می‌دهیم مکانیسمی مثل پیاده سازی تابع فاکتوریل!

همان‌طور که میدانید که در الگوریتم مینمکس دو بازیکن وجود دارد یکی در تلاش برای زیاد کردن امتیاز و دیگری برای تلاش برای کم کردن امتیاز است! برای این که بتوانیم به شکل بازگشتی (ساده‌تر) از این تابع استفاده کنیم نیاز نیست دوتابع مختلف بنویسیم تنها کافی است که نوع بازیکن را به شکل پارامتر در ورودی دریافت کنیم. به شبه کد زیر دقت کنید:

همان‌طور که در شبه کد می‌بینید مثلاً برای کاربر حداکثر کننده ما از منفی‌ترین مقدار ممکن شروع به سنجش حرکت‌های ممکن (تابع ایجاد حرکت) می‌کنیم و همیشه به دنبال بیشترین امتیاز ممکن هستیم. در قیمت حداقل کننده از مثبت‌ترین امتیاز ممکن شروع می‌کنیم و به دنبال کم کردن امتیاز هستیم. بعد از انجام یک حرکت مجدداً خود تابع را صدا می‌زنیم برای تخمین حرکت بازیکن مقابل.

بگذارید برای روشن شدن بیشتر عملکرد تابع با تصویر عملکرد شبه کد را نمایش دهیم.

در بالاترین سطر بازیکن حداقل کننده (O) بازی کرده و حالا نوبت بازیکن حداکثر کننده است (X) سه موقعیت خالی وجود دارد که می‌تواند در آنها بازی کند (پایین‌ترین ردیف بازی) پس بازیکن حداکثرکننده (X) مهره خود را در هر موقعیت قرار داده و بررسی می‌کنید در هر کدام از موقعیت‌ها احتمال برد بیشتر است.

حداکثرکننده پس بعد از هر انتخاب تابع minmax را برای حداقل کننده (O) فراخوانی می‌کند و تا او بازی را با قواعد خودش انجام دهد و بعد مجدداً تابع minmax را برای بازیکن حداکثرکننده صدا میزند و این فرایند تا تمام شدن تمام حالت‌های ممکن ادامه پیدا می‌کند و امتیازها محاسبه می‌شود و از بین امتیازها حرکتی انتخاب می‌شود که بالاترین امتیاز را داشته باشد.

 

کمی هوشمندتر

به مسائل بالا دقت کنید، اگر بازیکن حداکثرکننده (X) مهره خودش رو وسط یا سمت راست بذار توی هر کدام از حالت‌ها امکان برنده شدن هست، یعنی امتیازش میشه +۱۰ ولی اگر وسط رو انتخاب کنه به احتمال ۵۰ درصد ممکنه بازی رو نبره! و بازی مساوی بشه که اون ۵۰ درست بستگی به نحوه بازی حداقل‌کننده (O) داره، یا این که بازی طوری پیش بره که توی هر دو احتمال امکان برنده شدن وجود داشته باشه و هیچ مساوی‌ای هم در کار نباشد ولی برای برنده شدن در یک حالت اول نیاز به ۲ حرکت باشه در حالت دوم نیاز به ۴ حرکت باشد! منطقی‌تر هست که با ۲ حرکت بازی را برنده شویم به جای ۴ حرکت، اما چطور می‌شود چنین چیزی را پیاده سازی کرد؟ ساده است اینجا ما می‌توانیم تعداد حرکات رو در الگوریتم محاسبه امتیاز در نظر بگیریم هر حرکت اضافه (عمق بیشتر) امتیاز منفی در بر خواهد داشت، که به عبارتی می‌شود برای حداکثرکننده (X) و امتیاز مثبت خواهد داشت برای حداقل کننده (O) با این شرایط برنامه سعی خواهد کرد در سریع‌ترین حالت ممکن بازی را برنده شود.

در انتها برنامه به شکل زیر خواهد بود 

 

سورس کد برنامه را برای STM32f030f4 می‌توانید از گیت هاب سیسوگ دانلود کنید.

 

با تشکر از آقای پویا فردوسی که زحمت کشیدن و این الگوریتم رو با استفاده از پایتون بازنویسی کردند. برای مشاهده سورس کد پایتون بازی ایکس او کلیک کنید.

1 نفر

پــــســنــدیـده انـد

توجه

Zeus ‌
Zeus ‌

زئوس هستم ساکن المپ

دیدگاه ها

11 دیدگاه

  • مصطفی
    ۸ تیر ۱۴۰۱

    چرا حریف هوشمند بازی نمیکنه فقط الکی خونه هارو پر میکنه

    • Zeus ‌
      Zeus ‌
      ۸ تیر ۱۴۰۱

      به اندازه کافی هوشمند بازی میکنه 🙂

      • مصطفی
        ۹ تیر ۱۴۰۱

        کجاش هوشمنده 2 بار تست کردم فقط به ترتیب خونه هارو پر میکنه

        • Zeus ‌
          Zeus ‌
          ۹ تیر ۱۴۰۱

          خوب دقیقا چه انتظاری دارید ؟
          طبق الگوریتم توضیح داده شده کار میکنه دیگه در ادامه اگر فرصت باشه روشهای بهینه کردن رو میگم که به هوش مصنوعی مورد نظرمون نزدیک تر بشیم

  • علی
    ۱۶ فروردین ۱۴۰۱

    سلام عالی بود
    از چه محیطی برای برنامه نویسی استفاده باید کرد برای این آموزش

    • Zeus ‌
      Zeus ‌
      ۱۶ فروردین ۱۴۰۱

      سلام دوست عزیز
      من برای برنامه نویسی از eclipse استفاده می‌کنم

  • پویا فردوسی
    پویا فردوسی
    ۹ بهمن ۱۴۰۰

    سلام. من این برنامه را با پایتون پیاده‌سازی کردم و روی گیتهاب گذاشته‌‌ام. می‌خواهم ببینم آیا شما راضی هستید یا حقوق کپی‌رایت و حق مؤلف را نقض کرده‌ام.
    اگر لازم بود به من بگویید تا لینک کد را بدهم.
    همچنین امیدوارم این مطالب را ادامه دهید چون جواب یکی از پیچیده‌ترین سؤالات من هست!

    • Zeus ‌
      Zeus ‌
      ۹ بهمن ۱۴۰۰

      سلام دوست عزیز
      خوشحالم که این مطلب مورد توجه شما قرار گرفته است
      راجب پیاده سازی مجدد قطعا مشکلی نداره اگر لینک به سایت ما بدهید به عنوان رفرنس به شناخته شدن ما هم کمک کرده اید
      بله این مطالب ادامه دارد در فرصت مناسب قسمت های بعدی نیز منتشر خواهد شد.

      • پویا فردوسی
        پویا فردوسی
        ۱۰ بهمن ۱۴۰۰

        بله. لینک داده شده. ممنون

        • Sisoog Os
          Sisoog Os
          ۱۰ بهمن ۱۴۰۰

          ممنون برنامه پایتون را هم اینجا بزارید شاید برای دوستانی کاربردی باشه

پر بحث ترین ها

مسابقه دوم : چالش برنامه نویسی به زبان C

مسابقه دوم : چالش برنامه نویسی به زبان C

مسابقه اول سیسوگ (مسابقه اول: درک سخت افزار) انتقادهای زیادی رو در پی داشت تا جایی که حتی خودمم به نتیجه مسابقه...

Zeus ‌ Zeus ‌
  • 3 سال پیش
راه اندازی LCD گرافیکی Nokia 1661

راه اندازی LCD گرافیکی Nokia 1661

LCD گرافیکی یکی از مهم ترین پارامترهای موجود در طراحی انواع مدارات الکترونیکی پیچیده و حتی ساده است ، نمایش وضعیت و...

Zeus ‌ Zeus ‌
  • 4 سال پیش
ریموت کدلرن و چکونگی دکد کردن آن به همراه سورس برنامه

ریموت کدلرن و چکونگی دکد کردن آن به همراه سورس برنامه

ریموت کنترل امروزه کاربرد زیادی پیدا کرده است؛ از ریموت‌های درب بازکن تا ریموت‌های دزدگیر و کنترل روشنایی همه از یک اصول اولیه پیروی می‌کنند و آن‌هم ارسال اطلاعات به‌صورت بی‌سیم است....

Zeus ‌ Zeus ‌
  • 5 سال پیش
همه چیز درباره ریموت کنترل‌های هاپینگ

همه چیز درباره ریموت کنترل‌های هاپینگ

امنیت همیشه و در همه‌ی اعصار، مقوله‌ی مهم و قابل‌توجه‌ ای بوده و همیشه نوع بشر به دنبال امنیت بیشتر، دست به ابداعات و اختراعات گوناگونی زده است. ریموت کنترل یکی از این اختراعات است. در این مقاله، به بررسی امنیت انواع ریموت‌های کنترل خواهیم پرداخت....

Zeus ‌ Zeus ‌
  • 5 سال پیش
مسابقه سوم: استخراج داده از رشته ها در زبان C

مسابقه سوم: استخراج داده از رشته ها در زبان C

نزدیک به 5 ماه از مسابقه دوم سیسوگ می‌گذره و فکر کردم که بد نیست یک چالش جدید داشته باشیم! البته چالش‌ها...

Zeus ‌ Zeus ‌
  • 2 سال پیش
مسابقه ششم: بزن میکروکنترلر را بسوزون!

مسابقه ششم: بزن میکروکنترلر را بسوزون!

بزنم میکروکنترلر را بسوزونم اونم تو  این شرایط!، طراحی مسابقه از اون چیزی که به نظر می‌رسه سخت‌تر است، باید حواست باشه...

Zeus ‌ Zeus ‌
  • 12 ماه پیش
آموزش قدم به قدم راه اندازی NRF24L01

آموزش قدم به قدم راه اندازی NRF24L01

آموزش قدم به قدم راه اندازی +NRF24L01  با کتابخانه سازگار با انواع میکروکنترلرها و کامپایلرها قبل از اینکه قسمت بشه با ماژول...

رسول خواجوی بجستانی رسول خواجوی بجستانی
  • 3 سال پیش
ساخت ماینر با FPGA و ARM

ساخت ماینر با FPGA و ARM

چند ماهی هست که تب بیت کوین و ارزهای دیجیتال خیلی بالا رفته! چه شد که این پست را نوشتم همانطور که...

Zeus ‌ Zeus ‌
  • 3 سال پیش
کار با ماژول تمام عیار mc60 – قسمت دوم – راه اندازی OpenCPU

کار با ماژول تمام عیار mc60 – قسمت دوم – راه اندازی OpenCPU

در قسمت اول به یکسری اطلاعات کلی ماژول mc60 پرداختیم، با نرم افزار QNavigator کار کردیم و یک هدربرد هم برای کار...

Mahdi.h   Mahdi.h  
  • 3 سال پیش
مسابقه چهارم: کدام حلقه سریع‌تر است؟

مسابقه چهارم: کدام حلقه سریع‌تر است؟

حدود ۷ ماه پیش، مسابقه سوم سیسوگ رو برگزار کردیم و کلی نکته در مورد خواندن رشته‌های ورودی را بررسی کردیم. فکر...

Zeus ‌ Zeus ‌
  • 2 سال پیش
سیـــســـوگ

مرجع متن باز آموزش الکترونیک