Я делаю игру в крестики-нолики (я знаю, довольно легко, но я новичок), и я почти закончил, моя проблема сейчас в том, что я пытаюсь проверить, имеют ли строка, столбец или диагональ таблицы одинаковое значение. и рекурсивно продолжайте играть, если это не так. Моя доска выглядит так:
char xToken = 'x';
char oToken = 'o';
char vertical = '|';
char horizontal = '-';
class Board {
public:
const static int rows = 6;
const static int cols = 6;
char grid[rows][cols]{};
Board() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
grid[i][0] = '0' + i;
grid[0][j] = '0' + j;
if (j == 2 || j == 4) {
grid[i][j] = vertical;
}
if (i == 2 || i == 4) {
grid[i][j] = horizontal;
}
}
}
}
void printBoard() {
for (auto & i : grid) {
for (char j : i) {
cout << j << "\t";
}
cout << endl;
}
}
}
Это моя функция play():
void play() {
int i = 0;
int j = 0;
cout << "choose index you want to play in" << endl;
cout << "Player 1" << endl;
cout << "Row: " << endl;
cin >> i;
cout << "Column: " << endl;
cin >> j;
cout << i << ", " << j << endl;
if (Board().grid[i][j] != vertical && Board().grid[i][j] != horizontal) {
grid[i][j] = xToken;
printBoard();
}
cout << "choose index you want to play in" << endl;
cout << "Player 2" << endl;
cout << "Row: " << endl;
cin >> i;
cout << "Column: " << endl;
cin >> j;
cout << i << ", " << j << endl;
if (Board().grid[i][j] != vertical && Board().grid[i][j] != horizontal) {
grid[i][j] = oToken;
printBoard();
}
if (grid[i][j] == xToken) {
cout << "You Win!" << endl;
} else
play();
}
Идея в том, что я пытаюсь рекурсивно продолжать игру, если у нас нет трех элементов в строке, столбце или диагонали с одинаковым значением. Сама проблема теперь состоит в том, чтобы найти способ читать доску так, чтобы она могла найти 3 подряд и игра остановилась.
arr[0] == arr[1] && arr[1] == arr[2]
, если у вас массив большего размера, лучше использовать цикл
@3CxEZiVlQ Да, но есть ли в этом проблемы? Или было бы лучше, если бы я сделал что-нибудь другое? Моя идея состоит в том, чтобы заполнить индексы доски крестиками и нулями по ходу игры.
play()
учится в Board
классе?
@Mahdy.n Да, это так
В вашем коде нет ничего плохого, вам просто нужен кто-то, кто его настроит, и это ваша работа, но если у вас проблемы с кодом, вам не следует каждый раз вызывать конструктор Board().
! Также хотите проверить, равны ли строки, столбцы или диагонали? Так что, возможно, вам понадобятся 3 небольшие функции, которые сделают это за вас.
Предоставьте достаточно кода, чтобы мы увидели, как вы используете этот класс. В настоящее время существует только определение класса и функция потери, которая, согласно вашему комментарию, принадлежит тому же классу, но нет кода драйвера. Еще один комментарий: не рекомендуется смешивать задачи ввода-вывода с игровой логикой. Если play
— это метод, он не должен запрашивать ввод данных у пользователя и не должен распечатывать доску; это должно быть сделано вне этого класса.
Здесь не нужно беспокоиться о скорости или эффективности. Просто проверьте каждую строку, затем каждый столбец, а затем две диагонали, чтобы увидеть, есть ли в них три жетона последнего игрока. Это становится простым методом, который можно вызывать после каждого ввода пользователя.
@Pelz04 Pelz04, поскольку вы новичок в StackOverflow, я хочу помнить, что есть возможность принять ответ, нажав на галочку. Важно отметить, что это не является обязательным: выберите один ответ, который, по вашему мнению, является лучшим решением вашей проблемы. Если отсутствие ответа считается лучшим решением, ничего не делайте.
Мой плохой @LoS, я не знал. Без сомнения, многие ваши материалы были действительно полезны, я ценю это.
Можно выполнить проверку в квадратичном времени, проверив, существует ли строка, столбец или диагональ, содержащая только один и тот же символ.
По матрице m
, представляющей таблицу крестиков-ноликов, проверяется, существует ли комбинация типа m[i][j]
для jε[0, 3), которая указывает на строки, и для iε[0, 3), что указывает на столбцы, отличные от двух диагоналей m[i][i]
и m[i][3 - i]
для каждого iε[0, 3). Если одна из этих комбинаций содержит только один и тот же символ, который может быть как нулём, так и крестиком, это означает, что игрок выиграл, в противном случае — нет.
Чтобы упростить алгоритм, можно использовать следующий прием: при условии, что определенное число используется для обозначения нуля или крестика, все ячейки определенной строки, столбца или диагонали суммируются по отдельности, а затем полученное число сравнивается с двумя. константы. Таким образом, если сумма кратна 6 и одному из конкретных чисел, можно сделать вывод, что строка, столбец или диагональ содержат только соответствующий символ, и, следовательно, игрок выиграл. Например, нуль может быть представлен цифрой -1, а крест может быть представлен цифрой 1.
Пример:
std::pair<bool, bool> is_winner()
{
int sumb = 0;
int sumd = 0;
for(int i = 0; i != 3; ++i){
int sumr = 0;
int sumc = 0;
for(int j = 0; j != 3 ; ++j){
sumr += m[3 * i + j];
sumc += m[3 * j + i];
}
if (sumr == -3 || sumc == -3)
return {true, false};
if (sumr == 3 || sumc == 3)
return {false, true};
sumb += m[i][i];
sumd += m[i][3 - i];
}
if (sumb == -3 || sumd == -3)
return {true, false};
if (sumb == 3 || sumd == 3)
return {false, true};
return {false, false};
}
Характер игры таков, что некоторые поля могут быть еще незаполненными. Если они представлены 0, вам следует тщательно выбирать значения для представления x
и o
. Например, 1
и 2
не подходят, поскольку комбинация 2
и «пусто» может имитировать целый ряд 1
. Для строк размера 6 можно использовать 1
и 7
и сравнить сумму с 6
и 42
.
@anatolyg, спасибо за отзыв. Я думаю, что можно использовать отрицательное число для нулей и положительное число для крестиков.
Теперь, когда ваш класс появился, полезные элементы данных находятся в нечетных индексах вашего массива grid
:
grid[1][1]
grid[1][3]
grid[1][5]
grid[3][1]
grid[3][3]
grid[3][5]
grid[5][1]
grid[5][3]
grid[5][5]
Немного неудобно, но пока хватит.
Сначала вам следует подумать об интерфейсе вашего кода. Я предлагаю следующее:
class Board {
public:
...
bool isWinningPosition(char token) const;
...
};
Этот метод проверяет, появляется ли данный токен во всей строке, целом столбце или всей диагонали. Чтобы реализовать это:
bool Board::isWinningPosition(char token) const
{
// Check row 1
// If the token appears at all 3 relevant positions, it wins!
if (grid[1][1] == token && grid[1][3] == token && grid[1][5] == token)
return true;
// Check row 2
if (...more conditions...)
return true;
// Check row 3, then 3 columns, then 2 diagonals, similar to the above
// If all 8 conditions are false, the token doesn't win yet
return false;
}
Здесь я жестко запрограммировал размеры игры 3x3. Если вы хотите поддерживать гибкие размеры игрового поля, используйте несколько циклов.
Независимо от требуемой логики, я предлагаю вам перепроектировать хранилище данных. Вам следует отделить данные от представления. Создайте массив 3x3 (а не 6x6) для хранения токенов, и этот массив не должен содержать ничего другого. Это усложнит метод printBoard
, но упростит все остальные методы.
Знаете ли вы, что ваш код всегда имеет дело с пустыми досками
Board()
?play()
не использует сохраненные доски, использует только вновь созданные временные доски.