Resolviendo Mastermind en 6 o menos movimientos

24

Tu objetivo es escribir un programa que resuelva cualquier rompecabezas de Mastermind en 6 o menos movimientos.

Fondo

Mastermind es un juego de mesa. El objetivo del juego es adivinar exactamente la combinación (colores y orden) de 4 clavijas de colores ocultas por el otro jugador. Cuando se hace una suposición, el otro jugador responde con entre 0 y 4 clavijas blancas o rojas. Una clavija roja es donde el color y la ubicación son correctos. Una clavija blanca es donde se representa el color en las piezas restantes, pero está en la ubicación incorrecta. Si hay colores duplicados en la suposición, solo se otorgará una clavija por color correspondiente en el secreto. (Entonces, si el secreto contenía 1 Azul, y la suposición tenía 2 azules con uno en la ubicación correcta, se daría una clavija roja). Hay 6 colores diferentes y se pueden usar duplicados.

Entonces, por ejemplo, un juego podría ser el siguiente: (Suponiendo que la solución es Rojo Verde Verde Azul)

1:  Blue Purple Black Green - 2 white pegs
2:  Green Red Black Blue    - 2 white pegs, 1 red peg
3:  Green Green Green Blue  - 3 red pegs
4:  Red Green Green Blue    - 4 red pegs

Las reglas se expanden en Wikipedia.

Requisitos

  • El programa debe leer desde stdin y escribir en stdout
  • Usaré números por simplicidad en lugar de colores. La combinación para adivinar será 4 números entre 1 y 6
  • Deben generar sus conjeturas como una serie de 4 números separados por espacios del 1 al 6 que concluyen con una nueva línea. Por ejemplo:

    1 5 2 2 \ n

  • El programa recibirá posteriormente como entrada después de su conjetura 2 enteros entre 0 y 4 separados por un espacio y concluyendo con una nueva línea. El primero será la cantidad de clavijas blancas, el segundo la cantidad de clavijas rojas.

  • En una entrada de "0 4" (4 clavijas rojas), el programa debe terminar
  • El programa debe ser capaz de resolver cualquier rompecabezas en menos de 6 turnos (el programa que da salida, seguido de la entrada de respuesta es 1 turno). No hay bonificación (debido a la complejidad de la prueba) por poder resolverlo en menos.
  • La solución debe ser completamente interna e incluida en la fuente. Solo se permiten bibliotecas estándar. Por lo tanto, la solución puede no depender de ningún otro archivo (como diccionarios) o Internet.

Ejemplo de entrada / salida

> is your programs output
< is the responding input
Solution is 1 5 6 6

> 1 2 3 4
< 0 1
> 4 1 6 6
< 1 2
> 1 6 5 6
< 2 2
> 1 5 6 6
< 0 4 

Tanteo

  • Esto es puro y simple Code Golf . La solución más corta en bytes gana.

Esta es mi primera pregunta de Code Golf. Pido disculpas si he hecho algo mal, pero he intentado lo mejor posible para asegurarme de que no haya absolutamente ninguna ambigüedad y evitar la mayor cantidad posible de reglas de abogacía. Si he sido ambiguo o poco claro, no dude en hacer preguntas.

lochok
fuente
1
En su ejemplo, la entrada / salida no debería 1 2 3 4volver 0 1?
Gaffi
1
Y en el texto de ejemplo, ¿no debería "Verde Verde Verde Azul" dar una clavija blanca también (para el primer Verde)? EDITAR: Wikipedia aclara que no se debe dar blanco, como escribió. Pero creo que las reglas blanco / negro deberían estar explícitamente establecidas en la pregunta.
Ugoren
¿Qué tan lento se le permitirá trabajar?
dejó de girar en contra del reloj el
@Gaffi - Absolutamente correcto - arreglado
lochok
1
Las reglas para las clavijas blancas no se indican aquí. Suponga que eligió 1234 y supongo que 5611. Ambos mis 1 son del color correcto en el lugar incorrecto, por lo que por la forma en que declaró las reglas, diría que obtengo 2 blancos. Pero no, Wikipedia dice que es 1 blanco. El método incorrecto también es más fácil de programar (pero Steven Rumbalski implementó correctamente las reglas de Wikipedia).
Ugoren

Respuestas:

8

Python 2 Python 3, 359 365 338 caracteres

from itertools import*
from collections import*
m=h=set(product(*['123456']*4))
def f(g,k):b=sum(x==y for x,y in zip(g,k));return'%s %s'%(sum(min(g.count(c),k.count(c))for c in set(g))-b,b)
while len(m)>1:g=min(h,key=lambda g:max(Counter(f(g,k)for k in m).values()));print(*g);s=input();m=set(x for x in m if s==f(g,x))
print(*(m.pop()))

Curioso, me llevó muchas ediciones darme cuenta de que tenía un nombre de variable de cinco caracteres.

No me gustan las importaciones largas. Parece que debería poder implementar un reemplazo para collections.Countereso ahorraría la importación.

Tampoco me gusta print(*(m.pop()))el final. Parece que debería desaparecer en el ciclo while, pero no puedo encontrar una manera de hacerlo sin hacerlo más largo.

Steven Rumbalski
fuente
TypeError: join() takes exactly one argument (2 given)en return j(sum(min(g.count(c),k.count(c))for c in set(g))-b,b). también, sum () devuelve un int, mientras que j (str.join) debería tomar un iterable
Blazer
Mi estructura de bucle se deshace de la final print, y creo que es un poco más corta. También coincide mejor con el comportamiento solicitado (deteniéndose en "4 0" en lugar de cuando conoce la respuesta). Y len(m)>1== m[1:]. Importar es realmente molesto, from a,b import *hubiera sido agradable.
ugoren
Este programa parece salir cada vez que lo considera correcto. una vez lo ejecuté y se detuvo a las 5 conjeturas, no era correcto. la próxima vez se detuvo a las 4 conjeturas y fue correcto, pero ni siquiera ingresé 4 0, lo cual está en el criterio objetivo, y otras veces saldrá con una excepción:print(*(m.pop())) KeyError: 'pop from an empty set'
Blazer
@Chaqueta de sport. ¿Cuáles son los casos de prueba que causaron esta salida?
Steven Rumbalski
@Blazer: 4 0son cuatro clavijas blancas. Creo que tienes la puntuación invertida.
Steven Rumbalski
7

Haskell, 317 304

q[0,4]_=error""
q[n,m]l=(\s->all(==4-m)[g s,n+g(u(foldr((u((.drop 1).(++)).).break.(==)))$unzip s)]).c(u(/=)).zip l
u=uncurry;m=map;g=length;c=filter
f a=[head.c(($(zipWith q(take n a)$f a)).all.flip($)).sequence$replicate 4[1..6]|n<-[0..]]
main=interact$unlines.m(unwords.m show).f.m(m read.words).lines

¡Me encanta escribir programas interactivos puramente funcionales! Pero, por supuesto, este estilo tiene ciertas limitaciones: termina ahora con un error, pero no especificó que esto no está bien. Necesitaría refactorizar todo en la IOmónada para obtener una salida sin errores.

dejó de girar en sentido antihorario
fuente
¿Garantiza una suposición correcta en 6 movimientos?
ugoren
Uh Yo pensaba que era (por ejemplo las obras de la OP y otras soluciones), pero la prueba exhaustiva muestra que a veces necesita 7 conjeturas. ¡Tendré que trabajar en esto todavía!
dejó de girar en contra del reloj el
5

Python, 385 357 caracteres, resuelve en 5 movimientos

Cuanto más lo cambio, crece más y más como el de Steven Rumbalski ... La principal diferencia es que trabaja con cuerdas en lugar de enteros.
Implementé el algoritmo de Knuth (correctamente ahora, espero).
Tomó prestada la función de puntuación de Steven Rumbalski.
Generar la primera aproximación lleva mucho tiempo, luego mejora.
Codificarlo ( g=len(A)==1296 and [1,1,2,2] or ...) hace la vida más fácil si quieres probarlo.
No cuento 4 líneas nuevas + pares de pestañas, que se pueden reemplazar por punto y coma.

from collections import*
from itertools import*
a=B=A=list(product(*[range(1,7)]*4))
r=lambda g,k:(sum(x==y for x,y in zip(g,k)),sum(min(g.count(c),k.count(c))for c in set(g)))
while a!=4:
    g=A[1:]and min(B,key=lambda x:max(Counter(r(x,i)for i in A).values()))or A[0]
    print"%d "*4%tuple(g)
    b,a=map(int,raw_input().split())
    A=[x for x in A if r(g,x)==(a,a+b)]
Ugoren
fuente
"%d "*4%tuple(g)
gnibbler
from collections import*
gnibbler
a,b=map(int,raw_input())
gnibbler
product(*[range(1,7)]*4)
gnibbler
Counter(r(x,i)for i in A).values()
gnibbler