ساخت یک ربات نمونه برای IWOR
(ویرایش پایتون)

نویسنده: آیدین غریب‌نواز

فهرست


۱. مقدمه

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

این متن نحوه ساخت یک ربات ساده را برای Imperfect world of Robots توصیف می‌کند. این ربات به طور اتفاقی به اطراف حرکت کرده و سعی می‌کند تا کریستال‌هایی که در اطرافش قرار دارد را شناسایی کند. این ربات هوشمند نبوده و نمی‌تواند بیش از یک دقیقه در این دنیای بی‌رحم زنده بماند! اما با دنبال کردن این آموزش می‌توانید نحوه کار IWOR را درک کنید و ایده‌هایی برای ساخت ربات‌های خود بدست آورید.

کد کامل این ربات را می‌توانید در دایرکتوری '(sample/robots/Tutorial(Python' پیدا کنید.

توجه: کلیه کدهای درون این متن آموزشی و همچنین کدهای درون دایرکتوری '(sample/robots/Tutorial(Python' تحت قوانین اجازه نامه عمومی گنو (GPL) منتشر شده‌اند. مطابق قوانین این اجازه نامه اگر شما از این کدها در ربات خود استفاده کنید، باید کدهای ربات خود را نیز تحت قوانین GPL منتشر کنید. به این معنی که دیگران اجازه ویرایش کد و انتشار مجدد آن را خواهند داشت. اگر چنین چیزی را نمی‌خواهید، لطفا از کدهای به کار رفته در این آموزش استفاده نکنید.


۲. اتصال به سرور

در ابتدا ربات باید به سرور شبیه ساز متصل شود. به طور پیش فرض، سرور به پورت ۷۲۰۰ گوش کرده و منتظر اتصال ربات‌ها می‌ماند. کاری که باید انجام شود باز کردن یک socket و اتصال آن به این پورت است.

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

import sys
from socket import *

server = socket(AF_INET, SOCK_STREAM) #Creating a socket.
server.connect(('localhost', 7200))   #Connecting to the server.

این کد یک socket ساخته و به سرور شبیه ساز که بر روی همان دستگاه (localhost) اجرا شده است، متصل می‌شود. حال با استفاده از متغییر server می‌توانیم به سرور شبیه ساز پیغام ارسال کرده و پیام‌های آن را دریافت کنیم.


۳. آغاز بازی

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

import sys
from socket import *

server = socket(AF_INET, SOCK_STREAM) #Creating a socket.
server.connect(('localhost', 7200))   #Connecting to the server.

#Initilizing ...
server.send('INIT&1&Pitiless Killer&0&')
serverMessage = server.recv(256)
while (serverMessage != '255'):
   server.send('INIT&1&Tutorial&0&')
   serverMessage = server.recv(256)

دستور INIT سه آرگومان دارد. '1' شماره قبیله‌ای است که این ربات قصد پیوستن به آن را دارد. 'tutorial' نام ربات است و '0' کدی است که ربات باید به سرور ارسال کند تا اجازه حضور در دنیا را بدست آورد. در ابتدای بازی تعداد محدودی کد صفر وجود دارد. بعد از اتمام این کدها، ربات باید کد جدید را از والد خود کسب کند. بنابراین ایده خوبی است که قابلیتی را به این ربات اضافه کنید تا بتواند با استفاده از آرگومان‌های خط فرمان این کد را دریافت کند. در این صورت والدین می‌توانند به راحتی کدهایی را که از سرور دریافت کرده‌اند به فرزندان خود منتقل کنند. در این متن آموزشی ما در رابطه با نحوه تکثیر ربات‌ها بحث نخواهیم کرد. برای اطلاعات بیشتر به کتابچه راهنمای IWOR مراجعه کنید.

شیء server دو متد مهم دارد: send و recv. متد send یک آرگومان دریافت کرده و آن را از طریق اتصال برقرار شده به سرور ارسال می‌کند. recv برای دریافت پیغام‌های سرور استفاده می‌شود. تنها آرگومانی که دریافت می‌کند طول پیغام است. در بدترین حالت طول پیغام سرور از ۲۵۶ بایت بیشتر نخواهد شد.

دلیل استفاده از حلقه while این است که گاهی اوقات سرور نمی‌تواند ربات را در فضای شبیه سازی شده قرار دهد. به عنوان مثال هنگامی که فضای خالی موجود نباشد. در این صورت پیغام 190 دریافت خواهد شد و ربات باید دوباره دستور INIT را ارسال کند. در صورت موفقیت 255 دریافت می‌شود. این ربات آنقدر دستور INIT را ارسال می‌کند تا موفق به ورود به بازی شود.

اکنون ربات ما به این دنیا وارد شده است! قدم بعدی حرکت کردن به اطراف است.


۴. حرکت اتفاقی به اطراف

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

import random

def randomMove():
   rndint = (int)(random.random() * 4)
   if rndint == 0:
      return 'MOVE&NORTH&'
   elif rndint == 1:
      return 'MOVE&EAST&'
   elif rndint == 2:
      return 'MOVE&SOUTH&'
   else:
      return 'MOVE&WEST&'

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

movingCommand = 'MOVE&EAST&'

#Moving around ...
while (True):
   server.send(movingCommand)
   serverMessage = server.recv(256)
   if (serverMessage != '255'):
      #I hit a wall or something, changing the direction.
      movingCommand = randomMove()

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


۵. پیدا کردن کریستال در اطراف ربات

حال که ربات ما می‌تواند به اطراف حرکت کند، می‌خواهیم قابلیت تشخیص کریستال را به آن اضافه کنیم. ربات‌ها می‌توانند با ارسال دستور SENSE به سرور شبیه ساز از اشیاء اطراف خود مطلع شوند. نتیجه ارسال این دستور چیزی همانند &#,3,5&C,4,6 است و به این معنی است که دیواری در موقعیت (۳و۵) و کریستالی در (۴و۶) قرار دارد. همانطور که ملاحظه می‌کنید اشیاء با '&' و موقعیت آنها با ',' از یکدیگر جدا شده‌اند. بنابراین تنها کاری که باید انجام دهیم این است که پیغام را از این نقاط جدا کرده و برسی کنیم که آیا کریستالی در آن وجود دارد یا خیر. کد زیر این کار را انجام خواهد داد:

def seekCrystal(senseResult):
   index = 0
   lastAmpPosition = 0
   result = ['','']

   while index < len(senseResult):
      if senseResult[index]=='&':
         if (senseResult[index-1]=='C'):
            #I found a crystal!
            i = lastAmpPosition
            j = lastAmpPosition
            k = 0
            while (i < index-1):
               if (senseResult[i]==','):
                  result[k] = senseResult[j+1:i]
                  k=k+1
                  j = i
               
               i = i+1
            
         lastAmpPosition = index

      index = index+1

   return result

این کد آنقدر که به نظر می‌رسد پیچیده نیست. در ابتدا، کاراکترهای '&' را پیدا خواهد کرد و برسی می‌کند که آیا کاراکتر قبلی آن یک کریستال است یا خیر:

   while index < len(senseResult):
      if senseResult[index]=='&':
         if (senseResult[index-1]=='C'):
            #I found a crystal!
            .
            .
            .
         lastAmpPosition = index

      index = index+1

اگر کریستالی درون رشته دریافتی بود، ما موقعیت آن را احتیاج داریم. تنها کاری که باید انجام شود پیدا کردن ',' درست قبل از کاراکتر 'C' است:

   while (i < index-1):
      if (senseResult[i]==','):
         result[k] = senseResult[j+1:i]
         k=k+1
         j = i
                  
      i = i+1

سپس موقعیت کریستال درون متغییر result قرار گرفته و به عنوان نتیجه بازگردانده می‌شود.


۶. سخن آخر

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


Copyright (c) 2008 Aidin Gharibnavaz
کلیه حقوق برای آیدین غریب‌نواز محفوظ است. این سند تحت اجازه‌نامه مستندات آزاد گنو منتشر می‌شود. شما آزاد هستید تا این سند را ویرایش کرده و یا تحت قوانین مستندات آزاد گنو دوباره منتشر کنید. نسخه‌ای از این اجازه‌نامه در فایل FDL به همراه این مستند وجود دارد.

رنگ بندی کد‌ها در این صفحه با استفاده از نرم افزار Web C Plus Plus نسخه 0.8.4 انجام شده است.
Jeffrey Bakker دارنده حق کپی‌رایت webcpp بوده و آن را تحت قوانین GPL منتشر می‌کند.