Che cosa state aspettando? (Un risolutore di mahjong)


14

Idea grazie a @ MartinBüttner da una discussione in chat

Mahjong è un gioco a tessere che è immensamente popolare in Asia. In genere si gioca con quattro giocatori e l'obiettivo del gioco è quello di essere la prima persona a completare una mano valida usando le tessere. Per questa sfida, prenderemo in considerazione una versione semplificata del gioco - Mahjong PPCG.

In PPCG mahjong, ci sono tre vestiti - m, pe s- e le piastrelle sono numerati da 1a 9. Ci sono esattamente quattro copie di ogni piastrella, e le piastrelle sono indicate con il suo numero seguito dal suo seme (eg 3m, 9s).

Una mano di mahjong PPCG completata consiste di quattro serie da tre e una coppia, per un totale di 14 tessere.

Un set di tre può essere:

  • Tre tessere uguali (ad es. 4s 4s 4s, Ma non 4m 4p 4s), oppure
  • Una sequenza di tre tessere consecutive nello stesso seme (ad es. 1s 2s 3sOppure 6p 7p 8pno 3s 4m 5moppure oppure 3p 5p 7p). Le sequenze non vanno a capo (quindi 9m 1m 2mnon sono valide).

Una coppia è semplicemente due tessere identiche (ad es 5s 5s.).

La sfida

Il tuo programma riceverà una mano separata dallo spazio di 13 tessere, con ogni tessera che appare non più di quattro volte. È possibile scrivere un programma completo o una funzione che accetta una stringa.

Il tuo compito è quello di trovare tutte le 14 tessere possibili ("attese") che, se aggiunte alla mano, formerebbero una mano mahjong PPCG completata. Le tessere emesse devono essere separate dallo spazio, ma possono essere in qualsiasi ordine. È consentito uno spazio bianco iniziale o finale.

Il programma dovrebbe essere eseguito in un periodo di tempo ragionevole, non più di un minuto.

Esempi

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 

Nel primo esempio, 1m 4s 7p 3mtutti formano terzine esistenti, lasciando il solitario 9sa formare una coppia.

Nel secondo esempio, 2s 3se 7p 8ppuò solo formare sequenze e le tessere rimanenti possono solo formare terzine. Quindi nessuna coppia può essere formata e non c'è output.

Nel terzo esempio, la mano si divide in 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Normalmente sarebbe un'attesa 3m 1s, ma poiché tutti e quattro 3msono stati usati, l'unica attesa disponibile è 1s.

Nel quarto esempio, tutte le mtessere completano la mano. Ad esempio, per 1m, si potrebbe avere 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9mquale è una mano completata.

Prova a elaborare il resto del quarto esempio e del quinto esempio :)

punteggio

Questo è , quindi vince la soluzione nel minor numero di byte. Si applicano scappatoie standard .


9
Grazie per aver effettivamente fatto il Mahjong, piuttosto che il solitario (fastidioso dell'IMO) usando le tessere che gli occidentali sembrano pensare ogni volta che sentono la parola "Mahjong".
Giustino,

@Quincunx Curiosità: questa sfida è nata perché volevo fare una sfida con una rappresentazione ASCII del solitario Mahjong (che potrei ancora fare ad un certo punto ...). L'ho chiamato "solitario Mahjong" però. ;)
Martin Ender,

2
@Quincunx: non credo sia colpa loro. È colpa degli sviluppatori di giochi per aver chiamato i loro giochi "Mahjong solitario" "Mahjong" e nient'altro.
Joe Z.

Che ne dici di sette coppie? tredici orfani? potresti fare qualcosa di ancora più complesso con gli onori :) Pensi che sia inutile se creo un codegolf che chiede di calcolare lo shanten (numero minimo di tessere necessario prima di ottenere tenpai - pronto per vincere) di una mano?
V. Courtois,

@VCourtois È passato un po 'di tempo, ma ricordo specificamente di escludere sette coppie, tredici orfani, onori e chiamate già fatte in modo da non complicare troppo la sfida per le persone nuove al gioco. Penso di aver pensato di fare una sfida shanten più tardi, ma alla fine non l'ho mai fatto - se ti piacerebbe postarne una, penso che farebbe una bella sfida.
Sp3000,

Risposte:


4

Pitone, 312 281 byte

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 accetta una stringa come input e restituisce una stringa come output.

Il piccolo numero di tessere (27) lo rende abbastanza veloce da testare se ognuna di esse completa la mano. Il problema diventa verificare se una mano è valida. La funzione utilizza un semplice algoritmo di backtracking che considera tutte le possibili scelte di set e verifica se qualcuno di essi si somma a una mano completa.

Le mani sono rappresentate come istogrammi delle tessere, ovvero un elenco di conteggi delle tessere (per tutte le tessere, non solo quelle presenti nella mano.) Ciò consente di controllare facilmente se abbiamo un certo numero di una determinata tessera e se hanno una sequenza di tessere adiacenti (l'imbottitura tra tessere di semi diversi impedisce sequenze di semi multipli).


Ah, mi hai battuto: P Comunque, sembra che tu possa usarlo mapin un paio di posti, come:H(map((S+t).count,T))
FryAmTheEggman

@FryAmTheEggman L'ho perso. Grazie!
Ell

@ Sp3000 È Python 2. È strano; funziona bene per me il 2.7.8.
Ell

@Ell Works in 2.7.8 - 2.7.5 non ha gradito il 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
)

spiegato

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 
)

Test nella console 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)))

Produzione

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.