С самых первых дней зарождения игровой индустрии, начиная с таких игр, как Pong, средства искусственного интеллекта стали неотъемлемой частью практически любой компьютерной игры. Одной из фундаментальных проблем, занимающих разработчиков искусственного интеллекта, является поиск.
Поиск – это метод решения проблемы, в котором систематически просматривается пространство состояний задачи. Примеры состояний задачи: различные размещения фигур на доске в шахматах или же промежуточные шаги логического обоснования. Затем в этом пространстве альтернативных решений производится перебор в поисках окончательного ответа.
В «Крестиках-ноликах» ИИ должен уметь:
1) Убить
2) Защититься
3) Развить атаку
И почему в фильмах искусственный интеллект представляет опасность для человечества… :-)
Вся логика делится на несколько вариантов хода, и компьютер выбирает наилучший из них:
А) найти выигрышные позиции, то есть линии, где уже стоят 2 его, в нашем случае, крестика. То есть в первую очередь проверяется, сможет ли компьютер "убить" игрока сразу;
-
void AI()
-
{
-
if((kill==0)&&(a[0][0]=='x')&&(a[1][0]=='x')&&(a[2][0]=='-')){
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
stroke(169,149);a[2][0]='x';kill=1;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
}
В коде приведен лишь 1 из возможных случаев, когда компьютер может сразу выиграть (таких случаев я насчитал 23).
Б) Продумать защиту, т.е. не дать «убить» себя следующим ходом игрока: проверить, на какой линии есть 2 нолика игрока и там поставить там свой крестик;
-
if ((kill==0)&&(protection!=1) && (a[0][0]=='o') && (a[0][1]=='o')&&(a[0][2]=='-')) {
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
stroke(469,49);a[0][2]='x';protection=1;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
}
В) Атаковать, то есть развивать свою позицию. Проверить, где есть линия, на которой кроме крестика компьютера ничего не стоит. Если такая линия имеется, то именно туда поставить крестик;
-
for(i=0;i<=2;i++)
-
for(j=0;j<=2;j++){
-
if(attack!=1)
-
if(a[i][j]=='x'){
-
if(j==0){
-
if((a[i][j+1]=='-')&&(a[i][j+2]=='-'))
-
{while((k!=1)&&(k!=2))
-
k=random(3);
-
if(k==1) Y=319;
-
if(k==2) Y=469;
-
if(i==0) X=49;
-
if(i==1) X=99;
-
if(i==2) X=149;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
stroke(Y,X);a[i][k]='x'; attack=1;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
}}}
Сначала мы проверили, есть ли позиции, позволяющие развить атаку (здесь приведена лишь проверка по первому столбцу). Если таких позиций несколько, ты выбираем одну из них случайным образом. Затем проверим позиции для атак по строкам:
-
for (i=0;i<=2;i++)
-
for (j=0;j<=2;j++){
-
if (attack!=1)
-
if (a[i][j]=='x') {
-
if (i==0) {
-
if ((a[i+1][j]=='-')&&(a[i+2][j]=='-'))
-
{while ((k!=1)&&(k!=2))
-
k=random(3);
-
if (k==1) Y=99;
-
if (k==2) Y=149;
-
if (j==0) X=169;
-
if (j==1) X=319;
-
if (j==2) X=469;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
stroke(X,Y);a[k][j]='x'; attack=1;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
}}
Г) Поставить крестик в первую попавшуюся клетку
-
if((kill==0)&&(protection!=1)&&(attack!=1)){
-
while(a[i][j]!='-'){
-
i=random(3);j=random(3);
-
}
-
if(i==0) Y=49;
-
if(i==1) Y=99;
-
if(i==2) Y=149;
-
if(j==0) X=169;
-
if(j==1) X=319;
-
if(j==2) X=469;
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
stroke(X,Y);a[i][j]='x';
-
putimage(x-30, y-20, ptr, XOR_PUT);
-
}
Такой вариант будет применим при первом ходе компьютера, и когда не будет вариантов для развития атаки, самозащиты и «победного хода».
Конечно, искусственный интеллект для поля 3х3 звучит как-то слишком сильно, т.к. используется лишь полный перебор. Для полей большего размера и шахматных движков при анализе используют частичный перебор и эвристику. Но об этом как-нибудь в другой раз.
Скачать исходный код и исполняемый файл игры можно здесь.