¿Que estas esperando? (Un solucionador de mahjong)

14

Idea gracias a @ MartinBüttner de una discusión en el chat

Mahjong es un juego de fichas que es inmensamente popular en Asia. Por lo general, se juega con cuatro jugadores, y el objetivo del juego es ser la primera persona en completar una mano válida usando las fichas. Para este desafío, consideraremos una versión simplificada del juego: PPCG mahjong.

En PPCG mahjong, hay tres trajes - m, py s- y las baldosas están numeradas del 1a 9. Hay exactamente cuatro copias de cada mosaico, y los mosaicos se denotan por su número seguido de su palo (por ejemplo 3m, 9s).

Una mano de mahjong PPCG completa consta de cuatro juegos de tres y un par, para un total de 14 fichas.

Un conjunto de tres puede ser:

  • Tres del mismo mosaico (por ejemplo 4s 4s 4s, pero no 4m 4p 4s), o
  • Una secuencia de tres fichas consecutivas en el mismo palo (por ejemplo, 1s 2s 3so 6p 7p 8ppero no 3s 4m 5mo 3p 5p 7p). Las secuencias no se ajustan (por 9m 1m 2mlo que no es válido).

Un par es simplemente dos fichas idénticas (por ejemplo 5s 5s).

El reto

Su programa recibirá una mano separada por espacios de 13 mosaicos, y cada mosaico no aparecerá más de cuatro veces. Puede escribir un programa completo o una función que incluya una cadena.

Su tarea es encontrar todas las baldosas 14 posibles ("esperas") que, cuando se agregan a la mano, formarían una mano de mahjong PPCG completa. Los mosaicos de salida deben estar separados por espacios, pero pueden estar en cualquier orden. Se permiten espacios en blanco iniciales o finales.

Su programa debe ejecutarse en un período de tiempo razonable, no más de un minuto.

Ejemplos

Input: 1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s
Output: 9s

Input: 1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p
Output:

Input: 1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s
Output: 1s

Input: 1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m
Output: 1m 2m 3m 4m 5m 6m 7m 8m 9m

Input: 1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s
Output: 1m 4m 6s 9s 

En el primer ejemplo, 1m 4s 7p 3mtodos forman tripletes existentes, dejando al solitario 9spara formar un par.

En el segundo ejemplo, el 2s 3sy 7p 8psolo puede formar secuencias, y los mosaicos restantes solo pueden formar trillizos. Por lo tanto, no se puede formar un par y no hay salida.

En el tercer ejemplo, la mano se divide en 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Normalmente, esto sería una espera 3m 1s, pero como 3mse han utilizado los cuatro , la única espera disponible es 1s.

En el cuarto ejemplo, todas las mfichas completan la mano. Por ejemplo, para 1m, uno podría tener 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9muna mano completa.

Intenta resolver el resto del cuarto ejemplo y el quinto ejemplo :)

Puntuación

Este es el , por lo que la solución en la menor cantidad de bytes gana. Se aplican lagunas estándar .

Sp3000
fuente
99
Gracias por hacer realmente Mahjong, en lugar del solitario (molesto de la OMI) que usa los mosaicos en los que los occidentales parecen pensar cada vez que escuchan la palabra "Mahjong".
Justin
@Quincunx Dato curioso: Este desafío surgió porque quería hacer un desafío con una representación ASCII del solitario Mahjong (que aún podría hacer en algún momento ...). Sin embargo, lo llamé "solitario Mahjong". ;)
Martin Ender
2
@Quincunx: No creo que sea su culpa. Es culpa de los desarrolladores de juegos llamar a sus juegos "Mahjong solitario" "Mahjong" y nada más.
Joe Z.
¿Qué hay de siete pares? trece huérfanos? podrías hacer algo aún más complejo con honores :) ¿Crees que está fuera de propósito si creo un codegolf que pida calcular la shanten (número mínimo de fichas necesarias antes de obtener diezpai , listo para ganar) de una mano?
V. Courtois
@VCourtois Ha pasado un tiempo, pero recuerdo que específicamente excluí siete pares, trece huérfanos, honores y ya hice llamadas para no complicar demasiado el desafío para las personas nuevas en el juego. Creo que consideré hacer un desafío de shanten más tarde, pero nunca lo hice al final; si desea publicar uno, creo que sería un buen desafío.
Sp3000

Respuestas:

4

Pitón, 312 281 bytes

def W(S):H=lambda C,n=0,t=1:sum([m<C[0]and H([c-s for c in C][:l]+C[l:],n+1,u)for m,s,l,u in(2,3,1,t),(t,2,1,4),(4-5*all(C[:3]),1,3,t)])|H(C[1:],n,t)if C[2:]and max(C)<5else n>4;T=[i+s for s in"mps"for i in"12345678900"];return" ".join(t for t in T if("1"<t)*H(map((S+t).count,T)))

W toma una cadena como entrada y devuelve una cadena como salida.

El pequeño número de fichas (27) lo hace lo suficientemente rápido como para probar si cada una de ellas completa la mano. El problema es comprobar si una mano es válida. La función utiliza un algoritmo de retroceso simple que considera todas las opciones posibles de conjuntos y comprueba si alguno de ellos se suma a una mano completa.

Las manos se representan como histogramas de mosaico, es decir, una lista de recuentos de mosaicos (para todos los mosaicos, no solo los presentes en la mano). Esto hace que sea fácil verificar si tenemos un cierto número de cierto mosaico y si tener una secuencia de mosaicos adyacentes (el relleno entre mosaicos de diferentes trajes evita las secuencias de varios trajes).

Ana
fuente
Ah, me mapH(map((S+t).count,T))
ganaste
@FryAmTheEggman se lo perdió. ¡Gracias!
Ell
@ Sp3000 Es Python 2. Eso es extraño; funciona bien para mí en 2.7.8.
Ell
@Ell Works en 2.7.8 - 2.7.5 no le gustó el 5else: P
Sp3000
2

JavaScript (E6) 306

F=h=>(
  R=(a,p,n=1)=>(a=[...a]).splice(p,n)&&a,
  K=(t,d=3)=>
    !t[0]
    |t.some(
      (v,p)=>
        v==t[p+1]&v==t[p+d-1]&&
        K(R(t,p,d))
      ||
        ~((r=t.indexOf((x=-~v[0])+v[1]))|(s=t.indexOf(-~x+v[1])))&&
        K(R(R(R(t,s),r),p))
    ),
  o=[],
  [for(s of'mps')for(i of'123456789')h.replace(t=i+s,s,'g')[34]
  &&K([t,...h.split(' ')].sort(),2)&&o.push(t)
  ],o
)

Explicado

F=hand=>(
  Remove=(a,p,n=1)=>                // function to remove 1 or more element from an array, returning a new shorter array
    ((a=[...a]).splice(p,n), a),    // using array.splice on a new created array 

  Check=(ckHand, dim)=>  // recursive function to check hand. 
                         // removing pairs (at iteration 0) or sequence of three, if at last the hand remain empty then success
                         // parameter dim is 2 or 3 indicating how many equal elements are to be removed
    !ckHand[0]           // check if empty (element 0 does not exist)
    |ckHand.some(        // else traverse all array checking what can be removed
      (value, position)=> 
        value == ckHand[position + 1] 
        & value == ckHand[position + dim-1] &&   // look for 3 (or 2) equal elements
        Check(Remove(ckHand, position, dim), 3)   // if found, then remove elements and check again
      ||
        ~((r = ckHand.indexOf((x=-~value[0]) + value[1]))     // value[0] is number, value[1] is suit 
        |(s = ckHand.indexOf(-~x + value[1]))) &&              // look for an ascending sequence in following elements (the array is sorted)
        Check(Remove(Remove(Remove(ckHand, s), r), position),3) // if sequence found, remove elements and check again
    ),
  output=[], // start with an empty solution list
  [ // using array comprehension to implement a double loop
    for(s of'mps')        // loop for all suits
    for(i of'123456789')  // loop for all numbers
    (
       tile=i+s, // current tile 
       (hand.replace(tile,' ','g').length > 34)      // if tile is present 4 times in hand, the replaced length is 38-4 == 34
       && (                                       // else proceed with check
         ckHand = hand.split(' '), 
         ckHand.push(tile),    // in ckHand (as an array) the hand to be checked, that is base hand + current tile
         ckHand.sort(),        // sorting the array simplfy the checks
         Check(ckHand, 2)      // start checks looking for a pair
       )
       && 
         output.push(tile)   // if check ok, add tile to the solution list
    )   
  ],
  output // last expression in list is the function return value 
)

Prueba en la consola FireFox / FireBug

;["1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s", "1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p",
 "1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s", "1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m",
 "1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s"].forEach(s=>console.log(s+' => '+F(s)))

Salida

1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s => 9s
1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p =>
1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s => 1s
1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m => 1m,2m,3m,4m,5m,6m,7m,8m,9m
1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s => 1m,4m,6s,9s
edc65
fuente