Problem z Kółko i krzyżyk na 36 pól i grę komputera z graczem

0
#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'));

}
0

No i?

0

no i nie działa po pierwszym ruchu nie da się nic zrobić

1

Debugger do ręki i patrz, gdzie się zatrzymuje. Masz trochę zbyt skomplikowany kod do statycznej analizy…

0

Problem w tym że program się odpala i po pierwszym ruchu się freezuje i muszę zamknąć konsole

2

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).

1

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.

1

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

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0