Sapyor o'yinini "o'ynaymiz" :)
Bekorchilikda papkalar aro sayohat qilib o’tirib, tasodifan windows tarkibidagi sapyor o’yiniga kirib qoldim (winmine.exe). Ushbu o’yinni ko’pchilik hech bo’lmaganda bir marta o’ynab ko’rgan albatta. Oddiy mantiq va tezkorlik talab qiladigan o’yin. Bu o’yinni biz bir vaqtlar talabalikda sheriklarim bilan kim tezroq tugatishdan musobaqa ham o’ynar edik. Hullas hozir bu o’yinni yana ko’rdimu, o’sha paytdagi bir gap yodimga tushib ketdi. Dasturlashni endi o’rganayotgan paytimda ushbu o’yinni dastur orqali o’ynaydigan dasturcha qilgandim. Qarangki o’sha dastur hali ham arxivimda turgan ekan. Dastur kodlariga qarab kayfiyatimni ham ko’tarib oldim. Hozir aytadigan gaplarim shu dasturcha haqida. Dasturdan ko’ra uni qanday qilinganligi qiziqroq bo’lsa kerak menimcha.
Dastur Visual Basicda tuzilgan bo’lib, quyidagicha ishlar edi:
Sapyor oynasini izlash uchun Windows API tarkibidagi FindWindow funksiyasidan foydalanamiz. Ushbu funksiya ochiq turgan oynalarni sarlavha va/yoki klassi bo’yicha izlaydi va natija sifatida topilgan oyna xendlini qaytaradi. Topilgan xendldan oyna bilan ishlovchi boshqa ko’pgina funksiyalarda foydalanish mumkin. Xendl sodda qilib aytganda oynaning shaxsiy, unikal nomeri desak ham bo’laveradi.
Ushbu funksiyani dasturda e’lon qilish uchun DllImport attributidan foydalanamiz. DllImport dasturga funksiyani qaysi tashqi kutubxonadan ekanligini bildirish va ba’zi qo’shimcha attributlarini berish uchun ishlatiladi.
Demak, sapyor oynasini izlaymiz:
Funksiyada berilayotgan birinchi argument – izlanayotgan oynani klassi (bizning misolda har qanday nomga ega bo’lgan klass so’ralyapti, ya’ni qiymat null, ikkinchisi esa oyaning sarlavhasidagi matni (caption). Agar shularga mos oyna topilmasa, unda funksiya 0 qiymat qaytaradi.
Maydon holatini o’qishni qanday amalga oshiraman deb turganimda bir fikr kelib qoldi: Maydon holatini undagi piksellar rangidan aniqlash. Har bir katakning burchagi va markazidagi piksel rangidan uning holatini aniqlash mumkin ekan.
Avvalo oynadan maydon joylashgan qismni ajratib olamiz (rasmda qizil chiziq bilan belgilangan). Ushbu qism eni va bo’yini 16 ga (katak o’lchami shuncha pikseldan iborat) bo’lsak maydondagi ustun va qatorlar soni kelib chiqadi.
Sapyor oynasidagi tasvirni o’qish uchun avvalo oynaning kontekstini (Device Context) aniqlab so’ng kerakli sohani tasviri ma’lumotlarini olish mumkin bo’ladi.
Oynaning DC sini olishga GetDC funksiyasi yordam beradi. Oynaning xendli uning butun oynaning parametrlarini o’qish va o’zgartirish uchun kerak bo’lsa, DC ni oynaning chiziluvchi klient(ishchi) sohasini ifodalovchi identifikator deb ham tushunish mumkin. Barcha chiqarish qurilmalariga ma’lumot uzatish uning konteksti orqali amalga oshiriladi.
Oldin tashqi kutubxonalardan ishlatadigan funksiyalarni e’lon qilib olib, so’ng oynaning kontekstini aniqlaymiz:
Endi oynaning o’lchamlarini olamiz:
GetPixel funksiyasi berilgan konteksdagi tasvirdan berilgan koordinataga mos nuqtaning rangini olish uchun foydalaniladi. Tasvirdan har bir katak markazidagi va burchagidagi pikselning rangi yordamida maydon holatini “o’qiymiz”:
Olingan maydon holatiga qarab qayerga bayroq qo’yish yoki qayerni ochishni (mina yo’q joyni albatta) hal qilsak bo’ldi. Buni yozib o’tirmayman, dasturni ko’chirib olib ko’rish mumkin.
Va nihoyat ohirgi amal – sapyor dasturi oynasida sichqonchani bosish amali. Bunda bizga SendMessage funksiyasi yordam beradi. SendMessage oynalarga habar jo’natish funksiyasi bo’lib, ushbu funksiya yordamida OS va dasturlar, dasturlar va dasturlar o’zaro ma’lumot almashishi, habarlashishda foydalanadi. Bizga kerakli, sichqoncha holati haqidagi habarlar quyidagilardan iborat:
SendMessage funksiyasi:
Bu yerda: hWindow – habar jo’natiladigan oyna xendli, msg – habar (masalan WM_LBUTTONDOWN), wParam va lParam – habarga bog’liq ma’lumotlar.
Yuqoridagi habarlar uchun wParam – sichqoncha tugmasini bildiradi (MK_LBUTTON — chap tugma, MK_RBUTTON — o’ng tugma), lParam – sichqoncha koordinatasi, kichik 4 bayt – x, katta 4 bayt – y ni berish uchun.
Endi dasturni ishlatib natijasini kuzatishimiz mumkin.
Mana shunaqa gaplar. Umuman olganda bu narsa arzimagan oddiy narsa bo’lib tuyulishi mumkin. Biroq boshlang’ich dasturchi bo’lgan odam – menga, o’sha paytda bu narsa ancha qiziq va muammoli bo’lgan edi. Shuningdek dasturlashni kichikroq, o’yinsifat dasturlar tuzish misolida o’rganish jarayonni ancha qiziqarli qiladi manimcha.
Dastur kodi: WinminePlay.zip (94 kb)
VB dagi kod: WinminePlay_vb.zip (4 kb)
Dastur Visual Basicda tuzilgan bo’lib, quyidagicha ishlar edi:
- Ochiq turgan (ishlayotgan) Sapyor dasturini oynasini topish;
- Topilgan oynadan maydon holatini (hozirda qaysi kataklar ochilgan, qaysi biriga bayroq qo’yilgan kabi) o’qish;
- Maydon holatiga qarab, mina bor bo’lishi mumkin bo’lgan va bo’sh bo’lishi aniq bo’lgan joylarni aniqlash;
- Va Sapyor oynasida kerakli kataklarda sichqonchaning kerakli tugmalarini bosish;
Sapyor oynasini izlash uchun Windows API tarkibidagi FindWindow funksiyasidan foydalanamiz. Ushbu funksiya ochiq turgan oynalarni sarlavha va/yoki klassi bo’yicha izlaydi va natija sifatida topilgan oyna xendlini qaytaradi. Topilgan xendldan oyna bilan ishlovchi boshqa ko’pgina funksiyalarda foydalanish mumkin. Xendl sodda qilib aytganda oynaning shaxsiy, unikal nomeri desak ham bo’laveradi.
Ushbu funksiyani dasturda e’lon qilish uchun DllImport attributidan foydalanamiz. DllImport dasturga funksiyani qaysi tashqi kutubxonadan ekanligini bildirish va ba’zi qo’shimcha attributlarini berish uchun ishlatiladi.
[DllImport("user32.dll")]
public static extern IntPtr FindWindow (String className, String windowName);
Demak, sapyor oynasini izlaymiz:
hWindow = FindWindow(null, "Сапер");
Funksiyada berilayotgan birinchi argument – izlanayotgan oynani klassi (bizning misolda har qanday nomga ega bo’lgan klass so’ralyapti, ya’ni qiymat null, ikkinchisi esa oyaning sarlavhasidagi matni (caption). Agar shularga mos oyna topilmasa, unda funksiya 0 qiymat qaytaradi.
Maydon holatini o’qishni qanday amalga oshiraman deb turganimda bir fikr kelib qoldi: Maydon holatini undagi piksellar rangidan aniqlash. Har bir katakning burchagi va markazidagi piksel rangidan uning holatini aniqlash mumkin ekan.
Avvalo oynadan maydon joylashgan qismni ajratib olamiz (rasmda qizil chiziq bilan belgilangan). Ushbu qism eni va bo’yini 16 ga (katak o’lchami shuncha pikseldan iborat) bo’lsak maydondagi ustun va qatorlar soni kelib chiqadi.
Sapyor oynasidagi tasvirni o’qish uchun avvalo oynaning kontekstini (Device Context) aniqlab so’ng kerakli sohani tasviri ma’lumotlarini olish mumkin bo’ladi.
Oynaning DC sini olishga GetDC funksiyasi yordam beradi. Oynaning xendli uning butun oynaning parametrlarini o’qish va o’zgartirish uchun kerak bo’lsa, DC ni oynaning chiziluvchi klient(ishchi) sohasini ifodalovchi identifikator deb ham tushunish mumkin. Barcha chiqarish qurilmalariga ma’lumot uzatish uning konteksti orqali amalga oshiriladi.
Oldin tashqi kutubxonalardan ishlatadigan funksiyalarni e’lon qilib olib, so’ng oynaning kontekstini aniqlaymiz:
IntPtr hDC = GetDC(hWindow);
Endi oynaning o’lchamlarini olamiz:
// Oynaning client sohasi o'lchamlarini olish
RECT rc = new RECT();
GetClientRect(hWindow, ref rc);
// client sohasi o'lchamlaridan keraksiz sohalar
// uzunligini ayirib tashlash, ya'ni maydon chekkalarini
int cw = rc.right - rc.left - 12 - 8;
int ch = rc.bottom - rc.top - 55 - 8;
GetPixel funksiyasi berilgan konteksdagi tasvirdan berilgan koordinataga mos nuqtaning rangini olish uchun foydalaniladi. Tasvirdan har bir katak markazidagi va burchagidagi pikselning rangi yordamida maydon holatini “o’qiymiz”:
// Maydon holatini piksellar rangiga asoslanib "o'qish"
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
// client oynasidagi koord.ni hisoblash
int x = j * 16 + 12;
int y = i * 16 + 55;
// katak qirg'og'idagi ramkadan katak ochilgan
// yoki ochilmaganligi aniqlanadi:
if (GetPixel(hDC, x, y) == 0x808080) // ochilgan katak
{
// katak markazidagi nuqta rangi olinib,
// shunga asoslanib, katakda nima joylashganligi
// aniqlanadi
int color = GetPixel(hDC, x + 8, y + 8);
switch (color)
{
// ochilgan bo'sh joy
default: field[j, i] = 0;
break;
// 1
case 0xff0000: field[j, i] = 1;
break;
// 2
case 0x8000: field[j, i] = 2;
break;
// 3
case 0xff: field[j, i] = 3;
break;
// 4
case 0x800000: field[j, i] = 4;
break;
// 5
case 0x80: field[j, i] = 5;
break;
// 6
case 0x808000: field[j, i] = 6;
break;
// portlagan mina
case 0: field[j, i] = -1;
MessageBox.Show("Portlab ketibdiku :)");
return;
}
}
else // hali ochilmagan katak
{
int color = GetPixel(hDC, x + 8, y + 6);
switch (color)
{
default: field[j, i] = 256;
break;
// bayroq qo'yilgan
case 0xff: field[j, i] = 128;
break;
}
}
}
}
Olingan maydon holatiga qarab qayerga bayroq qo’yish yoki qayerni ochishni (mina yo’q joyni albatta) hal qilsak bo’ldi. Buni yozib o’tirmayman, dasturni ko’chirib olib ko’rish mumkin.
Va nihoyat ohirgi amal – sapyor dasturi oynasida sichqonchani bosish amali. Bunda bizga SendMessage funksiyasi yordam beradi. SendMessage oynalarga habar jo’natish funksiyasi bo’lib, ushbu funksiya yordamida OS va dasturlar, dasturlar va dasturlar o’zaro ma’lumot almashishi, habarlashishda foydalanadi. Bizga kerakli, sichqoncha holati haqidagi habarlar quyidagilardan iborat:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_RBUTTONDOWN
WM_RBUTTONUP
SendMessage funksiyasi:
SendMessage ( hWindow, msg, wParam, lParam);
Bu yerda: hWindow – habar jo’natiladigan oyna xendli, msg – habar (masalan WM_LBUTTONDOWN), wParam va lParam – habarga bog’liq ma’lumotlar.
Yuqoridagi habarlar uchun wParam – sichqoncha tugmasini bildiradi (MK_LBUTTON — chap tugma, MK_RBUTTON — o’ng tugma), lParam – sichqoncha koordinatasi, kichik 4 bayt – x, katta 4 bayt – y ni berish uchun.
// Sapyor oynasiga berilgan koord.da sichqonchaning
// o'ng tugmasi bosilgani habarini jo'natish (bayroq qo'yish).
private void buttonClickR(int col , int row)
{
// client oynasidagi koord.ni hisoblash
int x=col*16+12;
int y=row*16+55;
SendMessage (
hWindow,
(int)Const.WM_RBUTTONDOWN,
(int)Const.MK_RBUTTON,
(y << 16) + x);
SendMessage(
hWindow,
(int)Const.WM_RBUTTONUP,
(int)Const.MK_RBUTTON,
(y << 16) + x);
}
Endi dasturni ishlatib natijasini kuzatishimiz mumkin.
Mana shunaqa gaplar. Umuman olganda bu narsa arzimagan oddiy narsa bo’lib tuyulishi mumkin. Biroq boshlang’ich dasturchi bo’lgan odam – menga, o’sha paytda bu narsa ancha qiziq va muammoli bo’lgan edi. Shuningdek dasturlashni kichikroq, o’yinsifat dasturlar tuzish misolida o’rganish jarayonni ancha qiziqarli qiladi manimcha.
Dastur kodi: WinminePlay.zip (94 kb)
VB dagi kod: WinminePlay_vb.zip (4 kb)
7 комментариев