¿Puedes ganar con dos movimientos más en Three Men's Morris?

16

Recompensas

No. 1 ( otorgado )

Lanzaré 50 repeticiones por la primera respuesta válida

No. 2 ( otorgado )

Agregaré otras 100 repeticiones para obtener la respuesta válida más corta.

No. 3 ( abierto para envíos )

Lanzaré 200 repeticiones por la primera con una respuesta válida más corta y significativa. Ser significativo como máximo el 45% de la respuesta más corta actual ( 564 bytes x 0.45 = máximo 254 bytes ).


El juego

¿Recuerdas el clásico juego " Morris de nueve hombres " o simplemente " Mill "? Hay una variación llamada Three Men's Morris que es un poco como un tic-tac-toe mutable.

Reglas

Este es el tablero en blanco del juego:

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

[ ] es un campo y |–/\ representa rutas entre esos campos.

El juego es jugado por dos jugadores 1y 2cada uno coloca 3 fichas en el tablero. Esto ya sucedió y estamos en el juego. El juego se gana si un jugador puede formar unmill fila vertical u horizontal de las 3 fichas del jugador.

Las fichas se pueden mover en el tablero a lo largo de las líneas de conexión, de acuerdo con esta regla:

A cualquier posición vacía adyacente (es decir, desde una posición de borde al centro, o desde el centro a una posición de borde, o desde una posición de borde a una posición de borde adyacente

Un jugador debe hacer un movimiento a menos que no haya una posición vacía adyacente, en cuyo caso se omite el movimiento.

El reto

Eres jugador 1y tu movimiento es el siguiente. Escriba un programa o una función que determine si:

  • puedes forzar una victoria con 2 o menos movimientos ( victoria definitiva )
  • puedes ganar con 2 o menos movimientos, si tu oponente comete un error ( posible victoria )
  • no puedes ganar con 2 o menos movimientos, porque necesitarás más movimientos o porque los movimientos forzados llevan a tu oponente a ganar ( imposible de ganar )

Requisitos

  • Aunque definitivamente ganas cuando matas a tu oponente, tu programa debe terminar en un tiempo finito.
  • Puedes escribir un programa o una función.

Entrada

Los jugadores están representados por 1y 2. 0define un campo libre Puede tomar la entrada como una matriz o una 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]

Posible

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]

Imposible

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]

Salida

Su programa debería generar / devolver un smiley:

  • Victoria definitiva: :)
  • Posible victoria: :|
  • Imposible ganar: :(

Ejemplos

Victoria definitiva en dos movimientos:

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

Posible victoria en dos movimientos:

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

Imposible ganar en dos movimientos:

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

Prima

En caso de que sea posible una victoria definitiva y su programa genere los movimientos de una forma hacia el éxito, así como a1:a2(1 movimiento) o a1:a2,a3:b2(2 movimientos), puede retirar el 30% de su conteo de bytes.


Este es el código de golf, por lo que la respuesta más corta en bytes gana. Las lagunas estándar no están permitidas.


Gracias a Peter Taylor que solucionó algunos defectos y mejoró la redacción en el Sandbox .

insertusernamehere
fuente
Relacionados .
insertusernamehere
1
Me gustan esas tablas / gráficos ascii =)
flawr
1
¿Qué sucede si un jugador no puede moverse? por ejemplo [1,0,0,2,1,0,2,2,1], en , el jugador 2 no puede moverse: ¿es una victoria para el jugador 1?
VisualMelon
1
@LeifWillerts Podría estar malinterpretando lo que quieres decir, pero en ese caso, ningún jugador está en un estado ganador: solo ganan teniendo una línea horizontal o vertical (no diagonal).
VisualMelon
3
Bueno, solo hay 1680 posiciones de placa válidas, por lo que la codificación puede dar un poco más de 210 bytes. (menos si se considera la simetría)
lirtosiast el

Respuestas:

1

Haskell, 580 564 441 bytes

Así de lejos puedo jugar golf por ahora. No estoy seguro si los otros idiomas pueden vencerlo.

Llame ma una 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 de prueba:

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

:)
:)
:)
:)
:|
:|
:|
:(
:(
Leif Willerts
fuente
1
Corregido, creo. Verificará y jugará golf adecuadamente en la noche (que es aquí antes de que termine el período de gracia)
Leif Willerts
5

C # - 739 663 bytes

Programa completo, lee la entrada de argv y parece funcionar. Ejecútalo como

ThreeMill 1 2 1 1 2 0 0 0 2

Si este método de entrada es inaceptable, estaré encantado de cambiarlo (nunca me gusta usar 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))]);}}

No estaba dispuesto a publicar esto ayer, porque no he podido jugar mucho golf (no he tenido tanto tiempo y podría estar fuera de práctica), pero como todavía no ha habido una respuesta, yo ' Lo publicaré de todos modos, ciertamente no espero la recompensa, ¡preferiría que fuera para alguien que haya puesto un poco más de esfuerzo antes de publicar!

Editar: reemplacé todos los bools con ints, lo que significaba que podía hacer un mejor uso de Linq, y logré colapsar ambos bucles foreach, lo que generó grandes ahorros. Estoy un poco sorprendido de que el hcontador funcione ... ++ es una utilidad tan sutil.

El programa es muy simple, simplemente explora todos los posibles conjuntos de movimientos (almacena el estado del tablero en una cadena []). Repite todos nuestros movimientos posibles (los tableros que resultan de ellos) y cuenta el número de respuestas de nuestro oponente que podemos vencer con éxito (G ) (es decir, las que ganamos y él no). También cuenta el número de respuestas posibles (h) Si podemos ganar alguno, entonces es posible, y sumamos 1 a la suma, si podemos ganarlos a todos, es definitivo, y sumamos 2 a la suma. Por lo tanto, el máximo es nuestro mejor resultado posible, e indexamos en la cadena "(|))" para devolver la cara apropiada. Tenga en cuenta que necesitamos el ")" extra porque la suma puede ser 2 o 3 si es definitivo (es posible que no parezcamos capaces de superar cualquier respuesta que ya haya ganado en el primer intento, por lo que la posible verificación es un poco engañoso).

El programa busca una victoria al producir una cadena del tablero, es decir, filas y columnas separadas por espacios, y solo busca una cadena de 3 caracteres del jugador en esta cadena (por ejemplo, "201 201 021 220 002 111" es un gana para nosotros)

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

    }
}

Aquí está mi script de prueba:

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

Que salidas

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
VisualMelon
fuente
Agradable. Gracias por ser el primero. :) Si está bien, otorgaré la recompensa después del fin de semana, para mantenerla unos días más en la pestaña destacada.
insertusernamehere
@insertusernamehere Eso está bien para mí, si no me molestan en hacer un trabajo real, podría hacer un poco más de trabajo en esto mañana.
VisualMelon
1
Esto me recuerda este comentario: " No he podido responder una pregunta durante CUARENTA MINUTOS. ¡Esto es crítico! Simplemente envíe los detalles de la base de datos e insertaré mis respuestas en SQL. Tengo mucho trabajo que evitar y no tengo razón para evitarlo !! "en ¿Por qué Stack Overflow no funciona? . :)
insertusernamehere
1

PowerShell 576 550 bytes

No me detendré tan fácilmente: si no puedo obtener C # por debajo de 631 bytes, ¡tendré que usar un lenguaje diferente! Espero que Leif Willerts elimine 5 bytes de su respuesta, porque he decidido que no soy demasiado aficionado a PowerShell, tal vez solo necesito mirarlo objetivamente en términos de conteos de bytes ...

Este es un script, lo ejecutas . .\mill.ps1 "201102021" . Es bastante una copia de mi respuesta de C #, solo en un lenguaje con el que tengo poca experiencia. No he hecho un gran esfuerzo para jugar golf, porque me llevó mucho tiempo trabajar en primera instancia, y ya es bastante compacto.

Editar: no podía dejar esas [Math]::Floorllamadas allí

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]

Si tiene una descripción de cómo funciona ... la respuesta de C # es para usted, pero espero que los comentarios lo aclaren lo suficiente. Los puntos y comas pueden no coincidir perfectamente con el comando de una sola línea, todavía no estoy seguro de dónde se necesitan y no, y no los volví a copiar cuando puse todo en una línea.

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 prueba (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"

Salida del mismo:

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
:(
:(
VisualMelon
fuente
1

Python 3, 566 557 bytes

Tendré que ver si puedo seguir jugando golf, o si puedo obtener el bono del 30%, pero después de muchas dilaciones, aquí está mi respuesta.

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

Sin golf:

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
fuente