O que você está esperando? (Um solucionador de mahjong)

13

Ideia graças a @ MartinBüttner de uma discussão no chat

Mahjong é um jogo de peças imensamente popular na Ásia. Normalmente é jogado com quatro jogadores, e o objetivo do jogo é ser a primeira pessoa a completar uma mão válida usando as peças. Para esse desafio, consideraremos uma versão simplificada do jogo - PPCG mahjong.

Em PPCG mahjong, existem três fatos - m, pe s- e as telhas são numerados de 1a 9. Há exatamente quatro cópias de cada peça, e as peças são indicadas por seu número seguido por seu naipe (por exemplo 3m, 9s).

Uma mão de mahjong PPCG concluída consiste em quatro conjuntos de três e um par, para um total de 14 peças.

Um conjunto de três pode ser:

  • Três do mesmo bloco (por exemplo 4s 4s 4s, mas não 4m 4p 4s), ou
  • Uma sequência de três peças consecutivas no mesmo naipe (por exemplo, 1s 2s 3sou 6p 7p 8pmas não 3s 4m 5mou 3p 5p 7p). As sequências não são agrupadas (por isso 9m 1m 2mé inválido).

Um par é simplesmente duas peças idênticas (por exemplo 5s 5s).

O desafio

Seu programa receberá uma mão separada por espaço de 13 blocos, com cada bloco aparecendo no máximo quatro vezes. Você pode escrever um programa completo ou uma função que aceita uma string.

Sua tarefa é encontrar todas as 14as peças possíveis ("espera") que, quando adicionadas à mão, formariam uma mão de mahjong PPCG concluída. Os blocos gerados devem ser separados por espaço, mas podem estar em qualquer ordem. Espaços em branco à esquerda ou à direita são permitidos.

Seu programa deve ser executado em um período de tempo razoável, não mais que um minuto.

Exemplos

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 

No primeiro exemplo, 1m 4s 7p 3mtodos formam trigêmeos existentes, deixando o solitário 9spara formar um par.

No segundo exemplo, o 2s 3se 7p 8ppode formar apenas sequências, e os blocos restantes podem formar apenas trigêmeos. Portanto, nenhum par pode ser formado e não há saída.

No terceiro exemplo, a mão se divide em 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Normalmente, isso seria uma espera 3m 1s, mas, como todos os quatro 3mforam usados, a única espera disponível é 1s.

No quarto exemplo, todas as mpeças completam a mão. Por exemplo, para 1m, poderia-se ter 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9muma mão completa.

Tente elaborar o restante do quarto exemplo e do quinto exemplo :)

Pontuação

Isso é , então a solução com o menor número de bytes vence. Aplicam-se brechas padrão .

Sp3000
fonte
9
Obrigado por realmente fazer Mahjong, em vez do solitário (irritante da OMI) usando os ladrilhos que os ocidentais parecem pensar sempre que ouvem a palavra "Mahjong".
Justin
@ Quincunx Curiosidade: Esse desafio surgiu porque eu queria fazer um desafio com uma representação ASCII do Mahjong Solitaire (o que ainda posso fazer em algum momento ...). Mas eu chamei de "Mahjong Solitaire". ;)
Martin Ender
2
@ Quincunx: Eu não acho que é culpa deles. É culpa dos desenvolvedores de jogos chamar seus jogos de "Mahjong Solitaire" de "Mahjong" e nada mais.
Joe Z.
Que tal sete pares? treze órfãos? você poderia fazer algo ainda mais complexo com honras :) Você acha que é fora de propósito se eu criar um codegolf que peça para calcular o shanten (número mínimo de peças necessárias antes de ter o tenpai - pronto para vencer) de uma mão?
V. Courtois
@VCourtois Já faz um tempo, mas eu lembro especificamente de excluir sete pares, treze órfãos, honras e já fez ligações para não complicar demais o desafio para as pessoas novas no jogo. Acho que pensei em fazer um desafio de favela mais tarde depois disso, mas nunca fiz no final - se você quiser postar um, acho que seria um bom desafio.
Sp3000

Respostas:

4

Pitão, 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 pega uma string como entrada e retorna uma string como saída.

O pequeno número de peças (27) torna rápido o suficiente para testar se cada uma delas completa a mão. O problema passa a verificar se uma mão é válida. A função usa um algoritmo de retorno simples que considera todas as opções possíveis de conjuntos e verifica se algum deles é uma mão completa.

As mãos são representadas como histogramas de ladrilhos, ou seja, uma lista de contagens de ladrilhos (para todos os ladrilhos, não apenas os presentes na mão.) Isso facilita a verificação de se temos um determinado número de um determinado ladrilho e se ter uma sequência de peças adjacentes (o preenchimento entre peças de roupas diferentes evita seqüências de várias peças).

Ell
fonte
Ah, você me derrotou: P De qualquer forma, parece que você pode usar mapem alguns lugares, como:H(map((S+t).count,T))
FryAmTheEggman
@FryAmTheEggman Perdeu isso. Obrigado!
Ell
@ Sp3000 É Python 2. Isso é estranho; funciona bem para mim no 2.7.8.
Ell
O @Ell Works em 2.7.8 - 2.7.5 não gostou da 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 
)

Teste no console do 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)))

Resultado

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
fonte