Можете ли вы выиграть еще двумя ходами в Three Men Morris?

16

щедроты

№ 1 ( награжден )

Я добавлю 50 повторений для первого правильного ответа

№ 2 ( награжден )

Я добавлю еще 100 повторений для кратчайшего правильного ответа.

№ 3 ( открыт для представления )

Я добавлю 200 повторений для первого с более коротким правильным ответом. Существенным является не более 45% самого короткого в настоящее время ответа ( 564 байта x 0,45 = максимум 254 байта ).


Игра

Вы помните классическую игру « Nine Men's Morris » или просто « Мельница »? Существует вариация под названием Three Men Morris, которая немного напоминает изменчивые крестики-нолики.

правила

Это пустая доска игры:

   a   b   c
1 [ ]–[ ]–[ ]
   | \ | / |
2 [ ]–[ ]–[ ]
   | / | \ |
3 [ ]–[ ]–[ ]

[ ]является полем и |–/\представляет маршруты между этими полями.

Игра для двух игроков 1и 2каждый из которых проходят 3 жетонов на борту. Это на самом деле уже произошло, и мы в игре. Игра выиграна, если один игрок может сформировать millвертикальный или горизонтальный ряд из трех жетонов игрока.

Жетоны можно перемещать на доске вдоль соединительных линий, согласно этому правилу:

В любую соседнюю пустую позицию (то есть от положения края к центру, или от центра к положению края, или от положения края к соседнему положению края

Игрок должен сделать ход, если только нет соседней пустой позиции, в этом случае ход пропускается.

Соревнование

Вы игрок, 1и ваш ход следующий. Напишите программу или функцию, которая определяет:

  • Вы можете заставить выиграть с 2 или менее ходов ( определенный выигрыш )
  • Вы можете выиграть с 2 или менее ходами, если ваш оппонент допустил ошибку ( возможная победа )
  • Вы не можете выиграть с 2 или менее ходами, потому что вам нужно больше ходов или потому что вынужденные ходы приводят к победе вашего противника ( невозможно победить )

Требования

  • Даже если вы определенно выиграете, когда утомите своего противника, ваша программа должна закончиться за конечное время.
  • Вы можете написать программу или функцию.

вход

Игроки представлены 1и 2. 0определяет свободное поле. Вы можете принять входные данные в виде матрицы или массива.

определенный

A         B         C         D
2 1 0  |  2 1 0  |  1 0 1  |  1 2 2
2 1 2  |  0 1 0  |  1 0 2  |  2 1 O
0 0 1  |  2 2 1  |  0 2 2  |  O O 1

A: [2,1,0,2,1,2,0,0,1]
B: [2,1,0,0,1,0,2,2,1]
C: [1,0,1,1,0,2,0,2,2]
D: [1,2,2,2,1,0,0,0,1]

Возможно

A         B         C
1 0 1  |  1 0 1  |  1 2 2
1 2 2  |  1 2 0  |  0 0 1
2 0 0  |  2 0 2  |  2 1 0

A: [1,0,1,1,2,2,2,0,0]
B: [1,0,1,1,2,0,2,0,2]
C: [1,2,2,0,0,1,2,1,0]

Невозможно

A         B    
1 0 0  |  1 2 0
1 2 2  |  2 1 0
2 0 1  |  1 2 0

A: [1,0,0,1,2,2,2,0,1]
B: [1,2,0,2,1,0,1,2,0]

Выход

Ваша программа должна вывести / вернуть смайлик:

  • Определенная победа: :)
  • Возможная победа: :|
  • Невозможно выиграть: :(

Примеры

Определенная победа в два хода:

[2][1][ ] 1. [2][1][ ]
[2][1][2] -> [2][1][2]
[ ][ ][1]    [ ][1][ ]

[2][1][ ] 1. [2][1][ ]    [ ][1][ ] 2. [ ][ ][1]
[ ][1][ ] -> [ ][ ][1] -> [2][ ][1] -> [2][ ][1]
[2][2][1]    [2][2][1]    [2][2][1]    [2][2][1]

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][ ][2] -> [1][ ][2] -> [1][ ][2] -> [ ][ ][2]
[ ][2][2]    [ ][2][2]    [2][ ][2]    [2][ ][2]

Возможен выигрыш в два хода:

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][2][ ] -> [1][2][ ] -> [1][2][2] -> [ ][2][2]
[2][ ][2]    [2][ ][2]    [2][ ][ ]    [2][ ][ ]

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][2][ ] -> [1][2][ ] -> [1][2][2] -> [ ][2][2]
[2][ ][2]    [2][ ][2]    [2][ ][ ]    [2][ ][ ]

[1][2][2] 1. [ ][2][2]    [2][ ][2] 2. [1][2][2]
[ ][ ][1] -> [1][ ][1] -> [1][ ][1] -> [1][1][1]
[2][1][ ]    [2][1][ ]    [2][1][ ]    [2][ ][ ]

Невозможно выиграть в два хода:

[1][ ][ ]
[1][2][2]
[2][ ][1]

бонус

Если возможен определенный выигрыш, и ваша программа выводит ходы в одну сторону к успеху, например a1:a2(1 ход) или a1:a2,a3:b2(2 хода), вы можете снять 30% от вашего количества байтов.


Это код гольф - поэтому выигрывает самый короткий ответ в байтах. Стандартные лазейки запрещены.


Спасибо Питеру Тейлору, который исправил некоторые недостатки и улучшил формулировку в Песочнице .

insertusernamehere
источник
Относящиеся .
insertusername здесь
1
Мне нравятся эти таблицы / графика ascii =)
flawr
1
Что происходит, если игрок не может двигаться? например [1,0,0,2,1,0,2,2,1], игрок 2 не может двигаться - это выигрыш для игрока 1?
VisualMelon
1
@LeifWillerts Я могу неправильно понять, что вы имеете в виду, но в этом случае ни один игрок не находится в выигрышном состоянии - он выигрывает только с горизонтальной или вертикальной линией (не диагональю).
VisualMelon
3
Ну, есть только 1680 правильных позиций на плате, поэтому жесткое кодирование может дать чуть более 210 байтов. (меньше, если учитывать симметрию)
lirtosiast

Ответы:

1

Haskell, 580 564 441 байт

Это то, как далеко я могу играть в гольф на данный момент. Не уверен, что другие языки могут победить.

Вызовите mсписок списков, таких как [[2,1,0],[2,1,2],[0,0,1]](Определенный A).

import Data.Array
r=[0..2]
p?f=[(x,y)|x<-r,y<-r,f!y!x==p]
p%f=all(==x)xs||all(==y)ys where(x:xs,y:ys)=unzip$p?f
s p x y f=f//[(y,f!y//[(x,p)])]
p#f=[s 0 x y$s p u v f|a@(x,y)<-p?f,b@(u,v)<-0?f,((x-u)*(y-v)==0&&abs(x+y-u-v)==1)||elem(1,1)[a,b]]
p&f|p#f>[]=p#f|0<1=[f]
e=any
i a p f=e(a$e(p%))(map(map(p&))(map((3-p)&)$p&f))||e(p%)(p&f)
l=listArray(0,2)
f(True,_)=":)"
f(False,True)=":|"
f _=":("
m=putStrLn.f.(\f->(i all 1 f,i e 1 f)).l.map l

Тестовый код:

da = [[2,1,0],[2,1,2],[0,0,1]]
db = [[2,1,0],[0,1,0],[2,2,1]]
dc = [[1,0,1],[1,0,2],[0,2,2]]
dd = [[1,2,2],[2,1,0],[0,0,1]]
pa = [[1,0,1],[1,2,2],[2,0,0]]
pb = [[1,0,1],[1,2,0],[2,0,2]]
pc = [[1,2,2],[0,0,1],[2,1,0]]
ia = [[1,0,0],[1,2,2],[2,0,1]]
ib = [[1,2,0],[2,1,0],[1,2,0]]
al = [da,db,dc,dd,pa,pb,pc,ia,ib]

mapM_ m al возвращает:

:)
:)
:)
:)
:|
:|
:|
:(
:(
Лейф Виллертс
источник
1
Исправлено, я думаю. Будет ли дважды проверять и правильно ли играть в гольф вечером (что здесь до конца льготного периода)
Лейф Виллертс
5

C # - 739 663 байта

Завершить программу, читает входные данные из argv и, кажется, работает. Запустите это как

ThreeMill 1 2 1 1 2 0 0 0 2

Если этот метод ввода неприемлем, я буду рад изменить его (никогда не нравится использование argv).

using System;using System.Linq;class P{static void Main(string[]A){var I=new[]{0,3,6,1,4,7,2,5,8};Func<string[],string>J=S=>S[0]+S[1]+S[2]+" "+S[3]+S[4]+S[5]+" "+S[6]+S[7]+S[8]+" ";Func<string[],string,int>W=(B,p)=>(J(B)+J(I.Select(i=>B[i]).ToArray())).Contains(p+p+p)?1:0;Func<string[],string,string[][]>V=(B,p)=>I.SelectMany(a=>I.Where(b=>a!=b&B[a]==p&B[b]=="0"&(a==4|b==4|a-b==3|b-a==3|((a-b==1|b-a==1)&a/3==b/3))).Select(b=>{var N=(string[])B.Clone();N[b]=p;N[a]="0";return N;})).DefaultIfEmpty(B).ToArray();int h,G;Console.WriteLine(":"+"(|))"[V(A,"1").Max(z=>((h=0)<(G=V(z,"2").Sum(j=>V(j,"1").Max(q=>W(q,"1")-W(q,"2"))+h++*0))?1:0)+(h>G?W(z,"1")*2:2))]);}}

Я не был готов опубликовать это вчера, потому что у меня не было возможности сыграть в него много (у меня не было столько времени, и я мог бы быть вне практики), но так как ответа еще не было, я ' Я опубликую это в любом случае, я, конечно, не ожидаю награду, я бы предпочел, чтобы она досталась кому-то, кто приложил немного больше усилий, прежде чем отправлять!

Редактировать: заменил все bool-ы на целые, что означало, что я мог лучше использовать Linq, и мне удалось свернуть оба цикла foreach, что дало большую экономию. Я немного удивлен, что hсчетчик работает ... ++ такая тонкая утилита.

Программа очень проста, она просто исследует каждый возможный набор ходов (сохраняет состояние доски в строке []). Он перебирает все наши возможные ходы (доски, на которых это происходит) и подсчитывает количество ответов нашего оппонента, которые мы можем успешно победить ( G) (то есть те, которые мы выиграли, а он нет). Также подсчитывается количество возможных ответов (h). Если мы можем выиграть любой, то это возможно, и мы добавляем 1 к сумме, если мы можем выиграть их все, это определенно, и мы добавляем 2 к сумме. Таким образом, максимальный некоторый результат является нашим наилучшим возможным результатом, и мы индексируем строку «(|))», чтобы вернуть соответствующее лицо. Обратите внимание, что нам нужно дополнительное ")", потому что сумма может быть 2 или 3, если она определенная (возможно, мы не сможем побить ни одного ответа, уже выигравшего с первого хода, поэтому возможная проверка немного вводит в заблуждение).

Программа проверяет победу, создавая строку с доски, то есть разделенные пробелами строки и столбцы, и просто ищет строку из 3 символов игрока в этой строке (например, «201 201 021 220 002 111» является победить для нас)

using System;
using System.Linq; // all important

class P
{
    static void Main(string[]A) // transform to int?
    {
        var I=new[]{0,3,6,1,4,7,2,5,8}; // vertical indexes
        Func<string[],string>J=S=>S[0]+S[1]+S[2]+" "+S[3]+S[4]+S[5]+" "+S[6]+S[7]+S[8]+" "; // joins the strings up, so that there is a space separating each group of three (including at end)
        Func<string[],string,int>W=(B,p)=>(J(B)+J(I.Select(i=>B[i]).ToArray())).Contains(p+p+p)?1:0; // checks if a particular player wins
        Func<string[],string,string[][]>V=(B,p)=>I.SelectMany(a=>I // for each imagineable move
            .Where(b=>a!=b&B[a]==p&B[b]=="0"&(a==4|b==4|a-b==3|b-a==3|((a-b==1|b-a==1)&a/3==b/3))) // where it's legal
            .Select(b=>{var N=(string[])B.Clone();N[b]=p;N[a]="0";return N;}) // select the resulting board
        ).DefaultIfEmpty(B) // allow not-moving
        .ToArray();

        int h, // h stores the number of responses the opponent has to each move
        G; // G stores the number of responses by the opponent we can beat

        Console.WriteLine(":"+"(|))"[ // we index into this to decide which smiley
            V(A,"1").Max(z=>
                    ((h=0)<(G=V(z,"2").Sum(j=>V(j,"1").Max(q=>W(q,"1")-W(q,"2"))+h++*0))?1:0) // if there is atleast 1 reponse by the opponent we can beat, we can possibly win
                    +(h>G?W(z,"1")*2:2) // if there are moves which we can't win, then if we have already won (one-move), else, we can definitely win
                   ) // sum is therefore 0 if impossible, 1 if possible, >2 (no more than 3) if definite 
            ]);

    }
}

Вот мой тестовый скрипт:

ThreeMill 2 1 0 2 1 2 0 0 1
ThreeMill 2 1 0 0 1 0 2 2 1
ThreeMill 1 0 1 1 0 2 0 2 2
ThreeMill 1 2 2 2 1 0 0 0 1

ThreeMill 1 0 1 1 2 2 2 0 0
ThreeMill 1 0 1 1 2 0 2 0 2
ThreeMill 1 2 2 0 0 1 2 1 0

ThreeMill 1 0 0 1 2 2 2 0 1
ThreeMill 1 2 1 1 2 0 0 0 2
ThreeMill 1 0 1 2 0 2 1 0 2

Какие выводы

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
VisualMelon
источник
Ницца. Спасибо за то, что ты первый. :) Если все в порядке, я назначу награду после выходных, чтобы оставить ее еще на нескольких днях во вкладке.
имя пользователя здесь
@insertusernamehere Это нормально для меня, если я не смогу заняться какой-либо реальной работой, я мог бы сделать еще кое-что над этим завтра.
VisualMelon
1
Это напоминает мне об этом комментарии: « Я не смог ответить на вопрос в течение сорока минут. Это очень важно! Просто отправьте информацию о базе данных, и я вставлю свои ответы в SQL. У меня так много работы, которой нужно избегать, и нет причин чтобы избежать этого! " Почему не работает переполнение стека? , :)
insertusername здесь
1

PowerShell 576 550 байт

Меня не так легко удержать - если я не смогу получить C # ниже 631 байта, мне придется вместо этого использовать другой язык! Я надеюсь, что Лейф Виллертс выбьет из своего ответа 5 байтов, потому что я решил, что я не слишком люблю PowerShell, возможно, мне просто нужно взглянуть на него объективно с точки зрения количества байтов ...

Это сценарий, вы запускаете его . .\mill.ps1 "201102021". Это довольно хорошая копия моего ответа на C #, только на языке, с которым у меня мало опыта. Я не приложил слишком много усилий, чтобы играть в гольф, потому что это заняло так много времени, чтобы начать работать в первую очередь, и уже достаточно компактно.

Изменить: не мог просто оставить эти [Math]::Floorзвонки там

param($U);$I=0,3,6,1,4,7,2,5,8;function J($S){($S[0..2]+" "+$S[3..5]+" "+$S[6..8]-join"").Contains($p*3)}function W($D,$p){(J $D)-or(J $D[$I])}function V($Q,$C){$I|%{$a=$_;$I|?{$a-ne$_-and$Q[$a]-eq$c-and$Q[$_]-eq"0"-and($a-eq4-or$_-eq4-or$a-$_-eq3-or$_-$a-eq3-or(($a-$_-eq1-or$_-$a-eq1)-and$a/3-$a%3/3-eq$_/3-$_%3/3))}|%{$b=$Q[0..8];$b[$_]=$c;$b[$a]=0;$b-join''}}|%{$n=1}{$n=0;$_}{if($n){$Q}}}$e=$f=0;V $U "1"|%{$h=0;$x=$_;V $x "2"|%{$k=0;(V $_ "1"|%{if((W $_ "1")-and!(W $_ "2")){$k=$e=1}});$h+=1-$k};if($h-eq0-or(W $x "1")){$f=2}};":"+"(|))"[$e+$f]

Если вы описываете, как это работает ... ответ на C # для вас, но, надеюсь, комментарии прояснят это. Точки с запятой могут не совсем совпадать с однострочной командой, я еще не уверен, где они нужны и не нужны, и не скопировал их обратно, когда я поставил все это в одну строку.

param($U); # take input as argument

$I=0,3,6,1,4,7,2,5,8; # cols

function J($S){ # checks if this is a winning string
($S[0..2]+" "+$S[3..5]+" "+$S[6..8]-join"").Contains($p*3)}

function W($D,$p){ # checks if this is a winning board
(J $D)-or(J $D[$I])} # $D[$I] reorganises into columns

function V($Q,$C){ # yields all valid moves from position $Q for player $C
$I|%{$a=$_;$I| # for each possible move
?{$a-ne$_-and$Q[$a]-eq$c-and$Q[$_]-eq"0"-and($a-eq4-or$_-eq4-or$a-$_-eq3-or$_-$a-eq3-or(($a-$_-eq1-or$_-$a-eq1)-and$a/3-$a%3/3-eq$_/3-$_%3/3))}| # where legal
%{$b=$Q[0..8];$b[$_]=$c;$b[$a]=0;$b-join''}}| # make the move (copy $Q to an array, modify, join into a string)
%{$n=1}{$n=0;$_}{if($n){$Q}}} # if empty, return $Q - I am confident this can be achieved with commas, and [0], and maybe a +, but I don't want to think about it

$e=$f=0; # possible, definite

V $U "1"|%{ # for all our possible moves
$h=0;$x=$_; # $k is whether we win all of these
  V $x "2"| # for all opponent's responses
  %{$k=0;(V $_ "1"| # for all our responses
  %{if((W $_ "1")-and!(W $_ "2")){$k=$e=1}});$h+=1-$k}; # if we can win and he can't, then things are looking good, set $e to 1 (possible win)

  if($h-eq0-or(W $x "1")){$f=2} # if we win every move, or we have already won, it's a definite
};

":"+"(|))"[$e+$f] # smile, it's all over

Тестовый скрипт (PowerShell):

. .\mill.ps1 "210212001"
. .\mill.ps1 "210010221"
. .\mill.ps1 "101102022"
. .\mill.ps1 "122210001"

. .\mill.ps1 "101122200"
. .\mill.ps1 "101120202"
. .\mill.ps1 "122001210"

. .\mill.ps1 "100122201"
. .\mill.ps1 "121120002"
. .\mill.ps1 "101202102"

. .\mill.ps1 "100122201"
. .\mill.ps1 "120210120"

Вывод их:

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
:(
:(
оборота VisualMelon
источник
1

Python 3, 566 557 байт

Мне нужно будет посмотреть, смогу ли я сыграть в гольф дальше или получу бонус в 30%, но после долгих проволочек вот мой ответ.

def t(g,x=1,r=0,z=0):
 m=[[1,3,4],[0,2,4],[2,4,5],[0,4,6],[0,1,2,3,5,6,7,8],[2,4,8],[3,4,7],[4,6,8],[4,5,7]];a=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];z=z or[[],[],[],[]];s=0
 if r>3:return z
 for i in a:
  if g[i[0]]==g[i[1]]==g[i[2]]>0:s=g[i[0]];break
 z[r]+=s,
 for q in range(9):
  i=g[q]
  if i==x:
   for p in m[q]:
    if g[p]<1:n=g[:];n[q],n[p]=n[p],n[q];z=t(n,3-x,r+1,z)
 if r:return z
 else:
  w=l=0
  for j in range(4):w=w or 1in z[j];l=l or 2in z[j]
  if l<1and w:return":)"
  elif w<1and l:return":("
  else:return":|"

Ungolfed:

def three_mens_morris(grid, player=1, rec=0, w_l=0, p=0):
    moves = [[1,3,4],[0,2,4],[2,4,5],[0,4,6],[0,1,2,3,5,6,7,8],[2,4,8],[3,4,7],[4,6,8],[4,5,7]]
    w_l = w_l or [[],[],[],[]]
    if rec == 4: return w_l
    result = check_grid(grid)
    w_l[rec].append(result)
    for sq_1 in range(len(grid)):
        piece = grid[sq_1]
        if piece == player:
            for sq_2 in moves[sq_1]:
                if grid[sq_2] == 0:
                    new_grid = grid.copy()
                    new_grid[sq_1],new_grid[sq_2]=new_grid[sq_2],new_grid[sq_1]
                    w_l = three_mens_morris(new_grid,3-player,rec+1,w_l)
    if p: print(w_l)
    if rec:
        return w_l
    else:
        win = loss = 0
        for i in range(4):
            if 1 in w_l[i]:
                win = 1
            elif 2 in w_l[i]:
                loss = 1
        if p:print(win,loss)
        if loss==0 and win:
            return ":)"
        elif loss and win==0:
            return ":("
        else:
            return ":|"

def check_grid(grid):
    rows = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
    for i in rows:
        if grid[i[0]]==grid[i[1]]==grid[i[2]] and grid[i[0]]:
            return grid[i[0]]
    return 0
Sherlock9
источник