Zapałki
-- Sebastian Pawlak, 2001.
Prezentuję tu program, który ilustruje algorytm minimax
z obcinaniem alfa-beta na przykładzie pewnej wersji gry w zapałki.
Reguły gry: Jest 1 stos zapałek. Dwaj gracze na zmianę biorą co najwyżej
połowę liczby zapałek ze stosu. Wygrywa ten, który weźmie ostatnią
zapałkę.
Wersja bez obcinania alfa-beta
Kod źródłowy pliku "nim.c":
/* Gra w NIM 1 * Wersja bez ciec alfa-beta. * Sebastian Pawlak * 2001 */ #include <stdio.h> int najlepiejWziacZapalek = -1; int cnt = 0; /* liczba wezlow odpalonych w trakcie obliczen */ /* funkcja minimax */ int MiniMax(const int liczbaZapalek, const int glebokosc, const int gracz) { int najlepszy, tmp, k; cnt++; if (!liczbaZapalek) /* jesli stos zapalek jest pusty */ return glebokosc % 2 ? 1 : -1; /* 1 : wygrana, -1 : przegrana */ najlepszy = glebokosc % 2 ? 10 : -10; /* +nieskonczonosc, -nieskoncz. */ for (k = 1; k <= liczbaZapalek / 2 || k <= 1; k++) { /* rekurencyjne wywolanie funkcji minimaxowej */ tmp = MiniMax(liczbaZapalek - k, glebokosc + 1, gracz); if (!(glebokosc % 2)) { if (tmp > najlepszy) { najlepszy = tmp; if (!glebokosc) najlepiejWziacZapalek = k; /* najlepszy ruch */ } } else if (tmp < najlepszy) najlepszy = tmp; } return najlepszy; } int main (void) { int liczbaZapalek = 21; /* poczatkowa liczba zapalek na stosie */ int zebranych = 0; /* liczba zapalek zebranych w trakcie gry */ int gracz = 0; /* numer gracza, ktory aktualnie wykonuje ruch */ printf("Wersja bez obcinania alfa-beta.\n"); printf("Liczba zapalek na stosie: %d\n", liczbaZapalek); /* rozgrywamy cala gre - komputer vs komputer */ while (zebranych < liczbaZapalek) { MiniMax(liczbaZapalek - zebranych, 0, gracz); printf("gracz %c bierze %d zapalki\n", gracz ? 'B' : 'A', najlepiejWziacZapalek); zebranych += najlepiejWziacZapalek; if (zebranych < liczbaZapalek) gracz = gracz ? 0 : 1; } printf("wygral gracz %c\n", gracz ? 'B' : 'A'); printf("liczba odpalonych wezlow: %d\n", cnt); return 0; }
Wersja z obcinaniem alfa-beta
Kod źródłowy pliku "nimab.c":
/* Gra w NIM 1 * Wersja z obcinaniem alfa-beta. * Sebastian Pawlak * 2001 */ #include <stdio.h> int najlepiejWziacZapalek = -1; int cnt = 0; /* liczba wezlow odpalonych w trakcie obliczen */ /* funkcja minimax */ int MiniMax(const int liczbaZapalek, const int glebokosc, const int gracz) { int najlepszy, tmp, k; cnt++; if (!liczbaZapalek) /* jesli stos zapalek jest pusty */ return glebokosc % 2 ? 1 : -1; /* 1 : wygrana, -1 : przegrana */ najlepszy = glebokosc % 2 ? 10 : -10; /* +nieskonczonosc, -nieskoncz. */ for (k = 1; k <= liczbaZapalek / 2 || k <= 1; k++) { /* rekurencyjne wywolanie funkcji minimaxowej */ tmp = MiniMax(liczbaZapalek - k, glebokosc + 1, gracz); if (!(glebokosc % 2)) { if (tmp > najlepszy) { najlepszy = tmp; if (!glebokosc) najlepiejWziacZapalek = k; /* najlepszy ruch */ if (tmp == 1) break; /* wychodzimy z petli */ } } else if (tmp < najlepszy) { najlepszy = tmp; if (tmp == -1) break; /* wychodzimy z petli */ } } return najlepszy; } int main (void) { int liczbaZapalek = 21; /* poczatkowa liczba zapalek na stosie */ int zebranych = 0; /* liczba zapalek zebranych w trakcie gry */ int gracz = 0; /* numer gracza, ktory aktualnie wykonuje ruch */ printf("Wersja z obcinaniem alfa-beta.\n"); printf("Liczba zapalek na stosie: %d\n", liczbaZapalek); /* rozgrywamy cala gre - komputer vs komputer */ while (zebranych < liczbaZapalek) { MiniMax(liczbaZapalek - zebranych, 0, gracz); printf("gracz %c bierze %d zapalki\n", gracz ? 'B' : 'A', najlepiejWziacZapalek); zebranych += najlepiejWziacZapalek; if (zebranych < liczbaZapalek) gracz = gracz ? 0 : 1; } printf("wygral gracz %c\n", gracz ? 'B' : 'A'); printf("liczba odpalonych wezlow: %d\n", cnt); return 0; }
Wersja z obcinaniem alfa-beta oraz interakcją z graczem
Kod źródłowy pliku "niminter.c":
/* Gra w NIM 1 * Wersja z obcinaniem alfa-beta. * Wersja interaktywna. * Sebastian Pawlak * 2001 */ #include <stdio.h> int najlepiejWziacZapalek = -1; /* funkcja minimax */ int MiniMax(const int liczbaZapalek, const int glebokosc, const int gracz) { int najlepszy, tmp, k; if (!liczbaZapalek) /* jesli stos zapalek jest pusty */ return glebokosc % 2 ? 1 : -1; /* 1 : wygrana, -1 : przegrana */ najlepszy = glebokosc % 2 ? 10 : -10; /* +nieskonczonosc, -nieskoncz. */ for (k = 1; k <= liczbaZapalek / 2 || k <= 1; k++) { /* rekurencyjne wywolanie funkcji minimaxowej */ tmp = MiniMax(liczbaZapalek - k, glebokosc + 1, gracz); if (!(glebokosc % 2)) { if (tmp > najlepszy) { najlepszy = tmp; if (!glebokosc) najlepiejWziacZapalek = k; /* najlepszy ruch */ if (tmp == 1) break; /* wychodzimy z petli */ } } else if (tmp < najlepszy) { najlepszy = tmp; if (tmp == -1) break; /* wychodzimy z petli */ } } return najlepszy; } int main (void) { int liczbaZapalek; /* poczatkowa liczba zapalek na stosie */ int zebranych = 0; /* liczba zapalek zebranych w trakcie gry */ int ileZebrac; int gracz; /* numer gracza, ktory aktualnie wykonuje ruch */ printf("Wersja z obcinaniem alfa-beta.\n"); printf("Kto pierwszy wyciaga zapalki ?\na - komputer, b - gracz\n"); gracz = 1; if (getchar() == 'a') gracz = 0; do { printf("Prosze podac liczbe zapalek na stosie:\n"); scanf("%d", &liczbaZapalek); } while (liczbaZapalek <= 0); /* rozgrywamy cala gre - komputer vs komputer */ while (zebranych < liczbaZapalek) { printf("\nLiczba zapalek na stosie: %d\n", liczbaZapalek - zebranych); if (gracz == 1) { printf("ile zapalek bierzesz:\n"); do { scanf("%d", &ileZebrac); } while (ileZebrac <= 0 || (ileZebrac > (liczbaZapalek - zebranych) / 2 && ileZebrac != 1)); najlepiejWziacZapalek = ileZebrac; } else MiniMax(liczbaZapalek - zebranych, 0, gracz); printf("gracz %c bierze %d zapalki\n", gracz ? 'B' : 'A', najlepiejWziacZapalek); zebranych += najlepiejWziacZapalek; if (zebranych < liczbaZapalek) gracz = gracz ? 0 : 1; } printf("wygral gracz %c\n", gracz ? 'B' : 'A'); return 0; }