Qu'est-ce que tu attends? (Un mahjong solveur)


14

Idée grâce à @ MartinBüttner à partir d'une discussion dans le chat

Mahjong est un jeu de tuiles qui est extrêmement populaire en Asie. Il se joue généralement avec quatre joueurs, et le but du jeu est d'être la première personne à terminer une main valide en utilisant les tuiles. Pour ce défi, nous considérerons une version simplifiée du jeu - PPCG mahjong.

Dans le mahjong PPCG, il y a trois combinaisons - m, pet s- et les tuiles sont numérotées de 1à 9. Il y a exactement quatre copies de chaque tuile, et les tuiles sont indiquées par son numéro suivi de sa couleur (par exemple 3m, 9s).

Une main de mahjong PPCG terminée se compose de quatre ensembles de trois et d'une paire, pour un total de 14 tuiles.

Un ensemble de trois peut être soit:

  • Trois de la même tuile (par exemple 4s 4s 4s, mais pas 4m 4p 4s), ou
  • Une séquence de trois tuiles consécutives dans le même costume (par exemple 1s 2s 3sou 6p 7p 8pmais pas 3s 4m 5mou 3p 5p 7p). Les séquences ne sont pas bouclées (elles ne sont donc pas 9m 1m 2mvalides).

Une paire est simplement deux tuiles identiques (par exemple 5s 5s).

Le défi

Votre programme recevra une main séparée par des espaces de 13 tuiles, chaque tuile n'apparaissant pas plus de quatre fois. Vous pouvez écrire soit un programme complet, soit une fonction qui prend une chaîne.

Votre tâche consiste à trouver toutes les tuiles 14ème possibles ("attentes") qui, une fois ajoutées à la main, formeraient une main de mahjong PPCG terminée. Les tuiles en sortie doivent être séparées par des espaces, mais peuvent être dans n'importe quel ordre. Les espaces blancs avant ou arrière sont autorisés.

Votre programme doit s'exécuter dans un délai raisonnable, pas plus d'une minute.

Exemples

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 

Dans le premier exemple, 1m 4s 7p 3mtous forment des triplets existants, laissant le seul 9sformer une paire.

Dans le deuxième exemple, le 2s 3set 7p 8pne peut former que des séquences et les tuiles restantes ne peuvent former que des triplets. Par conséquent, aucune paire ne peut être formée et il n'y a pas de sortie.

Dans le troisième exemple, la main se divise en 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Normalement, ce serait une attente 3m 1s, mais comme les quatre 3mont été utilisés, la seule attente disponible est 1s.

Dans le quatrième exemple, toutes les mtuiles complètent la main. Par exemple, pour 1m, on pourrait avoir 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9mune main terminée.

Essayez de trouver le reste du quatrième exemple et du cinquième exemple :)

Notation

C'est du , donc la solution dans le moins d'octets l'emporte. Des échappatoires standard s'appliquent.


9
Merci d'avoir fait du Mahjong, plutôt que le solitaire (ennuyeux de l'OMI) utilisant les tuiles auxquelles les occidentaux semblent penser chaque fois qu'ils entendent le mot "Mahjong".
Justin

@Quincunx Fun fact: Ce défi est né parce que je voulais faire un défi avec une représentation ASCII du Mahjong solitaire (ce que je pourrais encore faire à un moment donné ...). Je l'ai quand même appelé "Mahjong solitaire". ;)
Martin Ender

2
@Quincunx: Je ne pense pas que ce soit leur faute. C'est la faute des développeurs de jeux pour avoir appelé leurs jeux "Mahjong solitaire" "Mahjong" et rien d'autre.
Joe Z.

Et sept paires? treize orphelins? vous pourriez faire quelque chose d'encore plus complexe avec les honneurs :) Pensez-vous que c'est inutile si je crée un codegolf qui demande de calculer le shanten (nombre minimal de tuiles nécessaires avant d'obtenir tenpai - prêt à gagner) d'une main?
V. Courtois

@VCourtois Cela fait un moment, mais je me souviens avoir spécifiquement exclu sept paires, treize orphelins, honneurs et déjà fait des appels afin de ne pas trop compliquer le défi pour les personnes nouvelles dans le jeu. Je pense que j'ai envisagé de faire un défi shanten plus tard après cela, mais je ne l'ai jamais fait à la fin - si vous souhaitez en poster un, je pense que ce serait un beau défi.
Sp3000

Réponses:


4

Python, 312 281 octets

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 prend une chaîne en entrée et renvoie une chaîne en sortie.

Le petit nombre de tuiles (27) le rend assez rapide pour tester si chacune d'elles complète la main. Le problème devient de vérifier si une main est valide. La fonction utilise un algorithme de retour arrière simple qui prend en compte tous les choix possibles d'ensembles et vérifie si l'un d'eux correspond à une main complète.

Les mains sont représentées sous forme d'histogrammes de tuiles, c'est-à-dire une liste de comptages de tuiles (pour toutes les tuiles, pas seulement celles présentes dans la main.) Cela permet de vérifier facilement si nous avons un certain nombre d'une certaine tuile et si nous avoir une séquence de tuiles adjacentes (le rembourrage entre les tuiles de différentes combinaisons empêche les séquences multi-combinaisons.)


Ah, vous m'avez battu: P Quoi qu'il en soit, il semble que vous pouvez l'utiliser mapà quelques endroits, comme:H(map((S+t).count,T))
FryAmTheEggman

@FryAmTheEggman Manqué ça. Merci!
Ell

@ Sp3000 C'est Python 2. C'est étrange; cela fonctionne bien pour moi sur 2.7.8.
Ell

@Ell Works en 2.7.8 - 2.7.5 n'a pas aimé le 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
)

Expliqué

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 dans la 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)))

Production

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.