Yazan : Şadi Evren ŞEKER

Bu yazının amacı, tictactoe oyununu nesne yönelimli olarak kodlamak ve bu sırada, aşağıdaki konuları açıklamaktır.

  1. Nesnelerden oluşan bir dizi kullanımı (Object Array)
  2. Kapsülleme (Encapsulation)
  3. İstisna yakalama (Exception Handling)

Kodumuz basitçe, oynanan her hamleyi 3×3 boyutlarında, iki boyutlu bir dizide tutacaktır. Ayrıca oyunun kazanılması durumunda bir istisna oluşturulacak ve bu istisnanın yakalanması ile, oyun sona erecektir.

Bu problemin çözümü için, aşağıdaki şekilde bir nesne dizisi tasarlıyoruz:

Yukarıdaki şekilde, 3×3 boyutlarında görülen tablo üzerindeki hamleler ayrı birer nesnede tutulmak istenmiştir. Dolayısıyla her hamleyi tutan bir nesne ve bu hamlelerin tanımlandığı bir sınıfa ihtiyacımız bulunuyor.

Bu sınıfı aşağıdaki şekilde kodlayabiliriz.

Hamle.h dosyası

class hamle{

private:

    int sembol; // x:1 veya o:2 değeri

public:

    int getSembol();

    void setSembol(int);

    hamle(int);

    hamle();

};

Kodda bulunan sembol değişkeni, basitçe hamlenin X veya O olmasını belirliyor. Şayet oynanan hamle X ise, değer olarak 1, O ise değer olarak 2 veya boş hamle ise, yani henüz tablonun bu alanına oynanmamış ise 0 değerini tutuyor.

Kapsülleme işlemi için bu değişkeni private tanımlıyor ve getter/setter metotlarını oluşturuyoruz. Ayrıca sınıfımızı kullanacak kişiler için bir adet int parametresi alan yapıcımız (constructor) bulunuyor.

Son olarak boş yapıcı (default constructor) tanımlıyoruz ki, tablo içerisine hamleleri boş olarak yerleştirdiğimizde, boş hamleleri belirten 0 değerini sembol değişkenine atayabilelim.

Bu sınıfın metotları aşağıdaki şekilde kodlanabilir:

Hamle.cpp dosyası:

#include
“hamle.h”

 

int hamle::getSembol(){

    return sembol;

}

void hamle::setSembol(int sembol){

    this->sembol =sembol;

}

hamle::hamle(int sembol){

    this->sembol= sembol;

}

hamle::hamle(){

    sembol = 0;

}

 

Her hamleyi tutmaya yarayan yukarıdaki koda ilave olarak bu hamlelerin oynandığı konumları tutan bir tahta sınıfı kodlamamız gerekiyor.

class Tahta{

private:

    hamle tahta[3][3];

    int sira;

public:

    Tahta();

    void oyna(int x,int y, int sira);

    void oyna(int x,int y);

    void bas(void);

    int kazandi();

};

.Yukarıdaki kodda görüldüğü üzere, 3×3 boyutlarında bir tahta, her elemanı hamle sınıfından değerler tutacak şekilde tanımlanmış. Bu tahta dizisine (array) erişmek için oyna metodu bulunuyor. Basitçe, tahta üzerinde bir adrese herhangi bir hamle konulabilmesi için oyna fonksiyonundaki x ve y değerlerinin verilmesi gerekiyor.

Ayrıca oyna metodu üzerine yükleme (overloading) ile iki farklı şekilde tanımlanmış (polymorphism). Bu sayede, kodumuzu kullanan diğer programcılar, isterlerse o andaki sırayı belirleyerek oynayabilecekler (örneğin istedikleri bir adrese, özel olarak X veya Y değeri koyabilecekler) veya sıradaki hamleyi (örneğin sıra X’te ise doğrudan X değeri) verilen adrese konulabilecek.

Yukarıda, tanımı verilen sınıfın kodlaması aşağıdaki şekildedir:

Tahta::Tahta(){

    sira = 1;

}

 

İnşa metodunun (constructor method) içerisinde, ilk sıranın kimde olduğunu belirtmek için sira değişkenine 1 değeri konulmuştur. Bunun anlamı, şayet programcı özel olarak bir sıra belirtmezse, ilk hamlenin X tarafından yapılacağıdır.

void Tahta::oyna(int x,int y,int sira){

    
 

    if(tahta[x][y].getSembol()==0){

            tahta[x][y] = hamle(sira);

            

        }

        else

            cout << “bu koordinata sembol yerlestirilmistir”<<endl;

}

Tahta üzerinde yapılan hamlelerin koordinatları oyna fonksiyonuna verilmektedir. Yukarıdaki oyna fonksiyonu, aldığı x ve y değerlerine, verilen sıradaki sembolü yerleştirir. Buna göre yeni bir nesne oluşturulup verilen x,y koordinatlarındaki dizi içerisine yerleştirilmektedir.

Aynı fonksiyonu aşağıdaki şekilde üzerine yüklenmesi mümkündür.

void Tahta::oyna(int x, int y){

 

        if(tahta[x][y].getSembol()==0){

            tahta[x][y] = hamle(sira);

            sira %=2;

            sira++;    

            if(kazandi()==1)

                throw kazanma(1);

            else
if(kazandi()==2)

                throw kazanma(2);

 

        }

        else

            cout << “bu koordinata sembol yerlestirilmistir”<<endl;

}

 

Yukarıdaki ikinci haliyle oyna fonksiyonu, sadece x ve y değerleri almaktadır. Bu sayede, sınıf içerisinde bulunan sıraya göre hamle yapılmaktadır. Elbette her hamleden sonra sıranın değiştirilmesi gerekiyor. Bunun için sıra değişkeninin 2ye bölümünden kalan alınıp değeri bir arttırılıyor (sıra değerinin 0 olamayacağını, 0 değerinin oynanmamış alanları temsil ettiğini hatırlayınız).

Yukarıdaki kodda bulunan özel bir durum ise, kazanma koşulunu kontrol etmektedir. Şayet X kazandıysa 1 veya O kazandıysa 2 kontrolü yapılıp ilgili istisna (exception) fırlatılmaktadır.

Bu işlem sırasında kendi yazdığımız istisna sınıfını (exception class) kullanıyoruz. Yani yukarıdaki kodda bulunan kazanma sınıfı bizim kodladığımız bir sınıf. Bu sınıfın kodu aşağıda verilecek.

Son olarak Tahta sınıfında, kazandi kontrolü yapılması için aşağıdakine benzer bir kod yazılabilir:

int Tahta::kazandi(){

    if((tahta[0][0].getSembol()==1 &&tahta[0][1].getSembol()==1 &&tahta[0][2].getSembol()==1 )

        ||(tahta[1][0].getSembol()==1 &&tahta[1][1].getSembol()==1 &&tahta[1][2].getSembol()==1 )

        ||(tahta[2][0].getSembol()==1 &&tahta[2][1].getSembol()==1 &&tahta[2][2].getSembol()==1 )

        ||(tahta[0][0].getSembol()==1 &&tahta[1][0].getSembol()==1 &&tahta[2][0].getSembol()==1 )

        ||(tahta[0][0].getSembol()==1 &&tahta[1][0].getSembol()==1 &&tahta[2][0].getSembol()==1 )

        ||(tahta[0][1].getSembol()==1 &&tahta[1][1].getSembol()==1 &&tahta[2][1].getSembol()==1 )

        ||(tahta[0][2].getSembol()==1 &&tahta[1][2].getSembol()==1 &&tahta[2][2].getSembol()==1 )

        ||(tahta[0][0].getSembol()==1 &&tahta[1][1].getSembol()==1 &&tahta[2][2].getSembol()==1 )

        ||(tahta[0][2].getSembol()==1 &&tahta[1][1].getSembol()==1 &&tahta[2][0].getSembol()==1 ))

        return 1;

    else
if((tahta[0][0].getSembol()==2 &&tahta[0][1].getSembol()==2 &&tahta[0][2].getSembol()==2 )

        ||(tahta[1][0].getSembol()==2 &&tahta[1][1].getSembol()==2 &&tahta[1][2].getSembol()==2 )

        ||(tahta[2][0].getSembol()==2 &&tahta[2][1].getSembol()==2 &&tahta[2][2].getSembol()==2 )

        ||(tahta[0][0].getSembol()==2 &&tahta[1][0].getSembol()==2 &&tahta[2][0].getSembol()==2 )

        ||(tahta[0][0].getSembol()==2 &&tahta[1][0].getSembol()==2 &&tahta[2][0].getSembol()==2 )

        ||(tahta[0][1].getSembol()==2 &&tahta[1][1].getSembol()==2 &&tahta[2][1].getSembol()==2 )

        ||(tahta[0][2].getSembol()==2 &&tahta[1][2].getSembol()==2 &&tahta[2][2].getSembol()==2 )

        ||(tahta[0][0].getSembol()==2 &&tahta[1][1].getSembol()==2 &&tahta[2][2].getSembol()==2 )

        ||(tahta[0][2].getSembol()==2 &&tahta[1][1].getSembol()==2 &&tahta[2][0].getSembol()==2 ))

        return 2;

    else

        return 0;

    

}

 

Yukarıdaki bu kod, 16 ayrı kazanma koşulunu kontrol etmektedir. Kazanan kişinin 1 veya 2 olmasına göre de sonucu döndürmektedir.

Yukarıdaki ikinci oyna metodunda bulunan istisna sınıfı ise aşağıdaki şekilde kodlanabilir:

class kazanma{

private:

    int kim;

public:

    kazanma(int x){

        kim = x;

    }

    int getKim(){

        return kim;

    }

};

 

Son olarak, yukarıdaki kodlarımızı çalıştıracak olan main fonksiyonunu aşağıdaki şekilde kodlayalım:

#include
“hamle.h”

#include
“tahta.h”

#include
<iostream>

#include
<conio.h>

 

using
namespace std;

 

int main(){

 

    Tahta t = Tahta();

    try{

        for(int i =0;i<9;i++){

            int x,y;

            cin>>x;

            cin>>y;

            t.oyna(x,y);

            t.bas();

 

        }

    }catch(kazanma k){

        t.bas();

        if(k.getKim()==1)

 

            cout << “kazanan: X” << endl;

        else

            cout << “kazanan: O” << endl;

    }

    getch();

}

 

Yukarıdaki kodda, 9 hamlenin yapıldığı bir for döngüsü bulunuyor. Bu döngü içerisinde, kullanıcıdan x ve y değerleri okunuluyor ve ardından bu okunan değerlere Tahta sınıfından tanımlanan t değişkeni ile oynanıyor. Burada kullanılan oyna fonksiyonu, sırayı almamaktadır. Dolayısıyla Tahta sınıfındaki tanımlı olan sırayı kullanmakta ve bu sıra otomatik olarak her hamleden sonra değiştirilmektedir.

Ayrıca bu döngü içerisindeki oynama işlemi sırasında herhangi bir taraf kazanacak olursa, kazanan kişi için bir istisna fırlatılacak ve bu fırlatılan istisna, yukarıdaki kodda bulunan catch bloğu ile yakalanacaktır. Burada yakalanan istisna, bizim tanımladığımız kazanma sınıfından olduğu için, sınıfın içerisinde tutulan kimin kazandığı bilgisini alıp buna göre uygun kazanma mesajını ekrana basabiliyoruz.

Kodun tamamına buradaki bağlantıyı tıklayarak erişebilirsiniz.

Bir Cevap Yazın

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


sekiz × 8 =