SunDrop / gist:9057069
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| package test ; |
| import java . util . Scanner ; |
| public class PlusZerro |
| // Размер игровово поля |
| private static final byte bPoleSize = 3 ; |
| // Задаем поле как (!)одномерный массив bPoleSize*bPoleSize (неожиданно?) |
| private static String [] pole = new String [ bPoleSize * bPoleSize ]; |
| // А тут у нас задается текущий игрок |
| private static byte bPlayerNum = 0 ; |
| public static void main ( String [] args ) throws Exception |
| int iTmp = 0 ; |
| Scanner sc = new Scanner ( System . in ); |
| // Инициализация поля |
| for ( int i = 0 ; i < bPoleSize * bPoleSize ; i ++) |
| pole [ i ] = Integer . toString (++ iTmp ); |
| // Играем, пока не наступит конец игры |
| while (! isGameEnd ()) |
| nextPlayer (); |
| while ( true ) |
| System . out . println ( » \n Ход игрока » + bPlayerNum ); |
| showPole (); // Рисуем поле |
| System . out . print ( «Наберите число, куда вы хотите вставить » + ( 1 == bPlayerNum ? «крестик» : «нолик» ) + «: » ); |
| if ( sc . hasNextInt ()) < // проверяем, есть ли в потоке целое число |
| iTmp = sc . nextInt () — 1 ; // считывает целое число с потока ввода и сохраняем в переменную |
| if ( isValidInput ( iTmp )) |
| break ; |
| > |
| System . out . println ( «Вы ввели неправильное число. Повторите ввод» ); |
| sc . next (); |
| > |
| try |
| putX ( iTmp ); // Вставляем на поле крестик или нолик |
| > catch ( Exception e ) |
| System . out . println ( «Что-то пошло не так ;(» ); |
| > |
| > |
| showPole (); |
| > |
| /** |
| * Проверяем корректность ввода. Введенное число должно быть по размеру поля |
| * и поле должно быть в этом месте еще не заполнено |
| */ |
| private static boolean isValidInput ( int iIn ) |
| if ( iIn >= bPoleSize * bPoleSize ) return false ; |
| if ( iIn < 0 ) return false ; |
| switch ( getX ( iIn )) |
| case ‘O’ : |
| case ‘X’ : |
| return false ; |
| > |
| return true ; |
| > |
| /** |
| * Функция задает номер следующего игрока |
| */ |
| private static void nextPlayer () |
| bPlayerNum = ( byte ) ( 1 == bPlayerNum ? 2 : 1 ); |
| > |
| /** |
| * Определяем, наступил конец игры или нет |
| * Условия: |
| * 1) Победили крестики |
| * 2) Победили нолики |
| * 3) Кончились ходы |
| */ |
| private static boolean isGameEnd () |
| int i , j ; |
| boolean bRowWin = false , bColWin = false ; |
| // Проверка победы на колонках и столбиках |
| for ( i = 0 ; i < bPoleSize ; i ++) |
| bRowWin = true ; |
| bColWin = true ; |
| for ( j = 0 ; j < bPoleSize - 1 ; j ++) |
| bRowWin &= ( getXY ( i , j ). charAt ( 0 ) == getXY ( i , j + 1 ). charAt ( 0 )); |
| bColWin &= ( getXY ( j , i ). charAt ( 0 ) == getXY ( j + 1 , i ). charAt ( 0 )); |
| > |
| if ( bColWin || bRowWin ) |
| System . out . println ( «Победил игрок » + bPlayerNum ); |
| return true ; |
| > |
| > |
| // Проверка победы по диагоналям |
| bRowWin = true ; |
| bColWin = true ; |
| for ( i = 0 ; i < bPoleSize - 1 ; i ++) |
| bRowWin &= ( getXY ( i , i ). charAt ( 0 ) == getXY ( i + 1 , i + 1 ). charAt ( 0 )); |
| bColWin &= ( getXY ( i , bPoleSize — i — 1 ). charAt ( 0 ) == getXY ( i + 1 , bPoleSize — i — 2 ). charAt ( 0 )); |
| > |
| if ( bColWin || bRowWin ) |
| System . out . println ( «Победил игрок » + bPlayerNum ); |
| return true ; |
| > |
| // Проверка существования новых ходов |
| for ( i = 0 ; i < bPoleSize * bPoleSize ; i ++) |
| switch ( getX ( i )) |
| case ‘O’ : |
| case ‘X’ : |
| break ; |
| default : |
| return false ; |
| > |
| > |
| if ( bPoleSize * bPoleSize <= i ) |
| System . out . println ( «Ничья. Кончились ходы.» ); |
| return true ; |
| > |
| // Продолжаем игру |
| return false ; |
| > |
| /** |
| * Получает значение координаты на поле |
| */ |
| private static String getXY ( int x , int y ) |
| return pole [ x * bPoleSize + y ]; |
| > |
| /** |
| * Получает значение координаты на поле |
| */ |
| private static char getX ( int x ) |
| return pole [ x ]. charAt ( 0 ); |
| > |
| /** |
| * Вставляет на поле крестик или нолик |
| */ |
| private static void putX ( int x ) |
| pole [ x ] = 1 == bPlayerNum ? «X» : «O» ; |
| > |
| /** |
| * Вывести игровое поле |
| */ |
| private static void showPole () |
| for ( int i = 0 ; i < bPoleSize ; i ++) |
| for ( int j = 0 ; j < bPoleSize ; j ++) |
| System . out . printf ( «%4s» , getXY ( i , j )); |
| > |
| System . out . print ( » \n » ); |
| > |
| > |
| > |
Игра крестики нолики на Си
Задача заключается в реализации игры крестики-нолики на языке программирования Си++. Программа должна создавать поле для игры в крестики-нолики. Игроки поочерёдно вводят номер строки и столбца, в котором они хотят сделать ход. Игра проходит таким образом до тех пор, пока кто-либо из игроков не победит или не произойдёт ничья. Если клетка уже занята, то программа не должна дать сделать некорректный ход. Если кто-либо победит, программа должна уведомить об этом, после чего пользователь должен нажать кнопку, чтобы выйти из программы.
Краткое описание алгоритма программы крестики нолики на си. При запуске программы показывается игровое поле, после чего первый игрок должен ввести номер строки и столбца клетки, на которой он хочет сделать ход. Затем такие же действия должен совершить второй игрок. Программа считывает номер строки и столбца, введённые пользователем, проверяет, занято ли выбранное место, если занято, то предлагает сделать ход заново, если нет, то ход будет сделан. Если кто-либо из игроков победил, программа выводит сообщение о том, кто из игроков победил, после чего пользователь нажимают любую кнопку, чтобы выйти из программы.

Переменные и массивы. Массив a[i][j] отвечает за номер строки и столбца каждой клетки. i – номер строки клетки, j – номер столбца клетки. Подробно о работе с массивами в Си
Если a[i][j] =0 то данная клетка свободна
Если a[i][j] =1 то данная клетка занята ноликом
Если a[i][j] =2 то данная клетка занята крестиком
Переменные igra и res отвечают за состояние игры, если эти переменные равны нулю, то пока никто не победил, если 1 – победил нолик, если 2 – победил крестик. В переменную hi записывается ход игрока, например, «12» — первая строка, второй столбец. str и sto отвечают за номер выбранных строки и столбца. ver – проверяет возможность хода, если ход невозможен – 0, если возможен – 1.
В программе присутствует четыре процедуры. Подробно о процедурах в Си.
nul() обнуляет находящиеся элементы каждой клетки на поле, это нужно для начала игры. Если клетка была пустой, то процедура оставит её пустой, если клетка была занята крестиком или ноликом, процедура уберёт их оттуда.

visual() предназначена для визуализации поля при каждом ходе. Если клетка пуста, то ничего в ней не нарисуется, если на клетке сделали ход, то процедура нарисует этот ход крестик или нолик.
Алгоритм процедуры визаулизации поля

Процедура hod(n). Процедура запрашивает номер строки и столбца в клетки, в которую ходит игрок и назначает на выбранную клетку нолик или крестик. n – номер игрока, который должен ходить в данный момент. Если n = 1 то ходит нолик, если n = 2, то ходит крестик.
Для проверки корректности хода, клетка должна быть пуста и номер должен быть один из номеров таблицы 3 на 3
Алгоритм процедуры ход.

proverka() при каждом ходе проверяет, не победил ли какой-либо игрок, и возвращает одно из возможных значений. Если proverka() = 0, то никто пока не победил или случилась ничья, если proverka() = 1, то победил нолик, если 2 – крестик.
При проверке мы проверяем в цикле for все строки и ищем есть в какой либо строке все нолики
// пробегаем по всем строкам
// проверка все ли в строке нолики
Аналогично идет проверрка по всем столбцам и проверка диагоналей

Полный код программы.
int a[3][3];// объявляем текущее поле 3 строки и 3 столбца. если нет ничего то поле 0, если нолик, то 1, если крестик то 2
// пробегаем по всем строкам
for(i=1; i // пробегаем по всем столбцам
for (j=1;j <=3;j++)
a[i][j]=0;
>
>
>
// вывод поля на экран
void visual()
int i,j;
printf (» 1 2 3\n»);
// переходим на следующую строку
// Ход игрока n — номер игрока На выходе: 0 —
void hod (int n)
int hi;// ход игрока
int sto; // номер столбца в ходе
int str; // номер строки в ходе
int ver=0; // проверка на корректность хода, если нельзя сделать такой ход то 0, если можно то 1
// запрашиваем ход, пока не будет введен корректный ход
while (ver==0)
// ввод хода
printf («Ваш ход. Введите номер строки и столбца. \n»);
scanf(«%d», &hi);
// определяем номер столбца — это последняя цифра в ходе
sto=hi%10;
// определяем номер строки хода — это первая цифра
str=(hi-sto)/10;
// проверка корректности хода, клетка должна быть пуста и номер должен быть один из номеров таблицы 3 на 3
if (a[str][sto]==0 && (hi==11 || hi==12 || hi==13 || hi==21 || hi==22 || hi==23 || hi==31 || hi==32 || hi==33 )) (ver=1);
>
// если ходил игрок 1 то поле хода 1
if ( n==1)
// если ходил игрок 2 то поле хода 2
if ( n==2)
>
// Проверка Если ни один игрок не выиграл то 0, если выиграл нолик то 1 если выиграл крестик то 2
int proverka()
< int i;
int res; // значение функции. Если ни один игрок не выиграл то 0, если выиграл нолик то 1 если выиграл крестик то 2
res=0;
// проверяем выигрыш первого игрока
// пробегаем по всем строкам
for (i=1; i // проверка все ли в строке нолики
if (a[i][1]==1 && a[i][2]==1 && a[i][3]==1 )
>
// пробегаем по всем столбцам
for (i=1; i // проверка все ли в столбце нолики
if (a[1][i]==1 && a[2][i]==1 && a[3][i]==1 )
>
// проверка диагоналей
if (a[1][1]==1 && a[2][2]==1 && a[3][3]==1 )
if (a[1][3]==1 && a[2][2]==1 && a[3][1]==1 )
// проверяем выигрыш второго игрока
// пробегаем по всем строкам
for (i=1; i // проверка все ли в строке крестики
if (a[i][1]==2 && a[i][2]==2 && a[i][3]==2 )
>
// пробегаем по всем столбцам
for (i=1; i // проверка все ли в столбце крестики
if (a[1][i]==2 && a[2][i]==2 && a[3][i]==2 )
>
main()
int igra;// состояние игры если победил нолик то 1 , если победил крестик то 2 если никто пока не победил то 0
// обнуляем очищаем поле
nul();
// отображаем игровое поле
visual();
igra=0;
// пока никто не победил продолжается игра
while (igra==0)
// ход 1 го игрока нолика
hod(1);
// отображаем игровое поле
visual();
// проверка на победу первого игрока
igra=proverka();
// если первый игрок не победил , то ходит второй игрок
if (igra==0)
// ход второго игрока
hod(2);
// отображаем игровое поле
visual();
// проверка на победу второго игрока
igra=proverka();>
Задание для самостоятельной работы.
Расширьте игровое поле до размеров 5×5 клеток.
Создание игры «Крестики-нолики» при помощи 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
- программирование игр
- программирование
- программирование для начинающих
Теория разработки ИИ на примере крестиков-ноликов
Эта статья будет посвящена тому, как написать простую, даже банальную игру: крестики-нолики. Причем самые обыкновенные — 3 на 3. Здесь я подробно опишу этапы написания программы и все рассуждения.
Статью можно поделить на две части — эта, где изложена теория написания крестиков-ноликов (далее — КН), и вторая — исходники программы, снабженные обильными комментариями (программа написана на C++ и адаптирована под MSVS6).
Так вот. Основа любой более или менее продвинутой игры — искусственный интеллект (ИИ). Я не подразумеваю тетрис или что-нибудь типа игры «Lines», где игрок не должен бороться с компьютером. И в наших КН ИИ должен присутствовать — пускай слабенький и тупенький, но хоть какой-то.
Для создания ИИ (если, конечно, не строить планы создать андроида) нужно сначала определиться, что он должен делать. Даже самый навороченный современный ИИ не может делать всего; интервал его способностей крайне ограничен. У нашего ИИ будет только одна задача — руководствуясь определенными правилами, поставить крестик (или нолик) в определенную клетку.
И нам надо эти правила сформулировать. Для этого надо напрячь мозги и вспомнить все тонкости игры в КН, а лучше — сыграть с десяток партий с приятелем. Поиграли, поиграли, и вывели, что:
- При идеальной игре выиграть невозможно
- Единственный шанс выиграть (если противник не абсолютно туп) — создать безвыходную ситуацию. Это достигается созданием определенной комбинации ваших меток, при которой, как бы враг ни походил, вы всегда можете выиграть партию. Эти комбинации — заполнены 3 угловые точки, причем между 2-мя парами из них должны быть пустые клетки; заполнены 2 угловые и центральная, и соблюдается то же правило.
- Из второго пункта можно понять, что существуют «выигрышные» позиции — это (1,1), (1,3), (3,1), (3,3), (2,2).
При формулировке я пользовался следующими определениями:
- Метка — крестик или нолик.
- Линия — она и есть линия, набор из 3-х клеток.
Теперь, поняв правила игры, можно писать ИИ.
Первым делом, компьютер должен проверить на наличие выигрышных ситуаций. Правда ведь, если компьютер может походить так, что выиграет, остальное его не колышет?
Затем, если ИИ не может выиграть с одного хода, он должен проверить наличие опасных для него ситуаций (т.е. противник выиграет следующим ходом).
Если и таковые ситуации отсутствуют, то компьютер должен походить просто так, а лучше — на одну из выигрышных позиций (причем лучше проверить, можно ли создать противнику безвыходную ситуацию). Если все таковые заняты, то просто на первую попавшуюся свободную клетку.
Итак, стоит обобщить вышеизложенное и кратко изложить шаги ИИ:
- Проверить, нельзя ли завершить игру победой.
- Проверить, не может ли враг на следующем ходе завершить игру победой.
- Проверить, можно ли создать противнику безвыходную ситуацию
- Если нельзя, то походить просто на одну из выгодных позиций.
- Если и это невозможно, походить на первую попавшуюся клетку.
Вот, в общем, и все. В примере компьютер играет сам с собой — и во всех тестах, что я проводил, игра заканчивается ничьей.