تبلیغات
Qt Developer Blog - Basic Qt tutorial(mines)-part 1
 
Qt Developer Blog
کیوتی ساده و آسان برای همه
                                                        
درباره وبلاگ

در این وبلاگ کیوتی که مجموعه ای از کلاس ها برای برنامه نویسی دلپذیر تر با سی پلاس پلاس هست آموزش داده میشود
مدیر وبلاگ : سعید دادخواه
نظرسنجی
آموزش ها به چه صورتی باشد






آمار وبلاگ
  • کل بازدید :
  • بازدید امروز :
  • بازدید دیروز :
  • بازدید این ماه :
  • بازدید ماه قبل :
  • تعداد نویسندگان :
  • تعداد کل پست ها :
  • آخرین بازدید :
  • آخرین بروز رسانی :
سه شنبه 9 فروردین 1390 :: نویسنده : علی میریان
سلام خدمت دوستان عزیز. قصد دارم در قالب یک سری مطالب آموزشی (همون toturial خودمون) با نوشتن یک بازی معروف به اسم mine sweeper یا همون mines (که به صورت پیش فرض روی ویندوز هم وجود داره) یک سری از مقدمات کیوت رو خدمتتون ارائه کنم. امیدوارم مفید واقع بشه.

سطح این مطلب آموزشی مقدماتیه( یا شاید به زور(!) متوسط باشه ) و همه ی کسانی که برنامه نویسی شئ گرا بلد باشن و ساختار کیوتی را تا حدودی بدونن(مثلا این که QWidget چیه یا signal و slot چین…) میتونن به راحتی این مقاله ی آموزشی رو بخونن و متوجه بشن.
خب. بهتر زیاد وقت رو تلف نکنیم و یه راست بریم سراغ اصل مطلب. اول صورت مسئله رو به طور دقیق بگم (مخصوصا واسه معدود کسانی که شاید تاحالا مین یاب رو بازی نکرده باشن).


معرفی بازی

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

چطوری برنامشو بنویسیم؟!

خب شاید اولین رویکردی که به ذهنمون برسه اینه که میتونیم برای خونه های صفحه ی بازی یک کلاس boardCell طراحی کنیم که مثلا از QWidget ارث بری می کنه و یک سری مشخصه داره مثل hasMine که تعیین میکنه زیر این خونه مین هست یا نه و ... یک سری تابع(رفتار) هم داره مثلا open() که اون خونه رو باز میکنه و بر حسب اینکه داخلش مین باشه یا نباشه یه آیکن مین و یا یک عدد (که نشون دهنده ی تعداد مین های خونه های همسایه هست) رو نمایش میده... و بعد هم بیایم رویداد(event)های کلیک و ... رو باز نویسی کنیم تا مثلا با کلیک چپ تابع open() صدا زده بشه. و بعد که این کلاس رو به طور کامل پیاده سازی کردیم، کلاس gameBoard رو بنویسیم که این کلاس یه تعدادی boardCell داره و...

اما این راه زیاد بهینه نیست، چون ما میایم و واسه هر خونه ی جدول کلی حافظه میگیریم( یه شی از کلاسی که از کلاس QWidget ارث میبره) و مثلا اگه صفحه ی بازی 100*100 باشه،

10000*sizeof(boardCell) حافظه اشغال میشه. یک راه دیگه میتونه استفاده از Graphic framework باشه، و یه راه بهینه تر اینه که ما کلاسی واسه خونه های صفحه ی بازی در نظر نگیریم و در عوض با رویداد PaintEvent از کلاس gameBoard کار کنیم. و با استفاده از QPainter، بسته به وضعیت خونه های جدول بازی(اینکه کدوم خونه ها باز شدن و اینکه کدوم خونه ها مین دارن و...)، جدول بازی رو نقاشی کنیم. و البته باید رویداد کلیک موس بر روی gameBoard رو مدیریت کنیم. خُب بریم سراغ پیاده سازی جزئیات بیشتر رو در حین پیادی سازی بهشون می پردازیم.

پیاده سازی

خُب اول QtCreator رو باز می کنیم و یه پروژه ی GUI می سازیم. در حین ایجاد پروژه، Base class رو QMainWindow انتخاب کنیم و اسم کلاس اصلی رو هم MinesWindow میگذاریم گزینه ی generate form رو هم تیک می زنیم تا یه فایل ui درست کنه و به برنامه اضافه کنه(بعدا برای ساختن منو های برنامه از UI استفاده خواهیم کرد).
حالا باید یه کلاس برای صفحه ی بازی(همون ناحیه ای که خونه ها هستند) بنویسیم. برای این کار روی پروژه کلیک راست میکنیم و add new رو انتخاب میکنیم تا یه کلاس به پروژه اضافه کنیم. اسم کلاس رو MinesBoard میگذاریم و baseclass رو QWidget انتخاب می کنیم.
خُب بریم سراغ پیاده سازی کلاس MinesBoard
برای اینکه بعدا بتونیم level های مختلفی برای بازی داشته باشیم بهتره که سایز صفحه ی بازی و تعداد مین های اون رو به صورت آرگومان(ورودی) در تابع سازنده ی کلاس بگیریم. البته برای سادگی صفحه رو مربعی در نظر میگیریم. سرآیند و تعریف تابع سازنده ی کلاس رو به صورت زیر تغییر میدیم:

public: MinesBoard(QWidget *parent = 0,int size = 9, int minesCount = 10);
//constructor prototype

به صورت پیش فرض صفحه ی بازی 9*9 و با 10 مین فرض شده است.
دو متغییر size و minesCount به ویژگی های private اضافه میکنیم. و با استفاده از لیست مقداردهی اولیه در تابع سازنده، اون هارو سِت میکنیم:

MinesBoard::MinesBoard(QWidget *parent, int size, int minesCount) :
        size(size),minesCount(minesCount),QWidget(parent){}//constructor
طول هر خونه از جدول رو مقدار ثابتی(بر حسب پیکسل) در نظر میگیریم و بخاطر اینکه تو محاسبات مختلف (که بعدا میبینید) از این عدد استفاده میکنیم، اون رو define# میکنیم که بعدا اگه خواستیم تغییرش بدیم، یک جا تغییر بدیم. پس بالای فایل CELL_LENGTH ،MinesBoard.cpp رو به 20 دیفاین میکنیم.
برای صفحه ی بازی یک آرایه ی دو بعدی در نظر میگیریم. برای هر خونه از این آرایه، اگه مین داشته باشه، مقدارش رو  برابر  -1 (منفی یک) قرار میدیم و در غیر این صورت مقدارش رو برابر تعداد خونه های همسایه که مین دارن قرار میدیم.  چون صفحه ی بازی مربعی هست و طول ضلع مربع از قبل معلوم نیست(داینامیکه) یک int ** (اینت استار استار) به اسم board به عنوان صفتی از کلاس MinesBoard در نظر میگیریم و در تابع سازنده با توجه به size، حافظه برای بورد new  میکنیم. بعد از اون به صورت تصادفی داخل تعدادی از خونه ها مین میگذاریم و بعد اعداد مربوط به سایر خونه ها رو حساب می کنیم. برای محاسبه ی تعداد مین های اطراف یک خونه به مختصات i و j یک تابع به اسم getAroundMineCount می نویسیم. عملکرد این تابع واضحه و با دیدن کد میشه متوجه شد، پس از توضیحش صرف نظر می کنم.
ما نیاز داریم که در هر لحظه بدونیم کاربر تاحالا چیکار کرده، به عبارت دیگه باید وضعیت تک تک خونه ها رو بدونیم. وضعیت خونه میتونه یکی از چهار خالت زیر باشه:
  1. خونه هنوز چک نشده( باز نشده)
  2. خونه باز شده
  3. خونه به عنوان "مین" نشونه گذاری شده
  4. خونه به عنوان "نامعلوم" نشونه گذاری شده
اگه دقت کرده باشید در بازی مین یاب ویندوز، با کلیک راست های متوالی وضعیت یک خونه بین حالت های 1و3و4 تغییر میکنه و با کلیک چپ خونه باز میشه(وضعیت 1).
ما برای نشون دادن وضعیت یک خونه از صفحه ی بازی، یک اینام(enum) به اسم cellState  تعریف میکنیم که همون چهار حالت رو با مقادیر:
  • notChecked
  • checked
  • markedAsMine
  • markedAsUnknown
می پذیره.
حالا برای اینکه در هر لحظه بدونیم وضعیت خونه ها به چه صورتیه، یک آرایه ی دو بعدی از cell state به اندازه ی صفحه ی بازی به اسم currentState در نظر میگیریم و در ابتدای کار وضعیت همه ی خونه ها رو notChecked میکنیم.
خوب الان صفحه ی بازی آمادست، فقط میمونه نحوه ی نمایش صفحه با توجه به وضعیت فعلی، و تغییر وضعیت صفحه از طریق رویداد کلیک، که این هارو در قسمت های بعدی خواهیم دید. فقط یه کار کوچیک دیگه هم توی این قسمت انجام میدیم و اون آزاد کردن حافظه ای که برای board و currentState گرفتیم در مخرب هست. برای این کار تابع مخرب رو مینویسیم و خیلی ساده حافظه هایی که توی سازنده new کردیم رو delete می کنیم.

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




نوع مطلب :
برچسب ها : tutorial، mine، game، part1، intermediate، QT، Minesweeper، step by step turorial، c++،