From ef4f2d2cbf55848f434ded53cd9254ee99a08e63 Mon Sep 17 00:00:00 2001 From: Jannis Heydemann Date: Fri, 24 Apr 2026 13:11:08 +0200 Subject: [PATCH] Refactor win-check to O(1) counter-based approach, scalable to any NxN - Win detection now uses per-player row/col/diagonal counters instead of scanning lines; O(1) per move and works for any board size (change N) - Moved spielfeld into gameState as board[N][N]; removed global - Replaced recursive getUserInput with a loop to avoid stack overflow on repeated bad input; also handles non-integer cin failures - Initialized gameState::winner to ' ' to avoid undefined behaviour - Fixed draw output: prints "Unentschieden!" instead of "Der Gewinner ist: -" - Removed stale TODO comment that incorrectly claimed draw detection was broken Co-Authored-By: Claude Sonnet 4.6 --- main.cpp | 160 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/main.cpp b/main.cpp index 43b982e..12a2269 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,5 @@ #include #include -#include #ifdef _WIN32 #include @@ -8,7 +7,7 @@ using namespace std; -char spielfeld[3][3]; +const int N = 3; struct userInput { bool ok; @@ -18,115 +17,120 @@ struct userInput { struct gameState { bool running = true; - char winner; + char winner = ' '; + char board[N][N]; int filledCells = 0; + // Per-player (0=X, 1=O) counters for rows, cols, and both diagonals. + // Win-check is O(1): increment on each move, compare to N. + int rowCount[2][N] = {}; + int colCount[2][N] = {}; + int diagCount[2] = {}; + int antiDiagCount[2] = {}; }; -void render() { - #ifdef _WIN32 +void render(const gameState &state) { +#ifdef _WIN32 system("cls"); - #endif - - #ifdef linux +#endif +#ifdef linux system("clear"); - #endif +#endif - cout << " | 1 | 2 | 3 |" << endl; - cout << "---------------" << endl; - for (int i = 0; i < 3; i++) { - cout << i+1 << " | "; - for (int j = 0; j < 3; j++) { - cout << spielfeld[i][j] << " | "; - } - cout << endl; - cout << "---------------" << endl; - } -} - -userInput getUserInput() { - int inputSpalte, inputZeile; - cout << "Zeile: "; - cin >> inputZeile; - cout << "Spalte: "; - cin >> inputSpalte; + cout << " | "; + for (int j = 0; j < N; j++) cout << j + 1 << " | "; cout << endl; - if (spielfeld[inputZeile-1][inputSpalte-1] == ' ') { - return {true, inputZeile-1, inputSpalte-1}; - } else { - cout << endl << "Input konnte nicht gelesen werden" << endl; - return getUserInput(); + for (int i = 0; i < N * 4 + 3; i++) cout << "-"; + cout << endl; + for (int i = 0; i < N; i++) { + cout << i + 1 << " | "; + for (int j = 0; j < N; j++) cout << state.board[i][j] << " | "; + cout << endl; + for (int k = 0; k < N * 4 + 3; k++) cout << "-"; + cout << endl; } } -void checkforWin(gameState &state) { - // TODO: implement a proper efficent check that doesnt end when every tile is filled - // simplest way would be to check all 8 different solutions. 3 from top to bottom, 3 from left to right and 2 diagonals - for (int i = 0; i < 3; i++) { - // left to right - if (spielfeld[i][0] == spielfeld[i][1] && spielfeld[i][2] == spielfeld[i][1] && spielfeld[i][2] != ' ') { - state.running = false; - state.winner = spielfeld[i][1]; - return; +userInput getUserInput(const gameState &state) { + while (true) { + int inputZeile, inputSpalte; + cout << "Zeile: "; + cin >> inputZeile; + cout << "Spalte: "; + cin >> inputSpalte; + cout << endl; + + if (!cin) { + cin.clear(); + cin.ignore(1000, '\n'); + cout << "Ungueltige Eingabe, bitte nochmal." << endl; + continue; } - // top to bottom - if (spielfeld[0][i] == spielfeld[1][i] && spielfeld[1][i] == spielfeld[2][i] && spielfeld[1][i] != ' ') { - state.running = false; - state.winner = spielfeld[1][i]; - return; + + if (inputZeile >= 1 && inputZeile <= N && + inputSpalte >= 1 && inputSpalte <= N && + state.board[inputZeile - 1][inputSpalte - 1] == ' ') { + return {true, inputZeile - 1, inputSpalte - 1}; } + + cout << "Feld bereits belegt oder ausserhalb des Spielfelds." << endl; } - // top left to bottom right - if (spielfeld[0][0] == spielfeld[1][1] && spielfeld[1][1] == spielfeld[2][2] && spielfeld[2][2] != ' ') { +} + +void checkforWin(gameState &state, int zeile, int spalte, char player) { + int p = (player == 'X') ? 0 : 1; + + state.rowCount[p][zeile]++; + state.colCount[p][spalte]++; + if (zeile == spalte) state.diagCount[p]++; + if (zeile + spalte == N - 1) state.antiDiagCount[p]++; + + if (state.rowCount[p][zeile] == N || + state.colCount[p][spalte] == N || + state.diagCount[p] == N || + state.antiDiagCount[p] == N) { state.running = false; - state.winner = spielfeld[2][2]; + state.winner = player; return; } - // top right to bottom left - if (spielfeld[0][2] == spielfeld[1][1] && spielfeld[1][1] == spielfeld[2][0] && spielfeld[2][0] != ' ') { - state.running = false; - state.winner = spielfeld[2][0]; - return; - } - - //draw - if (state.filledCells >= 9) { + if (state.filledCells >= N * N) { state.running = false; state.winner = '-'; - return; } } int main() { - #ifdef _WIN32 +#ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); - #endif - cout << fixed << setprecision(2); - - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - spielfeld[i][j] = ' '; - } - } +#endif gameState state; + for (int i = 0; i < N; i++) + for (int j = 0; j < N; j++) + state.board[i][j] = ' '; int counter = 1; while (state.running) { - render(); + render(state); char current_player = counter++ % 2 == 1 ? 'X' : 'O'; cout << endl << "Am Zug ist Spieler: " << current_player << endl; - if (auto [ok, zeile, spalte] = getUserInput(); ok) { - spielfeld[zeile][spalte] = current_player; - state.filledCells += 1; + auto [ok, zeile, spalte] = getUserInput(state); + if (ok) { + state.board[zeile][spalte] = current_player; + state.filledCells++; + checkforWin(state, zeile, spalte, current_player); } - checkforWin(state); } - render(); - cout << endl << "Der gewinner ist: " << state.winner << endl; - - #ifdef _WIN32 + + render(state); + if (state.winner == '-') { + cout << endl << "Unentschieden!" << endl; + } else { + cout << endl << "Der Gewinner ist: " << state.winner << endl; + } + +#ifdef _WIN32 system("pause"); - #endif +#endif return 0; }