Você pode ganhar com mais dois lances no Morris Three Men's?

16

Recompensas

Nº 1 ( concedido )

Vou jogar 50 rep para a primeira resposta válida

No. 2 ( concedido )

Vou colocar mais 100 representantes para a resposta mais curta válida.

Nº 3 ( aberto para inscrições )

Vou colocar 200 repetições para o primeiro com uma resposta válida mais curta e significativa. Significativo sendo no máximo 45% da resposta mais curta atualmente ( 564 bytes x 0,45 = máximo 254 bytes ).


O jogo

Você se lembra do clássico jogo " Nine Men's Morris " ou simplesmente " Mill "? Há uma variação chamada Morris de Três Homens que é um pouco como um tic-tac-toe mutável.

Regras

Este é o quadro em branco do jogo:

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

[ ] é um campo e |–/\ representa rotas entre esses campos.

O jogo é jogado por dois jogadores 1e 2cada um coloca 3 fichas no tabuleiro. Isso já aconteceu e estamos no jogo. O jogo é ganho se um jogador puder formar ummill linha vertical ou horizontal dos três tokens do jogador.

Os tokens podem ser movidos no tabuleiro ao longo das linhas de conexão, de acordo com esta regra:

Para qualquer posição vazia adjacente (ou seja, de uma posição de borda para o centro, ou do centro para uma posição de borda, ou de uma posição de borda para uma posição de borda adjacente

Um jogador deve fazer uma jogada, a menos que não haja uma posição vazia adjacente; nesse caso, a jogada é pulada.

O desafio

Você é jogador 1e sua jogada é a próxima. Escreva um programa ou uma função que determine se:

  • você pode forçar uma vitória com 2 ou menos jogadas ( vitória definitiva )
  • você pode ganhar com 2 ou menos jogadas, se o seu oponente cometer um erro ( vitória possível )
  • você não pode vencer com 2 ou menos jogadas, porque precisará de mais jogadas ou porque jogadas forçadas levam seu oponente a vencer ( impossível vencer )

Exigências

  • Mesmo que você definitivamente ganhe quando matou seu oponente até a morte, seu programa deve terminar em um tempo finito.
  • Você pode escrever um programa ou uma função.

Entrada

Os jogadores são representados por 1e 2.0define um campo livre. Você pode receber entrada como uma matriz ou uma matriz.

Definido

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]

Possível

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]

Impossível

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]

Resultado

Seu programa deve gerar / retornar um smiley:

  • Vitória definitiva: :)
  • Possível vitória: :|
  • Impossível ganhar: :(

Exemplos

Vitória definitiva em dois movimentos:

[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]

Possível vitória em duas jogadas:

[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][ ][ ]

Impossível vencer em duas jogadas:

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

Bônus

Caso seja possível uma vitória definitiva e o seu programa produza os movimentos de uma maneira para o sucesso, como a1:a2(1 movimento) ou a1:a2,a3:b2(2 movimentos), você pode retirar 30% da sua contagem de bytes.


Este é o código de golfe - a resposta mais curta em bytes vence. As brechas padrão não são permitidas.


Agradecemos a Peter Taylor, que corrigiu algumas falhas e melhorou a redação na Sandbox .

insertusernamehere
fonte
Relacionado .
insertusernamehere
1
I como as tabelas ASCII / gráficos =)
flawr
1
O que acontece se um jogador não puder se mover? por exemplo [1,0,0,2,1,0,2,2,1], no , o jogador 2 não pode se mover - isso é uma vitória para o jogador 1?
VisualMelon
1
@LeifWillerts Talvez eu esteja entendendo mal o que você quer dizer, mas nesse caso, nenhum jogador está em um estado vencedor - eles apenas vencem tendo uma linha horizontal ou vertical (não diagonal).
VisualMelon
3
Bem, existem apenas 1680 posições válidas na placa, portanto, a codificação pode dar um pouco mais de 210 bytes. (menos se simetria é considerado)
lirtosiast

Respostas:

1

Haskell, 580 564 441 bytes

É assim que eu posso jogar golfe por enquanto. Não tenho certeza se os outros idiomas podem vencê-lo.

Ligue mpara uma lista de listas como [[2,1,0],[2,1,2],[0,0,1]](Definido 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

Código do teste:

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 retorna:

:)
:)
:)
:)
:|
:|
:|
:(
:(
Leif Willerts
fonte
1
Corrigido, eu acho. Vai reverificar e devidamente golf à noite (que aqui é antes de terminar o período de carência)
Leif Willerts
5

C # - 739 663 bytes

Programa completo, lê a entrada do argv e parece funcionar. Execute como

ThreeMill 1 2 1 1 2 0 0 0 2

Se esse método de entrada for inaceitável, ficarei feliz em alterá-lo (nunca como usar o 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))]);}}

Eu não estava disposto a postar isso ontem, porque não consegui jogar muito (não tive muito tempo e posso estar fora de prática), mas como ainda não houve resposta, eu ' vou postá-lo de qualquer maneira, eu certamente não espero a recompensa, prefiro que alguém tenha se esforçado um pouco mais antes de postar!

Edit: substituiu todos os bools por ints, o que significava que eu poderia usar melhor o Linq e consegui recolher os dois loops de foreach, dando grandes economias. Estou um pouco surpreso que o hcontador funcione ... ++ é um utilitário tão sutil.

O programa é muito simples, apenas explora todos os conjuntos de movimentos possíveis (armazena o estado do quadro em uma string []). Ele repete todos os nossos movimentos possíveis (os painéis que resultam disso) e conta o número de respostas de nosso oponente que podemos vencer com êxito ( G) (ou seja, aquelas que vencemos e ele não). Também conta o número de respostas possíveis (h) Se podemos ganhar alguma, é possível e adicionamos 1 à soma; se podemos vencer todas, é definitivo e adicionamos 2 à soma. O máximo de alguns é, portanto, o nosso melhor resultado possível, e indexamos na string "(|))" para retornar a face apropriada. Observe que precisamos do extra ")" porque a soma pode ser 2 ou 3, se for definitiva (é possível que não pareçamos ser capazes de vencer nenhuma resposta que já tenha vencido na primeira tentativa, portanto, a verificação possível é um pouco enganador).

O programa procura uma vitória produzindo uma sequência do tabuleiro, que é linhas e colunas separadas por espaço, e apenas procura uma sequência de 3 caracteres do jogador nessa sequência (por exemplo, "201 201 021 220 002 111" é um ganhar para nós)

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 
            ]);

    }
}

Aqui está o meu script de teste:

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

Quais saídas

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
VisualMelon
fonte
Agradável. Obrigado por ser o primeiro. :) Se estiver tudo bem, concederei a recompensa após o fim de semana, para mantê-la por mais alguns dias na guia "Em destaque".
insertusernamehere
@insertusernamehere Isso é bom para mim, se eu não puder me incomodar em fazer um trabalho real, talvez faça mais algum trabalho amanhã.
VisualMelon
1
Isso me lembra este comentário: " Não fui capaz de responder a uma pergunta por QUARENTA MINUTOS. Isso é crítico! Basta enviar detalhes do banco de dados e inserirei minhas respostas no SQL. Tenho muito trabalho a evitar e nenhuma razão para evitá-lo !! "on Por que o Stack Overflow não está funcionando? . :)
insertusernamehere
1

PowerShell 576 550 bytes

Não serei tão facilmente intimidado - se não conseguir C # abaixo de 631 bytes, terei que usar um idioma diferente! Espero que Leif Willerts retire 5 bytes de sua resposta, porque decidi que não gosto muito do PowerShell, talvez apenas precise examiná-lo objetivamente em termos de contagem de bytes ...

Este é um script, você o executa . .\mill.ps1 "201102021". É muito bem uma cópia da minha resposta em C #, apenas em um idioma com o qual tenho pouca experiência. Não fiz muito esforço para jogar golfe, porque demorou tanto tempo para começar a trabalhar em primeira instância e já é razoavelmente compacto.

Editar: não era possível simplesmente deixar essas [Math]::Floorchamadas lá

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]

Se você faz uma descrição de como funciona ... a resposta em C # é para você, mas espero que os comentários deixem claro o suficiente. Os pontos e vírgulas podem não corresponder perfeitamente ao comando de linha única, ainda não tenho certeza de onde eles são necessários e não são, e não os copiei novamente quando coloquei tudo em uma linha.

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

Script de teste (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"

Saída do mesmo:

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
:(
:(
VisualMelon
fonte
1

Python 3, 566 557 bytes

Vou ter que ver se consigo jogar mais, ou se consigo o bônus de 30%, mas depois de muita procrastinação, aqui está a minha resposta.

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
fonte