Перейти к содержимому

Как сделать игру крестики нолики

  • автор:

Реализация игры крестики-нолики

Прежде чем писать код, надо взять карандаш, бумагу и попробовать прорисовать работу программы.

Давайте попробуем сначала. В игру крестики-нолики обычно играю два игрока, у каждого свой символ, соответственно крестик, или нолик и все это происходит на игровом поле произвольного размера.

Игровое поле:

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

Создание игры «Крестики-нолики» при помощи TypeScript, React и Mocha

Представляем вам перевод статьи Josh Kuttler, опубликованной на blog.bitsrc.io. Узнайте, как создать приложение «Крестики-нолики», используя React и TypeScript.

Простая игра в крестики-нолики создана по модульному принципу и загружена на сайт Bit. Вы можете изменять компоненты моей игры и тестировать ее онлайн на Bit PlayGround при помощи NPM, Yarn или Bit. Для этого перейдите к моей коллекции компонентов.

Когда создаешь игры типа «Крестики-нолики» по модульному принципу, трудно найти причину, по которой компоненты UI могут снова когда-либо использоваться. Поэтому я сосредоточился в основном на игровых утилитах.

Для программирования я выбрал язык TypeScript — скомпилировал код при помощи TypeScript на сайте Bit. Затем воспользовался фреймворком Mocha для тестирования.

Чтобы установить компоненты из моего проекта, сначала настройте bit.dev в качестве реестра области (скопируйте и вставьте на своем устройстве). Это следует сделать только один раз! При дальнейшем использовании сайта Bit проводить повторную настройку не понадобится.

npm config set '@bit:registry' https://node.bit.dev

Затем установите компонент при помощи менеджеров пакетов Yarn или NPM:

npm i @bit/joshk.tic-tac-toe-game.game yarn add @bit/joshk.tic-tac-toe-game.game

Компонент «игра»

Компонент «игра» является основным компонентом моего приложения — он создан при помощи одного компонента Board и двух компонентов Prime React.

Я использовал компоненты Button и Input-text для экрана настройки — протестировать и посмотреть их код можно здесь.

Установите компоненты PrimeReact в свой проект:

yarn add @bit/primefaces.primereact.inputtext yarn add @bit/primefaces.primereact.button

После настройки параметров можно кликнуть на «Играть» и… играть!

Компонент Board

Компонент Board создает динамическую таблицу при помощи Props, устанавливает очередь для игроков и определяет победителя. Протестировать и посмотреть код можно здесь.

Компонент Square

Компонент Square — это обычная ячейка, которая получает значение с опциональным цветом и отправляет ивент компоненту Board при изменении значения. Протестировать и посмотреть код можно здесь.

Функция Empty cell

Функция Empty cell — это вспомогательная функция для функции Winner-calc, которая проверяет, есть ли пустые ячейки в таблице игры.

Bit позволяет увидеть документы компонента и результаты тестов:

Код функции

/** * @description * check if 2d array have an empty cell * @param <>> matrix 2d array * @param rowsNum number of rows * @param colsNum number of columns * @returns return true if empty cell was found, and false if not. * @example * import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell'; * * const matrix = [ * ['X', 'O', 'X'], * ['O', 'X', 'O'], * ['O', 'X', 'O'] * ]; * const result = haveEmptyCell(matrix, 3, 3); * * export default result * @example * import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell'; * * const matrix = [ * ['X', 'O', 'X'], * ['O', '', 'O'], * ['O', 'X', 'O'] * ]; * const result = haveEmptyCell(matrix, 3, 3); * * export default result * @example * import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell'; * * const matrix = [ * ['X', 'O', 'X'], * ['O', , 'O'], * ['O', 'X', 'O'] * ]; * const result = haveEmptyCell(matrix, 3, 3); * * export default result * @example * import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell'; * * const matrix = [ * ['X', 'O', 'X'], * ['O', null, 'O'], * ['O', 'X', 'O'] * ]; * const result = haveEmptyCell(matrix, 3, 3); * * export default result */ function haveEmptyCell(matrix: Array>, rowsNum: number, colsNum: number): boolean < let empty: boolean = false; for (let x = 0; x < rowsNum; x++) < for (let y = 0; y < colsNum; y++) < const element: any = matrix[x][y]; if (!element) < empty = true; break; >> if (empty) break; > return empty; > export default haveEmptyCell

Функция Winner calculation

Winner calculation — это функция, которая вычисляет победителя по горизонтальной, вертикальной и диагональной плоскостям.

Bit позволяет увидеть документы компонента и результаты тестов:

Код функции

/** * @description * check winner horizontal, vertical and diagonal * @param > matrix 2d array with X and O * @param rowsNum number of rows * @param colsNum number of columns * @param numToWin the number of matching to win * @param lastRow the row number of the square player click * @param lastCol the column number of the square player click * @returns return the winner, X or O or '' if no one win. * @example * import winnerCalc from '@bit/joshk.tic-tac-toe-game.utils.winner-calc'; * * const matrix = [ * ['O', 'O', 'X'], * ['O', 'X', ''], * ['X', '', ''] * ]; * const result = winnerCalc(matrix, 3, 3, 3, 0, 2); * * export default result */ import haveEmptyCell from '../HaveEmptyCell' function winnerCalc(matrix: Array, rowsNum: number, colsNum: number, numToWin: number, lastRow: number, lastCol: number): string < let winner: string = ''; let match: number = 0; const lastValue: string = matrix[lastRow][lastCol]; //check Horizontal for (let c = 0; c < colsNum; c++) < let currentValue = matrix[lastRow][c]; if (currentValue === lastValue) match++; else match = 0; if (match === numToWin) < winner = lastValue; break; >> if (winner !== '') return winner; match = 0; //check Vertical for (let r = 0; r < rowsNum; r++) < let currentValue = matrix[r][lastCol]; if (currentValue === lastValue) match++; else match = 0; if (match === numToWin) < winner = lastValue; break; >> if (winner !== '') return winner; //check diagonal top-left to bottom-right - include middle match = 0; for (let r = 0; r rowPosition++; > if (winner !== '') break; > if (winner !== '') return winner; //check diagonal top-left to bottom-right - after middle match = 0; for (let c = 1; c columnPosition++; > if (winner !== '') break; > if (winner !== '') return winner; //check diagonal bottom-left to top-right - include middle match = 0; for (let r = rowsNum - 1; r >= rowsNum - numToWin - 1; r--) < let rowPosition = r; for (let column = 0; column < colsNum && rowPosition < rowsNum && rowPosition >= 0; column++) < let currentValue = matrix[rowPosition][column]; if (currentValue === lastValue) match++; else match = 0; if (match === numToWin) < winner = lastValue; break; >rowPosition--; > if (winner !== '') break; > if (winner !== '') return winner; //check diagonal bottom-left to top-right - after middle match = 0; for (let c = 1; c < colsNum; c++) < let columnPosition = c; for (let row = rowsNum - 1; row < rowsNum && row >= 0 && columnPosition < colsNum && columnPosition >= 1; row--) < console.log(`[$][$]`); let currentValue = matrix[row][columnPosition]; if (currentValue === lastValue) match++; else match = 0; if (match === numToWin) < winner = lastValue; break; >columnPosition++; > if (winner !== '') break; > if (winner !== '') return winner; if(haveEmptyCell(matrix, rowsNum, colsNum) === false) < winner = '-1'; >return winner; > export default winnerCalc

Проект доступен в моей коллекции на Bit и в моём репозитории GitHub.

Не стесняйтесь комментировать эту статью и подписывайтесь на мой Twitter.

  • разработка игр
  • разработка приложений
  • игры
  • начинающим
  • своими руками
  • крестики-нолики
  • крестики нолики
  • typescript
  • mocha
  • react
  • программирование игр
  • программирование
  • программирование для начинающих

Пишем игру крестики-нолики на Python на двоих и против компьютера

Если вы учитесь программировать, то программирование игр — это очень хороший способ освоить алгоритмы и структуры данных. В этом уроке мы разберем запрограммируем игру крестики нолики на Python. Полный код программы с искусственным интеллектом для игрока-компьютера занимает всего 140 строк. В варианте игры где человек играет против человека — раза в два меньше.

Игра крестики-нолики хороша тем, что ее правила знакомы всем с детства и понятны каждому. Это даст вам возможность сосредоточиться именно на процессе программирования, а не на анализе правил игры. В процессе работы над игрой крестики-нолики мы будем использовать только стандартные функции языка Python без подключения каких либо сторонних библиотек.

Шаг 1. Подбор структуры данных

Программирование любой игры начинается с моделирования ее объектов средствами языка программирования. Когда мы программируем игру крестики нолики нам нужно подумать где в программе хранить поле для игры с ходами которые сделали игроки.

В языке программирования Python наилучшим выбором будет список из 9 значений. Назовем его maps. Первоначально этот список будет заполнен цифрами от 1 до 9. Это сделано для удобства организации диалога с пользователем. Пользователю будет достаточно ввести символ от 1 до 9, что бы мы смогли понять куда он сделал ход. После того как ход сделан, цифру мы заменим на символ нолика или крестика. (Можно было заполнить цифрами от 0 до 8. В этом случает было бы удобнее работать — цифра и есть индекс элемента в списке, но первый нолик будет смущать игроков).

Еще мы создадим второй список victories в котором будем хранить информацию обо всех выигрышных комбинациях. И нам будет нужно создать функцию print_maps, которая будет выводить содержимое нашего списка maps на экран.

# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8])

Со структурами данных разобрались.

Шаг 2. Выполнение очередного хода и проверка на выигрыш

Карта для игры у нас есть, отображать ее мы умеем. Теперь нужно создать две вспомогательные функции, прежде чем мы приступим к программированию основного цикла игры.

Первая функция будет рисовать на поле крестик или нолик, в зависимости от того что в нее передали. Позицию в нее так же нужно будет передавать. Вставлять переданный элемент мы будем по индексу. Индекс определим функцией index (если бы мы пронумеровали от 0 до 8 элементы в maps, то переданное значение и было бы индексом. Можете попробовать — будет на одну строчку кода меньше.)

# Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol

После каждого ходы мы должны проверять — не победил ли кто то из игроков. Для этого переберем все победные линии из списка victories и проверим нет ли там комбинации из трех крестиков или трех ноликов.

# Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win

Эта функция вернет «X» в случае победы крестиков и «O» в случае победы ноликов.

Шаг 3. Основный игровой цикл

Ну вот мы и подошли к созданию основного игрового цикла. Нам предстоит крутиться в цикле до момента пока кто нибудь не выиграет. Игроки будут по очереди делать ходы, мы будем проверять каждый раз не победил ли кто и как только кто то побеждает — выходим из цикла и завершаем программу.

# Основная программа game_over = False player1 = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if player1 == True: symbol = "X" step = int(input("Человек 1, ваш ход: ")) else: symbol = "O" step = int(input("Человек 2, ваш ход: ")) step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False player1 = not(player1) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)

Вот полный код программы крестики-нолики на Python для двух игроков:

# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8]) # Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol # Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win # Основная программа game_over = False player1 = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if player1 == True: symbol = "X" step = int(input("Человек 1, ваш ход: ")) else: symbol = "O" step = int(input("Человек 2, ваш ход: ")) step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False player1 = not(player1) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)

Вот так выглядит процесс игры в крестики-нолики для 2 игроков:

1 2 3 4 5 6 7 8 9 Человек 1, ваш ход: 5 1 2 3 4 X 6 7 8 9 Человек 2, ваш ход: 1 O 2 3 4 X 6 7 8 9 Человек 1, ваш ход: 6 O 2 3 4 X X 7 8 9 Человек 2, ваш ход: 4 O 2 3 O X X 7 8 9 Человек 1, ваш ход: 3 O 2 X O X X 7 8 9 Человек 2, ваш ход: 7 O 2 X O X X O 8 9 Победил O

Шаг 4. Добавление алгоритма для искусственного интеллекта в игре крестики-нолики

А вот теперь мы подобрались к самому интересному моменту в программировании. Нам нужно создать искусственный интеллект, который всегда будет выигрывать или сводить игру к ничьей. На самом деле в игре крестики-нолики такой алгоритм написать совсем не сложно.

Для написания такого алгоритма нам понадобится вспомогательная функция, которая будет проверять все победные линии в игре и подсчитывать в них количество крестиков и ноликов. Если функция находит такую линию, то она возвращает позицию на этой линии куда нужно сделать ход. Например мы будем отслеживать линии где противник поставил два крестика и обязательно поставим нолик, что бы не дать ему выиграть. Вот эта функция:

#Искусственный интеллект: поиск линии с нужным количеством X и O на победных линиях def check_line(sum_O,sum_X): step = "" for line in victories: o = 0 x = 0 for j in range(0,3): if maps[line[j]] == "O": o = o + 1 if maps[line[j]] == "X": x = x + 1 if o == sum_O and x == sum_X: for j in range(0,3): if maps[line[j]] != "O" and maps[line[j]] != "X": step = maps[line[j]] return step

А сейчас мы напишем функцию для поиска очередного лучшего хода для искусственного интеллекта. Запрограммируем такой алгоритм для каждого хода (компьютер играет ноликами):

  1. Если этим ходом можем выиграть — выигрываем (уже 2 нолика стоят на одной из линий). Иначе идем к шагу 2.
  2. Если можем помешать выиграть человеку — мешаем ( у человека уже 2 крестика на линии — ставим на нее нолик). Иначе идем к шагу 3.
  3. Если на линии одна наша фигура — ставим вторую. Если пока ни одной нашей фигуры — идем к шагу 4.
  4. Ставим нолик в центр. Если центр занят идем к шагу 5.
  5. Ставим в левый верхний угол.

А вот так все это выглядит в программе на Python:

#Искусственный интеллект: выбор хода def AI(): step = "" # 1) если на какой либо из победных линий 2 свои фигуры и 0 чужих - ставим step = check_line(2,0) # 2) если на какой либо из победных линий 2 чужие фигуры и 0 своих - ставим if step == "": step = check_line(0,2) # 3) если 1 фигура своя и 0 чужих - ставим if step == "": step = check_line(1,0) # 4) центр пуст, то занимаем центр if step == "": if maps[4] != "X" and maps[4] != "O": step = 5 # 5) если центр занят, то занимаем первую ячейку if step == "": if maps[0] != "X" and maps[0] != "O": step = 1 return step

Немного перепишем и основной цикл игры. Теперь вместо человека 2 ход будет делать компьютер. Компьютер каждый раз будет говорить куда делает ход. Если компьютер не дает ответ, значит наметилась ничья — завершаем партию и объявляем ничью. Вот таким станет основной цикл игры, когда мы перепишем программу крестики-нолики под игру против компьютера:

# Основная программа game_over = False human = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if human == True: symbol = "X" step = int(input("Человек, ваш ход: ")) else: print("Компьютер делает ход: ") symbol = "O" step = AI() # 3. Если компьютер нашел куда сделать ход, то играем. Если нет, то ничья. if step != "": step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False else: print("Ничья!") game_over = True win = "дружба" human = not(human) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)

Готовая программа для игры в крестики нолики против компьютера на Python

Вот готовая программа для для игры против искусственного интеллекта.

# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8]) # Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol # Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win #Искусственный интеллект: поиск линии с нужным количеством X и O на победных линиях def check_line(sum_O,sum_X): step = "" for line in victories: o = 0 x = 0 for j in range(0,3): if maps[line[j]] == "O": o = o + 1 if maps[line[j]] == "X": x = x + 1 if o == sum_O and x == sum_X: for j in range(0,3): if maps[line[j]] != "O" and maps[line[j]] != "X": step = maps[line[j]] return step #Искусственный интеллект: выбор хода def AI(): step = "" # 1) если на какой либо из победных линий 2 свои фигуры и 0 чужих - ставим step = check_line(2,0) # 2) если на какой либо из победных линий 2 чужие фигуры и 0 своих - ставим if step == "": step = check_line(0,2) # 3) если 1 фигура своя и 0 чужих - ставим if step == "": step = check_line(1,0) # 4) центр пуст, то занимаем центр if step == "": if maps[4] != "X" and maps[4] != "O": step = 5 # 5) если центр занят, то занимаем первую ячейку if step == "": if maps[0] != "X" and maps[0] != "O": step = 1 return step # Основная программа game_over = False human = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if human == True: symbol = "X" step = int(input("Человек, ваш ход: ")) else: print("Компьютер делает ход: ") symbol = "O" step = AI() # 3. Если компьютер нашел куда сделать ход, то играем. Если нет, то ничья. if step != "": step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False else: print("Ничья!") game_over = True win = "дружба" human = not(human) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)

Вот пример игрового процессе. В этой игре победил компьютер:

1 2 3 4 5 6 7 8 9 Человек, ваш ход: 1 X 2 3 4 5 6 7 8 9 Компьютер делает ход: X 2 3 4 O 6 7 8 9 Человек, ваш ход: 2 X X 3 4 O 6 7 8 9 Компьютер делает ход: X X O 4 O 6 7 8 9 Человек, ваш ход: 4 X X O X O 6 7 8 9 Компьютер делает ход: X X O X O 6 O 8 9 Победил O

Вы можете прямо сейчас поиграть в крестики нолики против компьютера или друг против друга. Просто скопируйте текст программы на этой странице и вставьте его в поле программы в этом онлайн эмуляторе Python: https://replit.com/languages/python3 или тут https://www.onlinegdb.com/online_python_compiler

Домашнее задание

У нас получилась довольно беспощадная программа. Ее искусственный интеллект победить не возможно. Человек либо проигрывает, либо играет в ничью. Подумайте как можно дать человеку иногда выигрывать? Возможно вы захотите создать несколько уровней. Например в первом искусственный интеллект очень слаб, дальше он умнет. Или у вас будет на выбор несколько режимов игры на выбор: слабый ИИ, средний и непобедимый.

В общем ваша задача немного ослабить искусственный интеллект в нашей игре крестики-нолики. Сделать его чуть более человечным.

Пишем простые крестики-нолики на C++

Продолжаю небольшой цикл статей про простые проекты, которые будут полезны новичкам. В прошлый раз мы написали BlackJack в упрощенном виде, а в этой статье напишем классические крестики-нолики на двух игроков. Так как в каждой такой статье мы должны учиться чему-то новому, то на этот раз познакомимся с тернарными операторами и сразу же применим их на практике. Как всегда в конце будет ряд идей по тому, как можно улучшить игру.

С чего начать?

Как всегда начнем с проектирования идеи и изобразим алгоритм на псевдо блок-схеме:

Приступаем к коду

В начале проекта подключим все используемы заголовочные файлы стандартной библиотеки и объявим массив boardData, в котором будем хранить ходы игроков, заполним его числами от нуля до девяти. Объявим функции checkWin() — проверка выигрышной ситуации и boardDrawing() — отрисовка игрового поля в консоль, далее реализуем их:

Начнем с функции для отрисовки игрового поля. Она максимально простая и выводит в консоль поле в виде квадрата с девятью ячейками, которые по порядку заполняются элементами массива boardData[], начинаю элементом с индексом 1. Перед самим игровым полем очистим консоль с помощью строки system(“clear”), чтобы во время игры она не утонула в однотипных сообщениях с состоянием игры, а обновлялась ходу, затем выведем название игры и стороны за которые играют пользователи:

Теперь займемся функцией проверки выигрышных ситуаций. Рассмотрим возможные комбинации, когда игра заканчивается победой одной из сторон:

Остается описать все возможные случае через конструкцию if else, в выигрышных ситуациях (первые восемь) функция возвращает “1”, в ситуации, когда все ячейки уже заняты, а победы так и не был найдено (девятый случай), возвращает “0”, иначе возвращает “-1”:

Функции логики готовы, теперь перейдем к самой игре. Объявим функцию main() и в теле запишем несколько важных переменных — player для хранения номера игрока, i для состояния игры (продолжать или оповестить о победе/ничьей), choice для хранения номера выбранной пользователем ячейки и mark для символа которым пользователь совершает ход:

Далее запустим цикл do while, который будет у нас работать до тех пор, пока переменная i равна “-1” (выигрышных ситуаций не найдено). В начале цикла обновим игровое поле, используя ранее написанную функцию boardDrawing() и изменим переменную player, используя тернарный оператор для определения очередности хода. Затем выведем сообщение с предложением выбрать номер ячейки для хода и запишем его в переменную choice:

Как работает тернарный оператор?
Тернарный оператор имеет вид УСЛОВИЕ ? КОМАНДА 1 : КОМАНДА 2.
В блоке условия мы записываем любое необходимое условие и ставим после него знак вопроса (?). Далее, в этой же строке, после знака вопроса пишем код первой команды, которая выполнится только в том случае, если условие вернет истину (true). После этой команды ставим двоеточие (:) и пишем вторую команду, которая выполнится, если условие вернет ложь (false).
В нашем случае конструкцию тернарного оператора можно трактовать как “если переменная player нечетная, то передадим ей значение “1”, если четная, то “2””.
Более подробно можно почитать тут.

Далее определим каким символом будет ходить игрок (крестик/нолик). Опять обратимся к тернанрому оператору, который имеет в виду “если переменная player равна “1”, то переменная mark равна “X”, иначе “O””. Затем запишем в выбранную игроком ячейку символ, присвоенный ему. Для этого с помощью конструкции if else будем проверять условие “выбор пользователя равен n и ячейка под таким же номером n свободна”. В случае если условие сработало, то в ячейку под номером n помещаем символ пользователя, иначе выводим сообщение об ошибке и просим пользователя выбрать другую ячейку. В самом конце цикла do while проверяем поле на наличие выигрышных ситуаций путем присвоения переменной i возврата от вызова функции checkWin():

В завершении снова обновим игровое поле, вызвав функцию boardDrawing(). Затем рассмотрим случаи для вывода сообщений о победе/ничьей с использованием if else. Если переменная i равна “1”, то выведем сообщение о победе с номером победившего игрока, иначе выведем сообщение ничеьей:

Компиляция проекта

Для сборки достаточно открыть папку проекта в консоли и поочередно ввести команды со скриншота. Полностью код проекта доступен на GitHub и Repl.it с возможностью запуска в песочнице прямо в браузере:

Что дальше?

Нам удалось написать простую версию классической игры крестики-нолики, но проект можно заметно улучшить следующим образом:

  • на данный момент, если на этапе выбора номера ячейки для осуществления хода пользователь вместо числа укаже любой другой символ, то игра сломается и начнет бесконечно обновлять поле, было бы хорошо решить эту проблему и обрабатывать подобные случаи;
  • наша игра запускается в консоли и не имеет интерфейса. Если поработать над его реализацией, добавить отслеживание событий мышки и клавиатуры, музыку, звуковые эффекты, то игра станет заметно лучше;
  • на данный момент пользователи не мог выбирать сторону за которую будут играть, а подстраиваются под условия игры, можно было бы дать им такую возможность.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *