Kółko i krzyżyk

    -- Sebastian Pawlak, 1999.


Przedstawiam grę w kółko i krzyżyk zaimplementowaną z użyciem metody mini-max.

Aby wyznaczyć pierwszy ruch dla planszy 3x3, komputer musi wykonać kilkaset tysięcy sprawdzeń (bez obcinania alfa-beta).

Wnioski: najlepszym ruchem otwierającym jest któreś z narożnych pól; najlepszą odpowiedzią na taki ruch otwierający jest obstawienie środkowego pola.

Fragment drzewa gry
Fragment drzewa gry


Kod źródłowy pliku "tictac.c":

/* MINI-MAXowe kolko i krzyzyk
 * autor: Sebastian Pawlak
 *
 * Brak interfejsu. Zmiany na planszy nalezy wykonywac w kodzie programu
 * zmienajac odpowiednio tablice Plansza (na samym koncu programu).
 */
#include <stdio.h>

#define Nic 0
#define Gracz_X 1
#define Gracz_O 2
#define SZEROKOSC 3
#define WYSOKOSC 3
#define ROZMIAR (SZEROKOSC*WYSOKOSC)

short najlepsze_pole=-1;
long licznik;

typedef struct ListaWolnychPol
{
    short WolnePola [ROZMIAR];
    short DlugoscListy;
} WolnePola;


void PokazPlansze(const char Plansza[])
{
    int i, j;

    for (i = 0; i < WYSOKOSC; i++) {
        for (j = 0; j < SZEROKOSC; j++) {
            switch (Plansza[i * SZEROKOSC + j]) {
                case Nic: printf("."); break;
                case Gracz_X: printf("x"); break;
                case Gracz_O: printf("o"); break;
            }
        }
        printf("\n");
    }
}


char Wygrana(const char Plansza[], const char gracz)
{
    short i, j, k;

    // pionowa
    for (i = 0; i < 3; i++) {
        k = 0;
        for (j = 0; j < 3; j++)
            if (Plansza[j * 3 + i] == gracz)
                k++;

      	if (k == 3)
            return 1;
    }
    
    // pozioma
    for (i = 0; i < 3; i++) {
        k = 0;
        for (j = 0; j < 3; j++)
            if (Plansza[j + i * 3] == gracz)
                k++;

	if (k == 3)
	    return 1;
     }

    // ukosna
    k = 0;
    for (i = 0; i < 3; i++)
        if (Plansza[i + i * 3] == gracz)
            k++;
    
    if (k == 3)
        return 1;

    k = 0;
    for (i = 0; i < 3; i++)
        if (Plansza[2 - i + i * 3] == gracz)
            k++;
    
    if (k == 3)
        return 1;

    return 0;
}


WolnePola GenerujListeWolnychPol(const char Plansza[])
{
    WolnePola WP;
    short i;

    WP.DlugoscListy = -1;
    
    for (i = 0; i < ROZMIAR; i++)
        if (Plansza[i] == Nic)
	    WP.WolnePola[++WP.DlugoscListy] = i;

    return WP;
}


short MiniMax(const char Plansza[], const short glebokosc, const char gracz)
{
    WolnePola WolneRuchy;
    short Najlepszy, tmp;
    short i, k;
    char Plansza_tmp[ROZMIAR];
    char gracz_nastepny;

    if (gracz == Gracz_X)
        gracz_nastepny = Gracz_O;
    else gracz_nastepny = Gracz_X;

    if (glebokosc % 2 != 0) {
        if (Wygrana(Plansza, gracz) == 1)  // Wygralismy
            return 1;
    } else if (Wygrana(Plansza, gracz_nastepny) == 1)  // Przegralismy
        return -1;

    WolneRuchy = GenerujListeWolnychPol(Plansza);

    if (WolneRuchy.DlugoscListy == -1)  // Remis
        return 0;

    if (glebokosc % 2 == 0)
        Najlepszy = -100;
    else Najlepszy = 100;

    for (k = 0; k <= WolneRuchy.DlugoscListy; k++) {
        for (i = 0; i < ROZMIAR; i++)
            Plansza_tmp[i] = Plansza[i];

        if (glebokosc % 2 == 0)
            Plansza_tmp[WolneRuchy.WolnePola [k]]=gracz;
        else Plansza_tmp[WolneRuchy.WolnePola [k]] = gracz_nastepny;

        tmp = MiniMax(Plansza_tmp, glebokosc + 1, gracz);
	licznik++;

        if (glebokosc % 2 == 0) {
            if (tmp > Najlepszy) {
                Najlepszy = tmp;
                if (glebokosc == 0)
                    najlepsze_pole = WolneRuchy.WolnePola[k];
            }
        } else {
            if (tmp < Najlepszy)
                Najlepszy = tmp;
        }
    }

    return Najlepszy;
}


int main ()
{
/* char Plansza[ROZMIAR]= {Gracz_O,     Nic,     Nic,
                         Gracz_X, Gracz_X, Gracz_O,
                         Gracz_O, Gracz_X,     Nic};*/

    char Plansza[ROZMIAR]= {     Nic,     Nic,     Nic,
                                 Nic,     Nic,     Nic,
                                 Nic,     Nic,     Nic };

    short i;

    printf("wartosc koncowa: %d\n", MiniMax(Plansza, 0, Gracz_O));
    printf("wykonano %d sprawdzen\n", licznik);
    Plansza[najlepsze_pole] = Gracz_O;
    PokazPlansze(Plansza);

    return 0;
}
w3cw3c
automatyka przemysłowa