Я розробив AI 2048 року, використовуючи оптимізацію очікувань , замість пошуку мінімакс, який використовується алгоритмом @ ovolve. AI просто виконує максимізацію за всіма можливими рухами з подальшим очікуванням на всі можливі нерестини плитки (зважені ймовірністю плиток, тобто 10% для 4 та 90% для 2). Наскільки мені відомо, не вдається обрізати очікування оптимізації оптимізації (за винятком видалення гілок, що малоймовірно), і тому використовуваний алгоритм - це ретельно оптимізований пошук грубої сили.
Продуктивність
AI в його конфігурації за замовчуванням (максимальна глибина пошуку 8) займає від 10 мс до 200 мс для виконання переміщення, залежно від складності положення дошки. Під час тестування AI досягає середньої швидкості руху 5-10 ходів за секунду протягом всієї гри. Якщо глибина пошуку обмежена 6 рухами, AI може легко виконувати 20+ рухів за секунду, що робить цікавим перегляд .
Щоб оцінити результативність AI, я запустив AI 100 разів (підключений до гри браузера за допомогою дистанційного керування). Для кожної плитки наведені пропорції ігор, в яких ця плитка була досягнута хоча б один раз:
2048: 100%
4096: 100%
8192: 100%
16384: 94%
32768: 36%
Мінімальний бал за всі прогони склав 124024; максимальний досягнутий бал становив 794076. Середній бал - 387222. AI ніколи не зміг отримати плитку 2048 (тому він ніколи не програвав гру навіть один раз у 100 іграх); насправді вона досягала плитки 8192 хоча б раз у раз!
Ось скріншот найкращого виконання:
Ця гра займала 27830 рухів за 96 хвилин, або в середньому 4,8 ходу в секунду.
Впровадження
Мій підхід кодує всю дошку (16 записів) як єдине 64-бітне ціле число (де плитки - це nybbles, тобто 4-бітні шматки). На 64-бітному апараті це дозволяє передавати всю плату в єдиний реєстр машин.
Операції зсуву бітів використовуються для вилучення окремих рядків і стовпців. Один рядок або стовпець - це 16-бітна кількість, тому таблиця розміром 65536 може кодувати перетворення, які діють на один рядок або стовпець. Наприклад, ходи реалізуються у вигляді 4-х пошукових запитів у попередньо обчислену таблицю ефектів переміщення, яка описує, як кожен хід впливає на один рядок або стовпець (наприклад, таблиця "рухатись правою" містить запис "1122 -> 0023", описуючи, як рядок [2,2,4,4] стає рядком [0,0,4,8] при переміщенні праворуч).
Оцінка балів також проводиться за допомогою пошуку таблиць. У таблицях містяться евристичні оцінки, обчислені у всіх можливих рядках / стовпцях, а отриманий бал для дошки - це просто сума значень таблиці для кожного рядка та стовпця.
Таке представлення на дошці, поряд із підходом до пошуку таблиці для руху та забивання, дозволяє AI за короткий проміжок часу здійснювати пошук величезної кількості ігрових станів (понад 10 000 000 станів гри в секунду на одному ядрі мого ноутбука в середині 2011 року).
Сам пошук очікувань кодується як рекурсивний пошук, який чергується між кроками "очікування" (тестування всіх можливих нерестових місць розташування та значень та зважування їх оптимізованих балів за ймовірністю кожної можливості) та кроками "максимізації" (тестування всіх можливих рухів і вибір тієї, з найкращою оцінкою). Пошук по дереву закінчується, коли він бачить попередньо бачену позицію (використовуючи таблицю транспозиції ), коли вона досягає заздалегідь заданої межі глибини або коли вона досягає стану дошки, що є дуже малоймовірним (наприклад, це було досягнуто шляхом отримання 6 "4" плиток в ряд із вихідного положення). Типова глибина пошуку - 4-8 ходів.
Евристика
Кілька евристик використовуються для спрямування алгоритму оптимізації до вигідних позицій. Точний вибір евристики має величезний вплив на продуктивність алгоритму. Різні евристики зважуються та об'єднуються в позиційну оцінку, яка визначає, наскільки "хорошим" є дане положення дошки. Тоді оптимізаційний пошук буде спрямований на максимальне досягнення середнього балу всіх можливих позицій на дошці. Фактична оцінка, як показано в грі, не використовується для обчислення результатів настільної дошки, оскільки вона занадто сильно зважена на користь злиття плиток (коли затримка злиття може принести велику користь).
Спочатку я використовував дві дуже прості евристики, надаючи "бонуси" за відкриті квадрати та за великі значення на межі. Ці евристики спрацювали досить добре, часто досягаючи 16384, але ніколи не досягаючи 32768.
Петро Моравек (@xificurk) взяв мій інтелект і додав дві нові евристики. Перша евристика була покаранням за немонотонні рядки та стовпці, які збільшувались у міру збільшення рангів, гарантуючи, що немонотонні ряди невеликих чисел не будуть сильно впливати на рахунок, але немонотонні ряди великих чисел завдають істотної шкоди оцінці. Друга евристика підраховувала кількість потенційних злиття (суміжних рівних значень) на додаток до відкритих просторів. Ці дві евристики слугували для підштовхування алгоритму до монотонних дощок (які легше об'єднати) та до позицій плати з великими злиттями (спонукаючи її вирівняти злиття, де це можливо, для отримання більшого ефекту).
Крім того, Петр також оптимізував евристичні ваги, використовуючи стратегію "метаоптимізації" (використовуючи алгоритм під назвою CMA-ES ), де самі ваги були скориговані для отримання максимально можливої середньої оцінки.
Ефект від цих змін надзвичайно значний. Алгоритм пішов від досягнення плитки 16384 приблизно 13% часу до досягнення її понад 90% часу, і алгоритм почав досягати 32768 за 1/3 часу (тоді як стара евристика ніколи не виробляла плитку 32768) .
Я вважаю, що в евристиці ще є можливість вдосконалитись. Цей алгоритм, безумовно, ще не є "оптимальним", але я відчуваю, що він дуже близький.
Те, що AI досягає 32768 плитки в більш ніж третині своїх ігор, - це величезна віха; Я буду здивований, почувши, чи хтось із гравців людини досягнув 32768 в офіційній грі (тобто без використання таких інструментів, як savestates або скасувати). Я думаю, що плитка 65536 знаходиться в межах досяжності!
Ви можете спробувати AI для себе. Код доступний на веб- сайті https://github.com/nneonneo/2048-ai .