Стань Чемпіоном


11

Тик-так-латинь!

Це справжня історія, тому імена були змінені.

Мій вчитель латині, містер Латинь, створив свій власний варіант (без жарту) типу "tic tac toe". Назвемо це тик-так-латином. Гра проста, це по суті тик-так-носок, який грається на сітці чотири на чотири.

Офіційна декларація правил

Рядок - це або рядок, стовпець або діагональ. Є два символи, "X" і "O", але один або обидва можуть бути замінені на інший символ.
Ви набираєте один бал, коли у вас є три вашого символу та один із інших символів.

Ці бали домовляються:

--- О
-О--
XXXO
XOOX

O -XX
- О -
- X -
--- О

Вони не набирають балів:

----
XXXX
----
ТОВО

----
XXX-
----
ТОВ-

Гра виграється, коли у одного гравця більше очок, ніж у іншого. Гра - нічия лише в тому випадку, якщо дошка заповнюється.

Виклик

Вирішіть цю гру. Ваша робота полягає в тому, щоб забезпечити спосіб гарантувати виграш або нічию, залежно від того, що є оптимальним результатом.

Ваше рішення може вибрати або почати перше, або друге (і тому може обрати його символ). Не обов’язково реалізовувати інтерактивну гру, коли користувач вводить дані та змінюється відповідне відображення. Це також може бути функція або програма, яка сприймає вклад як стан гри, і виводить нову дошку або опис їх переміщення . Будь-яка опція повинна працювати протягом приблизно десяти секунд за зроблений хід.


Гра гравця проти будь-якої послідовності рухів повинна дати оптимальний результат. Це означає, що ви можете припустити, що вхідне положення є таким, до якого вона може бути доступною для гри з вашим плеєром. Подання повинні бути детермінованими і не обов’язково надавати докази оптимальності, але якщо вони будуть зламані (побившись), ваші подання будуть визнані недійсними (ви можете залишити їх, але додати (тріщини) у заголовку).
Це нетривіальне завдання, тому будь-яке дійсне подання вражає і заслуговує на прийняту галочку, але я зроблю гольф коду основним критерієм виграшу.

Переможець вибирається вниз за цим списком, поки не буде обраний один переможець.

  • Найкоротше вирішене виконання, яке завжди виграє
  • Найкоротша реалізація

1
"По-перше, якість гри дивиться на" Ви не думаєте, що це суб'єктивне?
user48538

Завдання надання інтерфейсу для відтворення здається периферійним записом ідеального гравця. Я б запропонував просто передати поточний стан гри як вхідний і вимагати від коду для отримання виграшного ходу або навіть просто оцінки за ідеальної гри (виграти, нічия, програти).
xnor

1
Розв’язання може бути побитим, зробивши неефективний пошук грубої сили. Ви все в порядку, якщо код працює дуже повільно?
xnor

1
" Ви виграєте гру, якщо забивали і в процесі не забивали свого опонента ". Чи означає це, що я можу виграти лише тоді, коли розміщую фігуру, а не тоді, коли робить суперник? Що станеться, якщо хода створює виграшні лінії для обох гравців: гра, намальована чи гра?
Пітер Тейлор

1
@RohanJhunjhunwala Ви повинні уточнити дозволений вхід ігрового стану, інакше можливо, що люди можуть скористатись не визначеним на даний момент форматом введення та обрати формат, який дуже допомагає їх вирішенню.
Лише ASCII

Відповіді:


6

Perl, 147 байт (не змагається, займає більше 10 секунд за ходу)

Включає +4 для -0p

Програма грає X. Він буде грати досконалу гру.

Введіть дошку на STDIN, наприклад:

tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D

Вихід буде однаковою дошкою з усіма Xзаміненими Oі навпаки. Порожні плями будуть заповнені цифрою, яка вказує на результат, якщо X гратиме там, 1значить, результат буде виграш, 2нічия та 3програш. Готова гра просто повертає ту саму позицію з перевернутими кольорами.

У цьому прикладі вихід буде:

1O1X
1O33
O3O3
X33X

Тож позиція є виграшною, Xякщо він грає в 3 місцях вгорі та зліва. Усі інші ходи програють.

Цей заплутаний вихід насправді зручний, якщо ви хочете знати, як триває гра після переїзду. Оскільки програма завжди грає, Xви повинні поміняти місцями Xі Oпобачити ходи O. Наприклад, досить зрозуміло, що Xвиграє, граючи у верхньому лівому куті, а як бути, якщо він Xграє на третій позиції вздовж верху? Просто скопіюйте вихід, поставте Oзамість вибраного вами переміщення та замініть всі інші числа ще -раз, тож ось:

-OOX
-O--
O-O-
X--X

Результат:

3XXO
3X33
X3X3
O33O

Очевидно, кожен крок Oповинен програвати, то як він програє, якщо грає у верхньому лівому куті? Знову зробіть це, поставивши Oвгорі ліворуч і замінивши цифри на -:

OXXO
-X--
X-X-
O--O

Давання:

XOOX
1O33
O3O3
X33X

Тож у Х є лише один шлях до його виграшу:

XOOX
OO--
O-O-
X--X

Даючи

OXXO
XX33
X3X3
O33O

Ситуація для Росії Oзалишається безперспективною. Зараз легко зрозуміти, що кожен крок дозволяє Xнегайно перемогти. Давайте хоча б спробуємо пройти 3 O підряд:

OXXO
XX--
X-X-
O-OO

Давання:

XOOX
OO13
O3O3
X3XX

Xграє єдиний виграшний хід (зауважте, що це робиться XXXOвздовж третього стовпця:

XOOX
OOO-
O-O-
X-XX

Тут результат:

OXXO
XXX-
X-X-
O-OO

бо гра вже була закінчена. Ви можете побачити виграш у третій колонці.

Фактична програма tictaclatin.pl:

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

Застосовано на порожній дошці, вона оцінює 9506699 позицій, що займає 30 Гб і 41 хвилини на моєму комп’ютері. Результат:

2222
2222
2222
2222

Отже, кожен стартовий хід притягується. Отже гра - нічия.

Екстремальне використання пам'яті здебільшого спричинене використанням рекурсії do$0. Для використання цієї 154-байтної версії за допомогою простої функції потрібно 3 Гбіт і 11 хвилин:

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+&f%eeg&&(/1/||/2/-1)}f

що більш терпимо (але все-таки занадто багато, щось все одно повинно просочуватися пам'яттю).

Об'єднання декількох прискорень призводить до цієї 160-байтної версії (5028168 позицій, 4 хвилини та 800 М для порожньої дошки):

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}f

Останній використовує 0для виграшу (не плутайте O), 1для нічиї та 2програші. Вихід цього також більш заплутаний. Він заповнює виграшну ходу для X у випадку виграшу без кольорового підміни, але якщо гра введення вже виграна, все-таки робиться кольорова заміна і не заповнює жодного ходу.

Звичайно, всі версії стають швидшими та використовують менше пам'яті, оскільки плата заповнюється. Більш швидкі версії повинні генерувати переміщення за 10 секунд, як тільки будуть зроблені 2 або 3 ходи.

В принципі, ця 146-байтна версія також повинна працювати:

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^/sx,--$|;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

але на моїй машині він запускає помилку Perl і скидає ядро.

Усі версії в принципі все ще працюватимуть, якщо кеш-позиція 6 байтів, яку виконується, $$_||=буде видалено, але для цього використовується стільки часу та пам'яті, що вона працює лише для майже заповнених дощок. Але теоретично у мене є рішення, що має 140 байт.

Якщо ви покладете $\=(вартість: 3 байти) безпосередньо перед тим, $@<=>0то на кожній платі виведення буде додаватися стан всієї дошки: 1для Xвиграшів, 0за нічию та -1програш.

Ось інтерактивний драйвер на основі найшвидшої версії, згаданої вище. У драйвера немає логіки, коли гра закінчена, тож вам доведеться зупинитись. Код про гольф знає, хоча. Якщо запропонований хід повернеться без -заміни ні на що, гра закінчена.

#!/usr/bin/perl
sub f{
    if ($p++ % 100000 == 0) {
        local $| = 1;
        print ".";
    }
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}

# Driver
my $tomove = "X";
my $move = 0;
@board = ("----\n") x 4;
while (1) {
    print "Current board after move $move ($tomove to move):\n  ABCD\n";
    for my $i (1..4) {
        print "$i $board[$i-1]";
    }
    print "Enter a move like B4, PASS (not a valid move, just for setup) or just press enter to let the program make suggestions\n";
    my $input = <> // exit;
    if ($input eq "\n") {
        $_ = join "", @board;
        tr/OX/XO/ if $tomove eq "O";
        $p = 0;
        $@="";
        %a = ();
        my $start = time();
        my $result = f;
        if ($result == 1) {
            tr/OX/XO/ if $tomove eq "O";
            tr/012/-/;
        } else {
            tr/OX/XO/ if $tomove eq "X";
            tr/012/123/;
        }
        $result = -$result if $tomove eq "O";
        my $period = time() - $start;
        print "\nSuggested moves (evaluated $p positions in $period seconds, predicted result for X: $result):\n$_";
        redo;
    } elsif ($input =~ /^pass$/i) {
        # Do nothing
    } elsif (my ($x, $y) = $input =~ /^([A-D])([1-4])$/) {
        $x = ord($x) - ord("A");
        --$y;
        my $ch = substr($board[$y],$x, 1);
        if ($ch ne "-") {
            print "Position already has $ch. Try again\n";
            redo;
        }
        substr($board[$y],$x, 1) = $tomove;
    } else {
        print "Cannot parse move. Try again\n";
        redo;
    }
    $tomove =~ tr/OX/XO/;
    ++$move;
}

NIce відповідь. Не могли б ви надати мені простий спосіб перевірити це? В ідеалі хотілося б побачити інтерактивну версію ... (це для моєї власної цікавості).
Rohan Jhunjhunwala

@RohanJhunjhunwala Добре, додав простий інтерактивний драйвер
Ton Hospel

Змінна "$ move" не оголошена в prog.pl:2
Rohan Jhunjhunwala

Чи є евристичне рішення, яке може реалізувати людина?
Rohan Jhunjhunwala

@RohanJhunjhunwala Просто перевірив програму драйверів. Працює нормально, $moveоголошено на лінії 11. Я не маю поняття, чи є людська евристика. Ця програма просто робить мінімакс на ігровому дереві, вона не має жодних стратегічних знань.
Євангеліє Тон

2

JavaScript (ES6) 392 байти

a=>b=>(c="0ed3b56879a4c21f",r=[],k=f=>r.push([a.filter(f),b.filter(f)]),[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)),k(n=>n%5==0),k(n=>n&&n-15&&!(n%3)),g=r.find(o=>(o[0].length==1&&o[1].length==2)||(o[0].length==2&&o[1].length==1)),g?parseInt(c[30-[...g[0],...g[1]].map(i=>parseInt(c[i],16)).reduce((p,c)=>p+c)],16):[...a,...b].indexOf(15-a[0])+1?15-a.find(i=>b.indexOf(15-i)==-1):15-a[0])

Використання

"Бот" зіграє другий.

Намалюйте сітку 4x4, які пронумеровані так:

+----+----+----+----+
|  0 |  1 |  2 |  3 |
+----+----+----+----+
|  4 |  5 |  6 |  7 |
+----+----+----+----+
|  8 |  9 | 10 | 11 |
+----+----+----+----+
| 12 | 13 | 14 | 15 |
+----+----+----+----+

Запустимо це в консолі браузера: Просто поставте f=перед кодом

Отже, якщо я хочу почати 1, я бігав, f([1])([])і це дасть мені 14.

Гарний хід ... Що робити, якщо 2після цього я зіграю? f([2,1])([14]). Це повернеться 13.

Спробуй здатися. Грати 3. f([3,2,1])([14,13]). Ой 0! Ти мене зрозумів!

Гра 0? f([0,2,1])([14,13]). 15Гаразд, продовжимо грати ...

Примітка

  1. Грайте інтерактивно. Почніть з f([your-step])([]).

  2. Попередьте свій наступний крок. (Дивіться демонстрацію вище)

  3. Допоможіть "боту" ввести його кроки. Це не дасть хороших результатів, якщо ви дасте йому випадкові налаштування. (Ніби f([1,2,4])([14,12])дасть 14- Ей, бот хотів грати на 13другому ходу!

Стислий підсумок

Поки ви не здастеся, бот буде грати дзеркальним рухом.

Дякую @EHTproductions за те, що ти сказав мені, що я неправильно прочитав правила гри та поради щодо гольфу: P

Тепер він також виявить, чи отримав він мат. Якщо так, заблокуйте його!

Його пріоритети: Блокування> Дзеркало> (резервне) шукати способи відтворення дзеркала


Мені дуже подобається тактика "дзеркального руху" :) Можливо, я нерозумію, але хіба 3,2,1для вас і 0бота це не виграш?
ETHproductions

На жаль, я неправильно зрозумів як "хто фіксує зразок 3 з роду і 1 іншого". Лемма трохи налаштувати рішення .. Дякуємо @ETHproductions.
Сонячний Пун

Кілька порад щодо гольфу: [0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})можна в гольф [0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)).
ETHproductions

Я не думаю, що це виграшно перемогло
Rohan Jhunjhunwala

0 - 14 - 12 -13 тріщин
Рохан Джунджунвала
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.