Worauf wartest du? (Ein Mahjong-Löser)

13

Idee danke an @ MartinBüttner aus einer Diskussion im Chat

Mahjong ist ein Kachelspiel, das in Asien sehr beliebt ist. Es wird normalerweise mit vier Spielern gespielt, und das Ziel des Spiels ist es, die erste Person zu sein, die eine gültige Hand mit den Plättchen abschließt. Für diese Herausforderung werden wir eine vereinfachte Version des Spiels betrachten - PPCG Mahjong.

In PPCG Mahjong gibt es drei Anzüge - m, pund s- und die Fliesen sind nummeriert von 1zu 9. Es gibt genau vier Kopien von jeder Platte, und die Fliesen sind gekennzeichnet durch ihre Nummer von seinem Beispiel gefolgt (zB 3m, 9s).

Eine fertige PPCG-Mahjong-Hand besteht aus vier Dreisätzen und einem Paar mit insgesamt 14 Plättchen.

Ein Satz von drei kann entweder sein:

  • Drei gleiche Kacheln (zB 4s 4s 4s, aber nicht 4m 4p 4s) oder
  • Eine Folge von drei aufeinanderfolgenden Plättchen in derselben Farbe (z. B. 1s 2s 3soder, 6p 7p 8paber nicht 3s 4m 5moder 3p 5p 7p). Sequenzen werden nicht umbrochen ( 9m 1m 2mist also ungültig).

Ein Paar sind einfach zwei identische Kacheln (zB 5s 5s).

Die Herausforderung

Ihr Programm erhält eine durch Leerzeichen getrennte Hand mit 13 Plättchen, wobei jedes Plättchen nicht öfter als viermal erscheint. Sie können entweder ein vollständiges Programm oder eine Funktion schreiben, die eine Zeichenfolge enthält.

Ihre Aufgabe ist es, alle möglichen 14. Plättchen ("Wartezeiten") zu finden, die, wenn sie der Hand hinzugefügt werden, eine fertige PPCG-Mahjong-Hand bilden würden. Die ausgegebenen Kacheln sollten durch Leerzeichen getrennt sein, können jedoch in beliebiger Reihenfolge sein. Führende oder nachfolgende Leerzeichen sind zulässig.

Ihr Programm sollte in einer angemessenen Zeitspanne von nicht mehr als einer Minute ausgeführt werden.

Beispiele

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 

Im ersten Beispiel 1m 4s 7p 3mbilden alle vorhandenen Drillinge, so dass der Einzelne 9sein Paar bildet.

Im zweiten Beispiel können die 2s 3sund 7p 8pnur Sequenzen bilden, und die verbleibenden Kacheln können nur Tripletts bilden. Daher kann kein Paar gebildet werden und es erfolgt keine Ausgabe.

Im dritten Beispiel teilt sich die Hand in 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Normalerweise wäre dies eine Wartezeit 3m 1s, aber da alle vier 3mverwendet wurden, ist die einzige verfügbare Wartezeit 1s.

Im vierten Beispiel mvervollständigen alle Kacheln die Hand. Zum Beispiel 1mkönnte 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9mman eine fertige Hand haben.

Versuchen Sie, den Rest des vierten und fünften Beispiels herauszufinden :)

Wertung

Das ist , also gewinnt die Lösung mit den wenigsten Bytes. Es gelten Standardlücken .

Sp3000
quelle
9
Vielen Dank, dass Sie tatsächlich Mahjong spielen, anstatt den (IMO ärgerlichen) Solitär zu verwenden, an den die Westler zu denken scheinen, wenn sie das Wort "Mahjong" hören.
Justin
@Quincunx Witzige Tatsache: Diese Herausforderung entstand, weil ich eine Herausforderung mit einer ASCII-Darstellung von Mahjong-Solitaire durchführen wollte (was ich möglicherweise noch irgendwann tun werde ...). Ich habe es allerdings "Mahjong Solitaire" genannt. ;)
Martin Ender
2
@ Quincunx: Ich glaube nicht, dass es ihre Schuld ist. Es ist die Schuld der Spieleentwickler, ihre "Mahjong Solitaire" -Spiele "Mahjong" zu nennen und sonst nichts.
Joe Z.
Was ist mit sieben Paaren? dreizehn Waisenkinder? du könntest etwas noch komplexeres mit Auszeichnung machen :) Glaubst du, es ist zwecklos, wenn ich einen Codegolf entwerfe, bei dem die Shanten (minimale Anzahl von Plättchen, die benötigt werden, bevor Tenpai - bereit zum Gewinnen) einer Hand berechnet werden ?
V. Courtois
@VCourtois Es ist schon eine Weile her, aber ich erinnere mich ausdrücklich, dass sieben Paare, dreizehn Waisen, Ehrungen und bereits getätigte Anrufe ausgeschlossen wurden, um die Herausforderung für neue Spieler nicht zu komplizieren. Ich glaube, ich habe später darüber nachgedacht, eine Shanten-Challenge zu machen, aber am Ende nie - wenn Sie eine veröffentlichen möchten, wäre das eine gute Challenge.
Sp3000,

Antworten:

4

Python, 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 Nimmt einen String als Eingabe und gibt einen String als Ausgabe zurück.

Durch die geringe Anzahl von Kacheln (27) ist es schnell genug zu testen, ob jede von ihnen die Hand vervollständigt. Das Problem wird zu überprüfen, ob eine Hand gültig ist. Die Funktion verwendet einen einfachen Backtracking-Algorithmus, der alle möglichen Auswahlmöglichkeiten von Sätzen berücksichtigt und prüft, ob sich einer von ihnen zu einer vollständigen Hand summiert.

Hände werden als Kachelhistogramme dargestellt, dh eine Liste von Kachelzählungen (für alle Kacheln, nicht nur für die in der Hand vorhandenen). Auf diese Weise können Sie leicht überprüfen, ob wir eine bestimmte Nummer einer bestimmten Kachel haben und ob wir eine Folge benachbarter Kacheln haben (das Auffüllen zwischen Kacheln verschiedener Farben verhindert Mehrfachkombinationsfolgen.)

Ell
quelle
Ah, du hast mich geschlagen: P Wie auch immer, es sieht so aus, als ob du es mapan ein paar Orten verwenden H(map((S+t).count,T))
kannst
@FryAmTheEggman Habe das verpasst. Vielen Dank!
Ell
@ Sp3000 Es ist Python 2. Das ist seltsam; es funktioniert gut für mich am 2.7.8.
Ell
@Ell Works in 2.7.8 - 2.7.5 mochte die 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
)

Erklärt

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 in der FireFox / FireBug-Konsole

;["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)))

Ausgabe

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
quelle