#include <iostream>
using namespace std;
// Funkcja rysuje planszę gry w kółko i krzyżyk
// Plansza przechowywana jest w tablicy t[]
void plansza(char t[])
{
for (int i = 1; i <= 36; i++)
{
cout << " " << t[i] << " ";
if (i % 6)
cout << "|";
else if (i != 36)
cout << "\n---+---+---+---+---+---\n";
else cout << endl;
}
}
bool win(char t[], char g, bool cisza)
{
bool test;
int i;
test = false;
for (i = 1; i <= 31; i += 6)
test |= ((t[i] == g) && (t[i + 1] == g) && (t[i + 2] == g) && (t[i + 3] == g) && (t[i + 4] == g) && (t[i + 5] == g));
for (i = 1; i <= 6; i++)
test |= ((t[i] == g) && (t[i + 6] == g) && (t[i + 12] == g) && (t[i + 18] == g) && (t[i + 24] == g) && (t[i + 30] == g));
test |= ((t[1] == g) && (t[8] == g) && (t[15] == g) && (t[22] == g) && (t[29] == g) && (t[35] == g));
test |= ((t[6] == g) && (t[11] == g) && (t[16] == g) && (t[21] == g) && (t[26] == g) && (t[32] == g));
if (test)
{
if (!cisza)
{
plansza(t);
cout << "\nGRACZ " << g << " WYGRYWA!!!\n\n";
return true;
}
return false;
}
}
bool remis(char t[], bool cisza)
{
for (int i = 1; i <= 36; i++) if (t[i] == ' ') return false;
if (!cisza)
{
plansza(t); cout << "\nREMIS !!!\n\n";
}
return true;
}
// Algorytm rekurencyjny MINIMAX
// Do algorytmu wchodzimy z planszą, na której ustawione jest pole
// bieżącego gracza. Parametr gracz przekazuje literkę gracza, a
// parametr mx przekazuje jego wynik w przypadku wygranej
//----------------------------------------------------------------
int minimax(char t[], char gracz)
{
int m, mmx;
// Najpierw sprawdzamy, czy bieżący gracz wygrywa na planszy. Jeśli tak, to
// zwracamy jego maksymalny wynik
if (win(t, gracz, true)) return (gracz == 'X') ? 1 : -1;
// Następnie sprawdzamy, czy nie ma remisu. Jeśli jest, zwracamy wynik 0
if (remis(t, true)) return 0;
// Będziemy analizować możliwe posunięcia przeciwnika. Zmieniamy zatem
// bieżącego gracza na jego przeciwnika
gracz = (gracz == 'X') ? 'O' : 'X';
// Algorytm MINIMAX w kolejnych wywołaniach rekurencyjnych naprzemiennie analizuje
// grę gracza oraz jego przeciwnika. Dla gracza oblicza maksimum wyniku gry, a dla
// przeciwnika oblicza minimum. Wartość mmx ustawiamy w zależności od tego, czyje
// ruchy analizujemy:
// X - liczymy max, zatem mmx <- -10
// O - liczymy min, zatem mmx <- 10
mmx = (gracz == 'O') ? 10 : -10;
// Przeglądamy planszę szukając wolnych pół na ruch gracza. Na wolnym polu ustawiamy
// literkę gracza i wyznaczamy wartość tego ruchu rekurencyjnym wywołaniem
// algorytmu MINIMAX. Planszę przywracamy i w zależności kto gra:
// X - wyznaczamy maximum
// O - wyznaczamy minimum
for (int i = 1; i <= 36; i++)
if (t[i] == ' ')
{
t[i] = gracz;
m = minimax(t, gracz);
t[i] = ' ';
if (((gracz == 'O') && (m < mmx)) || ((gracz == 'X') && (m > mmx))) mmx = m;
}
return mmx;
}
// Funkcja wyznacza ruch dla komputera.
int komputer(char t[])
{
int ruch, i, m, mmx;
mmx = -10;
for (i = 1; i <= 36; i++)
if (t[i] == ' ')
{
t[i] = 'X';
m = minimax(t, 'X');
t[i] = ' ';
if (m > mmx)
{
mmx = m; ruch = i;
}
}
return ruch;
}
// Funkcja umożliwia ruch gracza
// Po ruchu następuje zamiana gracza
void ruch(char t[], char &gracz)
{
int r;
plansza(t);
if (gracz == 'O')
{
cout << "\nCZLOWIEK : wybiera ruch : ";
cin >> r;
}
else
{
r = komputer(t);
cout << "\nKOMPUTER : wybiera ruch : " << r << endl;
}
cout << "---------------------------\n\n";
if ((r >= 1) && (r <= 36) && (t[r] == ' ')) t[r] = gracz;
gracz = (gracz == 'O') ? 'X' : 'O';
}
int main()
{
char t[10], gracz, wybor;
do
{
cout << "Gra w Kolko i Krzyzyk dla gracza i komputera\n"
"============================================\n\n";
for (int i = 1; i <= 36; i++) t[i] = ' ';
gracz = 'O';
while (!win(t, 'X', false) && !win(t, 'O', false) && !remis(t, false)) ruch(t, gracz);
cout << "Jeszcze raz ? (T = TAK) : ";
cin >> wybor;
cout << "\n\n\n";
} while ((wybor == 'T') || (wybor == 't'));
}
No i?
no i nie działa po pierwszym ruchu nie da się nic zrobić
Debugger do ręki i patrz, gdzie się zatrzymuje. Masz trochę zbyt skomplikowany kod do statycznej analizy…
Problem w tym że program się odpala i po pierwszym ruchu się freezuje i muszę zamknąć konsole
pewnie minmax mieli i mieli bo nie ograniczyłeś głębokości rekurencji.
na dodatek nie ma zrobionej optymalizacji, w której dany układ nie będzie analizowany wielokrotnie (do tego samego układu można dojść na wiele sposobów).
Z funkcją remis poszedłeś na łatwiznę, bo już poniższy układ doprowadzi do remisu, choć część pól jest pusta:
x*---x
-x*---
--x*--
---x*-
----*x
*---x*
W tym układzie nie ma żadnego ruchu, który dawałby zwycięstwo a jednocześnie nie osiągasz Twojego warunku remisu, w którym każde pole musi mieć symbol gracza.
Być może to jest powód, że algorytm kręci się w kółko bo żadna ze stron nie jest w stanie wygrać, a jednocześnie nie osiąga remisu.
A teraz krytyka kodu:
- funkcje powinny mieć nazwę zawierającą czasownik (w końcu coś robią)
- zmienne powinny być rzeczownikami
- gorsze od pisania kodu po polsku jest pisanie równocześnie po polsku i angielsku - pisz po angielsku
- od groma liczb magicznych.
- niby korzystasz z C++ a piszesz tak jakby to było C
- części minmax-a nie powinny niczego wypisywać!
- wiele innych rzeczy mniej ważnych