Compare commits

...

3 Commits

Author SHA1 Message Date
Jannis Heydemann
ef4f2d2cbf 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 <noreply@anthropic.com>
2026-04-24 13:11:08 +02:00
Jannis Heydemann
c057c9f3c1 Merge branch 'master' of ssh://gitea.starfour.de:2222/Berufsschule/tictactoe 2026-04-23 12:51:32 +02:00
Jannis Heydemann
cfbf7e9974 switched from strings to chars 2026-04-23 12:50:02 +02:00

160
main.cpp
View File

@@ -1,6 +1,5 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <vector>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@@ -8,11 +7,7 @@
using namespace std; using namespace std;
string spielfeld[3][3] = { const int N = 3;
{" ", " ", " "},
{" ", " ", " "},
{" ", " ", " "}
};
struct userInput { struct userInput {
bool ok; bool ok;
@@ -22,109 +17,120 @@ struct userInput {
struct gameState { struct gameState {
bool running = true; bool running = true;
string winner; char winner = ' ';
char board[N][N];
int filledCells = 0; 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() { void render(const gameState &state) {
#ifdef _WIN32 #ifdef _WIN32
system("cls"); system("cls");
#endif #endif
#ifdef linux
#ifdef linux
system("clear"); system("clear");
#endif #endif
cout << " | 1 | 2 | 3 |" << endl; cout << " | ";
cout << "---------------" << endl; for (int j = 0; j < N; j++) cout << j + 1 << " | ";
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 << endl; cout << endl;
if (spielfeld[inputZeile-1][inputSpalte-1] == " ") { for (int i = 0; i < N * 4 + 3; i++) cout << "-";
return {true, inputZeile-1, inputSpalte-1}; cout << endl;
} else { for (int i = 0; i < N; i++) {
cout << endl << "Input konnte nicht gelesen werden" << endl; cout << i + 1 << " | ";
return getUserInput(); 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) { userInput getUserInput(const gameState &state) {
// TODO: implement a proper efficent check that doesnt end when every tile is filled while (true) {
// simplest way would be to check all 8 different solutions. 3 from top to bottom, 3 from left to right and 2 diagonals int inputZeile, inputSpalte;
for (int i = 0; i < 3; i++) { cout << "Zeile: ";
// left to right cin >> inputZeile;
if (spielfeld[i][0] == spielfeld[i][1] && spielfeld[i][2] == spielfeld[i][1] && spielfeld[i][2] != " ") { cout << "Spalte: ";
state.running = false; cin >> inputSpalte;
state.winner = spielfeld[i][1]; cout << endl;
return;
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] != " ") { if (inputZeile >= 1 && inputZeile <= N &&
state.running = false; inputSpalte >= 1 && inputSpalte <= N &&
state.winner = spielfeld[1][i]; state.board[inputZeile - 1][inputSpalte - 1] == ' ') {
return; 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.running = false;
state.winner = spielfeld[2][2]; state.winner = player;
return; return;
} }
// top right to bottom left if (state.filledCells >= N * N) {
if (spielfeld[0][2] == spielfeld[1][1] && spielfeld[1][1] == spielfeld[2][0] && spielfeld[2][0] != " ") {
state.running = false; state.running = false;
state.winner = spielfeld[2][0]; state.winner = '-';
return;
}
//draw
if (state.filledCells >= 9) {
state.running = false;
state.winner = "-";
return;
} }
} }
int main() { int main() {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
#endif #endif
cout << fixed << setprecision(2);
gameState state; gameState state;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
state.board[i][j] = ' ';
int counter = 1; int counter = 1;
while (state.running) { while (state.running) {
render(); render(state);
string current_player = counter++ % 2 == 1 ? "X" : "O"; char current_player = counter++ % 2 == 1 ? 'X' : 'O';
cout << endl << "Am Zug ist Spieler: " << current_player << endl; cout << endl << "Am Zug ist Spieler: " << current_player << endl;
if (auto [ok, zeile, spalte] = getUserInput(); ok) { auto [ok, zeile, spalte] = getUserInput(state);
spielfeld[zeile][spalte] = current_player; if (ok) {
state.filledCells += 1; 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"); system("pause");
#endif #endif
return 0; return 0;
} }