Реализация игры крестики-нолики
Всем привет! Пишу консольную игру крестики-нолики. В перспективе - создать бота. Реализация игры: есть пронумерованное поле 3х3 от 1 до 9, в котором игроку предлагается поставить крестик или нолик путем ввода соответствующего числа. Пока я создал черновик, где ходит только крестик. И на данный момент для меня представляется затруднительным следующее:
- как сделать поочередный ход?
- как установить запрет на изменение крестика на нолик и наоборот?
Спасибо!
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Введите число от 1 до 9, чтобы сделать Ваш ход: " + '\n');
char[][] field = {{'1','2','3'},
{'4','5','6'},
{'7','8','9'}};
boolean isWon = false;
while (true) {
printField(field);
int num = Integer.parseInt(reader.readLine());
switch (num) {
case 1: field[0][0] = 'x'; break;
case 2: field[0][1] = 'x'; break;
case 3: field[0][2] = 'x'; break;
case 4: field[1][0] = 'x'; break;
case 5: field[1][1] = 'x'; break;
case 6: field[1][2] = 'x'; break;
case 7: field[2][0] = 'x'; break;
case 8: field[2][1] = 'x'; break;
case 9: field[2][2] = 'x'; break;
}
for (int i = 0; i < field.length; i++) {
if (field[i][0] == field[i][1] && field[i][0] == field[i][2] || // сравнение по горизонтали
field[0][i] == field[1][i] && field[0][i] == field[2][i] || // сравнение по вертикали
field[0][0] == field[1][1] && field[0][0] == field[2][2] || // сравнение по
field[2][0] == field[1][1] && field[2][0] == field[0][2]) { // диагоналям
isWon = true;
break;
}
}
if (isWon){
System.out.println("YOU WIN!!!");
printField(field);
break;
}
}
}
public static void printField (char[][] field) {
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[i].length; j++) {
if (j != 2)
System.out.print(field[i][j] + "|");
else System.out.print(field[i][j]);
}
System.out.println();
}
}
}
Ответы (3 шт):
Прежде чем писать код, надо взять карандаш, бумагу и попробовать прорисовать работу программы.
Давайте попробуем сначала. В игру крестики-нолики обычно играю два игрока, у каждого свой символ, соответственно крестик, или нолик и все это происходит на игровом поле произвольного размера.
Игровое поле:
class GameTable {
public GameTable(int row, int col) {
...
}
// Выполнение хода игроком
// Параметр Player player - игрок с методом player.getChip()
// return есть ли выигрышная комбинация (true), или нет (false)
public boolean move(Player player) {
// Здесь игрок player выбирает не занятую/свободную ячейку и помещает
// на нее свою фишку player.getChip()
return this.checkWinCombination(selectedRow, selectedColumn);
}
// Проверяет заполненность стола. Если заполнен и ходов больше сделать нельзя,
// то true, иначе false
public boolean isFull() {
}
// Метод проверки выигрышной комбинации, начиная с заданной ячейки row, col
private boolean checkWinCombination(int row, int col) {
// Берем ячейку и пробегаем смежные ячейки по горизонтали,
// вертикали и диагоналям в поисках выигрышной последовательности
// Если нашли, то return true
// иначе false
}
}
Игрок:
class Player {
// name - имя игрока
// chip - фишка игрока
public Player(String name, String chip) {
}
public String getName() {
}
public String getChip() {
}
}
Инициализация
//Список игроков:
List<Player> players = new ArrayList<>();
// Игровое поле
GameTable gameTable = new GameTable(3, 3);
// Создаем игроков
// Первый параметр - имя игрока, второй - тип фишки (chip)
Player player1 = new Player("Крестик", "X");
Player player2 = new Player("Нолик", "0");
players.add(player1);
players.add(player2);
Игровой процесс
Ходят игроки последовательно друг за другом до тех пор пока один из них не выставит свои фишки в ряд (горизонтально, вертикально, или по-диагоналям).
Ага, так, последовательно, друг за другом, пока кто-то не будет признан выигравшим после проверки очередного хода на выполненную/выигрышную комбинацию:
// Ссылка на победителя
Player winner = null;
// Индекс текущего игрока
int currentPlayerIndex = 0;
// Цикл будет крутиться до тех пор, пока внутри него игроки будут делать ходы и
// либо один из них не выиграет, либо пока не заполнится игровое поле
while(true) {
Player player = players.get(currentPlayerIndex);
if ( gameTable.move( player ) ) {
// у нас есть победитель
winner = player;
break;
} else if (gameTable.isFull()) {
break;
}
// Если нет победителя и на доске еще есть место для ходов, то
// меняем индекс текущего игрока, чтобы передать ход другому.
// Здесь мы увеличиваем индекс на единицу и делим по модулю на
// количество игроков, тем самым избегая "выскакивания" индекса за пределы массива
currentPlayerIndex = ++currentPlayerIndex % players.size();
}
if ( winner != null ) {
System.out.print(" Победитель: " + player.getName());
} else {
System.out.print(" Ничья! ");
}
как сделать поочередный ход?
можно использовать переменную типа boolean, где false - первый игрок (крести), а true - второй игрок (нолик).
перед каждым ходом - проверяем кто сейчас ходит.
после каждого хода - инвертируем переменную.
как установить запрет на изменение крестика на нолик и наоборот?
вариантом может быть создание второй скрытой матрицы, которая будет наполнена изначально одним значение =0.
и когда игрок делает ход, например "крестик в [1][2]", то значение в этой "скрытой" матрице меняется с 0 на 1.
соответственно перед каждый ходом надо проверять, чему равно значение в "скрытой" матрице.
доп.идея (та же суть, другая реализация).
у вас матрица [3][3], а можно сделать [3][3][1] и в последнем хранить точно так же 0 или 1.
то есть
После нескольких часов всё-таки родил своё решение:
public class Main {
public static boolean isWin = false;
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int randomPlayer = (int) (Math.random() * 2 + 1);
if (randomPlayer == 1)
System.out.println("Player1 ходит первым!" + '\n');
else System.out.println("Player2 ходит первым!" + '\n');
System.out.println("Введите число от 1 до 9, чтобы сделать Ваш ход: " + '\n');
char[][] field = {{'1','2','3'},
{'4','5','6'},
{'7','8','9'}};
char crossOrZero;
int switcher = 0; // переключатель между первым и вторым игроком
while (true) {
if (switcher % 2 == 0)
crossOrZero = 'x';
else crossOrZero = 'o';
printField(field);
int num;
try {
num = Integer.parseInt(reader.readLine()); // запрет ввода символов, отличных от чисел
} catch (NumberFormatException e) {
System.out.println("Число, только число!");
continue;
}
if (Arrays.deepToString(field).contains(Integer.toString(num)) && num <= 9 && num > 0) {
switch (num) {
case 1: field[0][0] = crossOrZero; break;
case 2: field[0][1] = crossOrZero; break;
case 3: field[0][2] = crossOrZero; break;
case 4: field[1][0] = crossOrZero; break;
case 5: field[1][1] = crossOrZero; break;
case 6: field[1][2] = crossOrZero; break;
case 7: field[2][0] = crossOrZero; break;
case 8: field[2][1] = crossOrZero; break;
case 9: field[2][2] = crossOrZero; break;
}
} else {
System.out.println("Введите число из оставшихся на поле!");
continue;
}
checkingForMatch(field);
if (isWin) {
System.out.println("YOU WIN!!!");
printField(field);
break;
}
switcher++;
}
}
public static void printField (char[][] field) { // печать поля
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[i].length; j++) {
if (j != 2)
System.out.print(field[i][j] + "|");
else System.out.print(field[i][j]);
}
System.out.println();
}
}
public static void checkingForMatch (char[][] field) { // проверка на совпадение трёх элементов
for (int i = 0; i < field.length; i++) {
if (field[i][0] == field[i][1] && field[i][0] == field[i][2] || // сравнение по горизонтали
field[0][i] == field[1][i] && field[0][i] == field[2][i] || // сравнение по вертикали
field[0][0] == field[1][1] && field[0][0] == field[2][2] || // сравнение по
field[2][0] == field[1][1] && field[2][0] == field[0][2]) { // диагоналям
isWin = true;
break;
}
}
}