Подход к такого рода проблемам заключается в изучении возможных вариантов будущего. Обычно (для шахматного или шашечного ИИ) вы бы рассматривали фьючерсы на определенное количество ходов вперед, но поскольку игры в крестики-нолики такие короткие, вы можете исследовать их до конца игры.
Концептуальная реализация
Таким образом, вы создаете разветвленную структуру:
- ИИ воображает, что делает все законные действия
- ИИ воображает, что пользователь делает каждый легальный ход, который он может сделать после каждого его легального хода.
- Затем ИИ представляет каждый из своих следующих легальных ходов.
- и т.п.
Затем, идя от самого разветвленного конца (самого дальнего вперед во времени), игрок, чей сейчас ход (ИИ или пользователь), выбирает, какое будущее для него лучше (победа, поражение или ничья) в каждой точке разветвления. Затем он передается игроку выше по дереву (ближе к настоящему); каждый раз выбирая наилучшее будущее для игрока, чей воображаемый ход сейчас, пока, наконец, вы не окажетесь в первой точке ветвления, где ИИ может видеть варианты будущего, в которых он проигрывает, рисует и выигрывает. Он выбирает будущее, в котором он выигрывает (или, если он недоступен, рисует).
Фактическая реализация
Обратите внимание, что концептуально это то, что происходит, но нет необходимости создавать все дерево, а затем судить об этом таким образом. Вы можете так же легко работать с деревом, добираясь до самых дальних точек во времени и выбирая их.
Здесь этот подход хорошо работает с рекурсивной функцией. Каждый уровень функции опрашивает все свои ветви; передавая им возможное будущее и возвращая -1,0,+1; выбор лучшего результата для текущего игрока в каждой точке. Верхний уровень выбирает ход, фактически не зная, как складывается каждое будущее, насколько хорошо они срабатывают.
Псевдокод
В этом псевдокоде я предполагаю, что +1 — выигрыш ИИ, 0 — рисование, -1 — проигрыш пользователя.
determineNextMove(currentStateOfBoard)
currentBestMove= null
currentBestScore= - veryLargeNumber
for each legalMove
score=getFutureScoreOfMove(stateOfBoardAfterLegalMove , AI’sMove)
if score>currentBestScore
currentBestMove=legalMove
currentBestScore=score
end
end
make currentBestMove
end
getFutureScoreOfMove(stateOfBoard, playersTurn)
if no LegalMoves
return 1 if AI wins, 0 if draw, -1 if user wins
end
if playersTurn=AI’sTurn
currentBestScore= - veryLargeNumber //this is the worst case for AI
else
currentBestScore= + veryLargeNumber //this is the worst case for Player
end
for each legalMove
score=getFutureScoreOfMove(stateOfBoardAfterLegalMove , INVERT playersTurn)
if playersTurn ==AI’sTurn AND score>currentBestScore //AI wants positive score
currentBestScore=score
end
if playersTurn ==Users’sTurn AND score<currentBestScore //user wants negative score
currentBestScore=score
end
end
return currentBestScore
end
Этот псевдокод не заботится о начальной доске (вы вызываете эту функцию при каждом движении ИИ на текущей доске) и не возвращает путь, по которому пойдет будущее (мы не можем знать, будет ли пользователь играть оптимально, поэтому это информация бесполезна), но он всегда будет выбирать путь, ведущий к оптимальному будущему для ИИ.
Рекомендации по более крупным проблемам
В этом случае, когда вы исследуете игру до конца, очевидно, какое будущее будет наилучшим (победа, поражение или ничья), но если вы собираетесь (например) только на пять ходов в будущее, вы бы нужно найти какой-то способ определить это; в шахматах или шашках оценка фигуры - самый простой способ сделать это, а положение фигуры является полезным улучшением.
person
Richard Tingle
schedule
28.07.2013