C Programlama Dilinde Mayın Tarlası

Mayın tarlası tek kişilik bir bilgisayar oyundur. Oyunun amacı bir alanda mayınlara rastlamadan tüm boş kareleri bulmaktır. Karelere tıklayınca çıkan sayılar ise karenin etrafındaki mayın sayısının toplamını gösterir. İlk tıklamada her zaman boşluk denk gelir. Mayın tarlası oyununu sizler için C dili ile kodladık.

# Oyunun Kuralları

Çoğunuz oyunun nasıl oynandığını biliyorsunuzdur; ancak ben oyunun kurallarını oyunu C ile yazmadan önce öğrendim.Bilmeyenler için : Çok sayıda kareden oluşan bir şekil var, bir kare seçiyorsunuz.Eğer mayınlı olanı seçmemişseniz, o karenin komşularından(çevresindeki 8 kare) kaç tanesinde mayın var bilgisini görüyorsunuz.Bazende bir kare açmanıza rağmen birden fazla kare açılıyor.Bu durumda ise açtığınız karenin komşularından hiç birinde mayın yoktur.Bu sebeple o karenin 8 komşusu açılır.Eğer o sekizlinin içerisinde de mayına komşuluğu olmayan kare varsa o da açılır.Bu şekilde tek kare seçmiş olsanız da birden fazla karenin durumunu görmüş olacaksınız.

# Programlama

* Mayın tarlası dediğimiz şeyin matris olacağı açık.Şimdilik 10×10 düşünelim.

* İçerisine rastgele kareler seçerek 15 tane mayın yerleştireceğiz.(Aynı yere gelmemesine dikkat!)

* Mayın komşuluğunu hesaplamak için her karenin 8 komşusunu incelememiz gerekiyor. Örtaki karelerin 8 komşusu var.Ama kenardaki kareler için 8 tane komşu yok.Bu yüzden matrisimizin etrafına bir set kuralım.Onlarında 8 komşusu olsun diye.Birer satır alta ve üste, birer sütun sağa ve sola.Bu durumda tarlamız 12×12 bir matris oldu.

* Her karenin 8 tane komşusuna ulaşmak için aritmetik işlemler gerekiyor.

matris[i-1][j-1] dediğimiz şey matris[i][j] nin sol üst çaprazdaki komşusudur. matris[i-0][j-1] dediğimiz şey matris[i][j] nin soldaki 3 komşusundan ortadaki gibi.

{-1,-0,+1,-1,-0,+1,-1,+1}

{-1,-1,-1,+1,+1,+1,-0,-0}

* Üsteki matris bizim arkada sorgulamalar yaparken kullanacağımız kısım, kullanıcıya gösterilecek kısım için ise yeni bir matris oluşturuyoruz.

* Kullanıcının yapabileceği bir kaç işlem/hareket olduğu için bir menüye ihtiyacımız var.

* Matrislerimiz integer saklayacağı için bazı şeyleri belli sayılarla ifade etmemiz gerekecek.(10 sayısının bayrak(flag) temsil etmesi gibi.)

* Mayın komşuluğu olmayan kareleri açarken, onların içerisinde de mayın komşuluğu olmayan kareler çıkıyordu.Bu durum zaten bu programın en can sıkıcı noktası, (şimdilik) çözümüm şu şekilde : Her sıfır karesiyle karşılaştığımız zaman o karenin satır ve sütun sayısını ard arda olacak şekilde bir diziye ekliyorum.Dizinin neresinde kaldığımı saklayan bir de değişken var.Dizi boyutunun sonuna geldiğinde işlem tamamlanmış oluyor.Yeni kareler diziye eklendikçe dizi boyutu artıyor.

* Oyunun tüm kareleri açılmışsa, kullanıcı mayın olan kareyi açtırmış ise, yeni oyuna başlamak istiyorsa oyun biter.

# Kodlama

* Açıklama yapmaya kalkarsam yazı baya uzun olacak.O yüzden gerekli açıklamaları kod içerisinde yorum olarak ekledim.

Kullanılan C dili özellikleri : 

* Tek ve çok boyutlu diziler

* Fonksiyonlar

* Pointer

* While, do-while, for, if,  elseif, switch-case

# Yapılabilecekler

*Bu şekilde bu oyunu oynamak çok can sıkıcı ve zor oluyor. Tasarımı iyileştirilebilir.

* Sayıları takip etmek, ne olduğunu anlamak zor olabiliyor.Ekrana tarlayı yansıttığım zaman işaretlenmiş mayınlara F(flag) yazdırabiliriz.Sayı yazmaktan daha iyi olacaktır.

* 8 komşuyu açıp onların içinden tekrar açtırma işi üsteki çözümden daha iyi bir çözüm olabilir.

* O çözüm kullanılacaksa bile dinamik bellek yapısı kullanılabilir.Her döngüden sonra sıfırlanabilir.Gerekmeyecek verileri dizide tutmuş oluyoruz.

* Her seçimden sonra tarlayı ekrana alt alta yazdırmamız pek güzel olmuyor.Ekranı sildirip her zaman en üstte tarlanın son halini ve altına menüyü yazdırmamız daha güzel olacaktır.Biraz araştırdım ama etkili, kolay uygulanabilecek bir yöntem bulamadım.

* Mayın tarlasını[matrisi] oluşturduğunda biraz bekleme yapıyor.Bunu çözmek veya kullanıcıyı bilgilendirmek için bir şeyler yazılabilir ekrana.

Not : randNumber() fonksiyonundaki srand(time(NULL)) ifadesini alıp, main fonksiyonunda tanımlamalardan sonraki kısma yazarsak bekleme sorunu çözülecektir.

* Mayınları tutan ve kullanıcıya gösterdiğimiz matrisler aynı boyutta olmak zorunda mı? Kullanıcıya gösterdiğimiz 10×10 boyutunda olabilir.Ama ikisi içinde kullandığım ortak fonksiyonlar var. Öyle yaparak biraz kolaya kaçmış oldum.Düzeltilebilir.


#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int field[12][12];
int seenField[12][12];
int pattern[2][8] = { {-1,-0,+1,-1,-0,+1,-1,+1},
{-1,-1,-1,+1,+1,+1,-0,-0} };
int acilacaklar[100];
int in=0,nerdeyim=0,acilanKare=0;

// Mayınları yerleştirileceği rastgele satır ve sütun sayilari icin
int randNumber(void){
int i;

i = rand() % 9 + 1;
srand(time(NULL));

return i;
}

void initalize(int field[12][12], int input){
int i,j;

for( i=0; i<12; i++){
for( j=0; j<12; j++ ){
field[i][j] = input;
}
}
}

void printMatrix(int field[12][12]){
int i=1,j;

printf("\n 01 02 03 04 05 06 07 08 09 10");
for( i=1; i<11; i++){
printf("\n ----------------------------------------------------------\n%2.d",i);
for( j=1; j<11; j++ ){
printf(" |%2.d| ", field[i][j]);
}
}
printf("\n ----------------------------------------------------------\n");
}

void yerAl(int *row, int *col){

do{
printf("\nSatir ve Sutun sayisini giriniz > ");
scanf("%d%d", row,col);

// Girilen yer bilgisi daha onceden acilmis olabilir
if( seenField[*row][*col] != 11 ){
printf("HATA : Bu kare daha onceden acilmis!\n");
*row = 0; // while şartını devam edecek şekilde etkilemek icin.
}

// girilen row ve col matris boyutlarinin dısında olabilir kontrol!
}while( *row>10 || *row<1 || *col>10 || *col<1);

}

void menu(int *secim){

printf("\n\n");
printf("[1] Kare Ac\n");
printf("[2] Mayin Isaretle\n");
printf("[3] Yeni Oyun\n");
printf("[4] Nasil Oynanır?\n");

// Secim 1,2,3,4 girilene kadar devam et.
do{
printf("Secim > ");
scanf("%d", secim);
}while( *secim>4 || *secim<1 );
}

void ekle(int row, int col){
int i;
// eklenecek yer daha onceden eklenmiş mi?
for( i=0; i<in; i=i+1){
if( acilacaklar[i] == row && acilacaklar[i+1] == col ){
return;
}
}
// Eklenmemiş ise ekle
acilacaklar[in] = row;
in++;
acilacaklar[in] = col;
in++;
}

void kareAc(int acilacaklarIndex){
int i,j,k;

// Acilan karenin mayına komsulugu yoksa komsu 8 kareyi ac.
for( k=0; k<8; k++){
i = acilacaklar[acilacaklarIndex]+(pattern[0][k]);
j = acilacaklar[acilacaklarIndex+1]+(pattern[1][k]);
seenField[i][j] = field[i][j];
acilanKare++;
// Eger acilan karelerden 0 olan varsa onları da "acilacaklara" ekle.
if( field[i][j] == 0){
ekle(i,j);
}
}
// acilacaklar listesinde bulunan nokta.
nerdeyim = nerdeyim + 2;// bir kare actıgımız ıcın 2 ılerlıyoruz
}

void howToPlay(){
printf("\n###############\n");
printf("9 sayisi mayinlari gösterir.\n");
printf("10 sayisi ise mayin isaretlenmis bölgeleri.\n");
printf("1-8 sayilari mayin komsuluk sayisini.\n");
printf("Mayin tarlasi 10x10 boyutlarındadir.\n");
printf("Satir ve sutun sayilarina siz giriyorsunuz.\n");
printf("\n###############\n");
}

int main(){
int i,j,k=0,numberOfMine;
int secim,userRow,userCol;

// Tarlayı sifirla
initalize(field,10); // Duvarlar 10 olsun.
initalize(seenField,11); // 11 acilmamis kareleri gostermek icin

// Mayınları yerleştir.
while( k != 14 ){
i = randNumber();
j = randNumber();
// do not overwrite
if( field[i][j] != 9 ){
field[i][j] = 9; // 9 for mine
k++;
}
}

// Komsu olunan mayın sayısını hesapla
for( i=1; i<11; i++){
for( j=1; j<11; j++ ){
if( field[i][j] != 9 ){
numberOfMine = 0;
for( k=0; k<8; k++){
if( field[i+(pattern[0][k])][j+(pattern[1][k])] == 9 ){
numberOfMine++;
}
}
field[i][j] = numberOfMine;
}
}
}

// Oyun tüm kareler acildiysa veya mayin bulunduysa biter!
// diğer durumlardan biri olusmussa acilanKare = 100 yapıp oyunu sonlandırıyorum.
while ( acilanKare != 100 ){
printMatrix(seenField);

// Get ınput from user
menu(&secim);

switch(secim){
case 1 :
// Bolgeyi kazacaz

yerAl(&userRow,&userCol);

// Eğer bolge mayinliysa oyunu bitir.
if( field[userRow][userCol] == 9 ){
printf("Mayinli bolge oyun bitti!\n\n");
acilanKare = 100; // Oyunu bitir
}
// Eğer mayın değilse ve sıfırdan farklı ise tek kare aciyoruz.
else if( field[userRow][userCol] != 0 ){
// Karenin komsu oldugu mayin sayisini aciyoruz.
seenField[userRow][userCol] = field[userRow][userCol];
acilanKare++;
} else {
// Secilen bolge 0.
seenField[userRow][userCol] = 0;
acilanKare++;
// Kareyi acilacaklar dizisine ekliyoruz
ekle(userRow,userCol);
// Acilacaklar listesindekileri bitirene kadar devam
while( nerdeyim < in ){
kareAc(nerdeyim);
}
}
break;

case 2 :
// Istenen yere bayrak koyacaz.
yerAl(&userRow,&userCol);
// İstenen kareye bayrak yerleştiriyoruz.
seenField[userRow][userCol] = 10; // 10 for flag
acilanKare++;
break;

case 3 :
// Yeni oyun başlat
printf("Yeni oyun baslatilacak!");
return 0;
break;

case 4 :
// oyun nasıl oynanır
howToPlay();
break;
// Default a gerek yok secim alırken gerekli kontroller yapildi!
}// end of switch
} // while in sonu

// Kullaniciya olusturulan mayin tarlasini göster
printf("== Mayın Tarlasi ==\n");
printMatrix(field);
return 0;
}

c programlama dilinde mayın tarlası oyunu yapımı

C ile ilgili örnek bu projeler için bu yazımızı okuyabilirsiniz.

Tahsin ALTINTAŞ

Computer science 4 life mottosuyla yola çıkmış bir bilgisayar mühendisi.

1 Yorum

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir