крестики нолики код на c#

Код игры «крестики-нолики» на языке программирования C#

Доброго времени суток. На данной странице я приведу пример создания всеми любимой логической игры под названием «крестики-нолики». Так как программирую я в основном на C#, то и представленная программа будет так же на данном языке программирования. Но даже если Вы не знакомы с C#, а программируете на C++, Java, Delphi или на любом другом языке, данный пост в любом случае поможет Вам, так как тут я приведу алгоритм работы такой программы.

Рассматривать программу, предназначенную для игры вдвоем, мы не будем. Сейчас мы рассмотрим программу, в которой противником пользователя будет компьютер, ведь для программа для игры двоих будет состоять только из необходимости прорисовки ходов, что, думаю, довольно просто. Поэтому мы рассмотрим игру компьютера.

Всю программу можно поделить на две большие части:

  1. Компьютер ходит первым(крестики)
  2. Компьютер ходит вторым(нолики)

Далее я опишу стратегию игры компьютера, которого мы собираемся сделать непобедимым.

Стратегия игры «крестики-нолики»

Основополагающие правила

Независимо от того, кто ходит первым и какая сейчас позиция, компьютер перед каждый ходом должен проверить две вещи:

  1. Можно ли выиграть? Если да, то компьютер выигрывает!
  2. Угрожает ли нам опасность проиграть следующим ходом? Если да, то закрываем такую возможность!

Компьютер ходит первым

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

Компьютер ходит вторым

Когда компьютер ходит вторым, а пользователь, соответственно, первым, может возникнуть несколько ситуаций:

  1. Первый ход в центр. Тогда ходим до конца игры в любой из углов, а если это невозможно, то в любую свободную клетку.
  2. Первый ход в угол. Отвечаем ходом в центр. Следующие ходы — в любой из свободных углов, иначе — в свободные клетки.
  3. Первый ход в сторону. Ситуация аналогичная предыдущей: по максимуму закрываем углы, после чего ходим туда, где осталось место.

Вот такая стратегия игры в «крестики-нолики». Она не слишком сложная, и теперь мы попытаемся ее реализовать.

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

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

Проверка на возможность победы

Перед тем как писать код, необходимо уяснить смысл ее работы. Выигрышных комбинаций всего 8. Можете посмотреть скриншоты: я специально выделил и указал номер этих комбинаций(они понадобятся нам). Эти номера комбинаций будет хранить переменная cherta, которая сразу после выигрыша отдаст команду нарисовать выигрышную черту.

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

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

  • Изначально матрица обнуляется и это значит, что все 9 клеток пусты. Перед каждый ходом нужно проверять, пустая ли клетка, и можно ли на нее сходить. Полезно, пользуйтесь.
  • Для обозначения компьютера я использовал цифру 1.
  • Для обозначения пользователя я использовал цифру 2.

Далее нам необходимо будет создать специальный метод, который будет отрисовывать игровое поле, используя данные этой матрицы(метод paint, который будет мелькать в коде). А вот собственно и код данного метода:

[code language=»csharp»]if (((a[0, 0] + a[0, 1] + a[0, 2]) == 2) && (a[0, 0] == 1 || a[0, 1] == 1 || a[0, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[0, j] == 0)
{
a[0, j] = 1;
}
}
win = 1;
paint();
cherta = 4;
}
else
{
if (((a[1, 0] + a[1, 1] + a[1, 2]) == 2) && (a[1, 0] == 1 || a[1, 1] == 1 || a[1, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[1, j] == 0)
{
a[1, j] = 1;
}
}
win = 1;
paint();
cherta = 5;
}
else
{
if (((a[2, 0] + a[2, 1] + a[2, 2]) == 2) && (a[2, 0] == 1 || a[2, 1] == 1 || a[2, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[2, j] == 0)
{
a[2, j] = 1;
}
}
win = 1;
paint();
cherta = 6;
}
else
{
if (((a[0, 0] + a[1, 0] + a[2, 0]) == 2) && (a[0, 0] == 1 || a[1, 0] == 1 || a[2, 0] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 0] == 0)
{
a[i, 0] = 1;
}
}
win = 1;
paint();
cherta = 1;
}
else
{
if (((a[0, 1] + a[1, 1] + a[2, 1]) == 2) && (a[0, 1] == 1 || a[1, 1] == 1 || a[2, 1] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 1] == 0)
{
a[i, 1] = 1;
}
}
win = 1;
paint();
cherta = 2;
}
else
{
if (((a[0, 2] + a[1, 2] + a[2, 2]) == 2) && (a[0, 2] == 1 || a[1, 2] == 1 || a[2, 2] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 2] == 0)
{
a[i, 2] = 1;
}
}
win = 1;
paint();
cherta = 3;
}
else
{
if (((a[0, 0] + a[1, 1] + a[2, 2]) == 2) && (a[0, 0] == 1 || a[1, 1] == 1 || a[2, 2] == 1))
{
if (a[0, 0] == 0)
a[0, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[2, 2] == 0)
a[2, 2] = 1;
win = 1;
paint();
cherta = 7;
}
else
{
if (((a[2, 0] + a[1, 1] + a[0, 2]) == 2) && (a[2, 0] == 1 || a[1, 1] == 1 || a[0, 2] == 1))
{
if (a[2, 0] == 0)
a[2, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[0, 2] == 0)
a[0, 2] = 1;
win = 1;
paint();
cherta = 8;
}
}
}
}
}
}
}
}[/code]

Тут так же появляется переменная win, которая хранит значение победившей стороны. Я использовал только два значения: 0-ничья, 1 — победа компьютера. В моей программе нет даже поздравления пользователя с возможной победой, так как это невозможно.

Проверка на возможность проигрыша следующим ходом

[code language=»csharp»]if ((a[0, 0] + a[0, 1] + a[0, 2]) == 4 && a[0, 0]!=1 && a[0, 1]!=1 && a[0, 2]!=1) //1-4-7 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[0, j] == 0)
{
a[0, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[1, 0] + a[1, 1] + a[1, 2]) == 4 && a[1, 0] != 1 && a[1, 1] != 1 && a[1, 2] != 1) //2-5-8 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[1, j] == 0)
{
a[1, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[2, 0] + a[2, 1] + a[2, 2]) == 4 && a[2, 0] != 1 && a[2, 1] != 1 && a[2, 2] != 1) //3-6-9 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[2, j] == 0)
{
a[2, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 0] + a[1, 0] + a[2, 0]) == 4 && a[0, 0] != 1 && a[1, 0] != 1 && a[2, 0] != 1) //1-2-3 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 0] == 0)
{
a[i, 0] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 1] + a[1, 1] + a[2, 1]) == 4 && a[0, 1] != 1 && a[1, 1] != 1 && a[2, 1] != 1) //4-5-6 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 1] == 0)
{
a[i, 1] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 2] + a[1, 2] + a[2, 2]) == 4 && a[0, 2] != 1 && a[1, 2] != 1 && a[2, 2] != 1) //7-8-9 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 2] == 0)
{
a[i, 2] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 0] + a[1, 1] + a[2, 2]) == 4 && a[0, 0] != 1 && a[1, 1] != 1 && a[2, 2] != 1) //1-5-9 — защита
{
if (a[0, 0] == 0)
a[0, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[2, 2] == 0)
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
if ((a[2, 0] + a[1, 1] + a[0, 2]) == 4 && a[2, 0] != 1 && a[1, 1] != 1 && a[0, 2] != 1) //3-5-7 — защита
{
if (a[2, 0] == 0)
a[2, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[0, 2] == 0)
a[0, 2] = 1;
hdpc = false;
paint();
}
}
}
}
}
}
}
}[/code]

Как видите, смысл защиты от возможного поражения является почти что точной копией предыдущего метода. Тут стоит лишь напомнить и уточнить несколько вещей:

  • Ходы противника мы отмечаем значением 2. Для этого используется массив a.
  • Метод paint вызывает отрисовку на основе данных матрицы.
  • Переменная булевого типа hdpc(hod pc, ход компьютера) принимает значение false, если пришла очередь ходить пользователю.

Дирижер всех ходов компьютера

Я использовал несколько переменных, которые сохраняли некоторое значение, на основании которых можно было бы понять, чей сейчас ход и кто ходил первым. Очень важным моментом всей программы является необходимость передавать управление то пользователю, то компьютеру. Для этого я рекомендую простой способ: лично я использовал элемент panel, который выполняет роль игрового поле. С помощью булевой переменной hdpc, я либо разрешал принимать нажатие мышкой пользователя на panel, либо нет, что обеспечивало только своевременный ход пользователя. Так же сразу после хода пользователя я запускал на действие специальный метод, который я образно назвал дирижером всех ходом компьютера. В чем это определение очень даже подходит для него, ведь именно здесь будут приниматься решения, какой метод активировать, то есть именно тут можно увидеть всю стратегию данной игры:

[code language=»csharp»]nichia(); //проверяем, если хотя бы одно свободное место на поле. Если да, то ходим. Если нет, то вывод сообщения о ничье. Данный метод предлагаю написать вам самим, он довольно прост.
pobeda(); //это тот самый метод для проверки возможности выигрыша
if (win == 0) //как помните мы использовали переменную win, чтобы обозначить победу компьютера. Если компьютер еще не выиграл, то продолжаем
{
zachita(); //этот метод мы так же описали. Компьютер защищается от возможного проигрыша
if (hdpc == true) //до сих пор ход компьютера, а это значит компьютер пока не может выиграть и поражением никто не пугает, а значит можно ходить по стратегии
{
if (pc == 1) //данная переменная хранит информацию о том, кто первым ходил. Если первым ходил компьютер, то значение равно 1, если пользователь — то 2.
{
krestiki(); //так называется метод, который выполняет ход в самый отдаленный угол от предыдущего хода пользователя
}
else //компьютер ходит вторым
{
if (xfir == 1 && yfir == 1) //пользователь первым ходом сходил в центр
{
ugol(); //ищем любой свободный угол и ходим туда, если это невозможно, ходим в любую свободную клетку
}
else //пользователь первым ходом не сходил в центр
{
if (pervhod) //данная булевая переменная хранит true, если пользователь собирается сделать первый ход в этой партии
{
hod1(); //ходим в центр
pervhod = false;
}
else
{
ugol(); //ходим в свободный угол
}
}
}
} //конец противоположного хода
nichia();
}
else
{
winner();
}[/code]

В данной методе, который я заботливо уложил комментариями, и хранится вся стратегия игры в «крестики-нолики». Нам же остается дописать все необходимые методы.

Ход в противоположный от предыдущего хода угол

Данный метод, который в представленном коде был назван как krestiki, выполняет ход в противоположный от предыдущего шага пользователя угол. Чтобы выполнить данную операцию, мы должны знать, куда последним шагом сходил пользователь. Именно для этих целей я использую специальные переменные. Парочку из них(xfir и yfir — сократил от x first и y first) я уже использовал в предыдущем методе. Данные переменный запоминают только первый ход пользователя. Следующие переменные, которые вы встретите, будут хранить каждый последний шаг пользователя.

[code language=»csharp»]if (xlast == 0 && ylast == 0) //если 0,0
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 2 && ylast == 0) //2.0
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 0 && ylast == 2) //0.2
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 2 && ylast == 2) //2.2
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 0 && ylast == 1) //0.1
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 1 && ylast == 0) //1.0
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 2 && ylast == 1) //2.1
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 1 && ylast == 2) //1.2
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
}
}
}
}
}
}
}[/code]

В данном методе мы знакомимся с новым методом random, который отвечает, надеюсь вы догадались, за абсолютно рандомный(случайный шаг). Помните, в стратегии есть и такие ходы. Данный метод я так же оставлю для реализации Вам.

Ход в любой из углов

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

[code language=»csharp»]if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
}[/code]

Данный метод сделан на основе предыдущего метода, разница лишь в том, что в нем отсутствуют лишние проверки. Тут все просто: находим пустой угол — ходим, не находим — ходим рандомно.

Я понимаю, что приведенная часть это только половина всей программы. Но это основная половина всей программы. В ближайшее время я напишу еще одну статью, в которой опишу «рисовательную» часть этой программы, а так же укажу как отловить ход пользователя на panel`и. Напомню, что все это будет на языке C#. Так же напомню, что мы только что писали код программы, которая имитирует игру компьютера с пользователем в «Крестики-нолики»

Update: Боюсь у меня нет времени и еще больше нет желания заново разбираться в игре, которую я некогда написал и поэтому, скорее всего, обещанная рисовательная часть программы и прочие фишки никогда не увидят свет на страницах сайта About-windows.ru. В связи с этим просто предлагаю вам скачать исходник этой игры и дальше уже самим разобраться в ее коде. Прошу не судить строго по поводу «красоты» программирования.

Ссылка на скачивание игры Крестики и нолики.

P.S. Для тех кто заявляет, что программу можно выиграть, просьба предоставить доказательства(скриншот; порядок ходов с указанием того, кто начинает партию). При этом не нужно обременять себя рассказами про Ваши геройства, если вы играли нечестно(защита от дурака и хакера не продумана так же хорошо, так алгоритм программы).