9 votes

Comment puis-je calculer rapidement le nombre de shanten au Mahjong ?

Je suis capable de calculer moi-même le nombre de shanten, mais cela me prend souvent plus d'une minute. Je suis à la recherche d'idées pour devenir plus efficace.

Le contexte : Je suis actuellement en train d'écrire un article sur la détermination du nombre de shanten avec un algorithme informatique. Cela fonctionne très bien jusqu'à présent, mais il y a probablement des idées d'amélioration qui m'ont échappé. Je suis moi-même un très mauvais joueur de Mahjong, donc les contributions de joueurs expérimentés seraient les bienvenues.

Voici une description approximative de ma stratégie (manuelle) actuelle :

Tout d'abord, détectez toutes les tuiles dont l'utilisation est certaine. Cela signifie les tuiles qui sont seules (aucune tuile adjacente dans un rayon de 2) et les ensembles qui sont complétés (chi, pon).

Je compte le nombre de tuiles simples qu'il y a. Ensuite, je cherche des ensembles incomplets (dans une forme similaire à l'une des 11x, 12x, 1x3, x23). J'insère l'une des tuiles individuelles dans l'un d'entre eux et j'incrémente le nombre de shanten dans ma tête. Pendant cette étape, j'essaie de garder jusqu'à 2 paires pour plus tard.

S'il reste des tuiles individuelles ou des ensembles incomplets, je les divise et multiplie leur nombre par 2/3 (parce que n'importe quelles 3 tuiles peuvent être transformées en un ensemble en échangeant 2 d'entre elles). Je vérifie également s'il y a une ou deux paires, sinon, j'en crée une (en augmentant le shanten de 1). Jusqu'à présent, aucun problème, cela ne me prend que quelques secondes.

Le problème commence lorsqu'il existe plusieurs options pour combiner les tuiles. Dois-je diviser cet ensemble ou le conserver ? Dois-je ajouter cette tuile ici ou là ? Les mains pures (une seule couleur) sont particulièrement complexes, et je n'ai pas réussi à trouver un moyen rapide de les résoudre.

Mon algorithme se moque bien sûr des mains qui me posent problème, car il est capable de résoudre les mains les plus compliquées en quelques fractions de seconde. Il explore toutes les façons raisonnables de combiner les tuiles dans un arbre (avec des heuristiques) et donne la meilleure option.

3voto

Luke Francl Points 11707

Si tu le regardes, alors tu comptes trois choses :

  • le compte standard à partir de 8. (dans le pire des cas : 147m147p147s1234z ou kokushi à 13 faces)
  • le terminal compte à partir de 13. (dans le pire des cas : tout est simple, c'est 13-shanten)
  • les sept paires comptent à partir de 6 (pire scénario : tout ce qui n'a pas de paire).

Le comptage standard à partir de 8 permet d'identifier jusqu'à 5 groupements. Les groupes de 3 (en soustrayant 2) sont toujours meilleurs que 2 groupes de 2 (en soustrayant 1 chacun), car il y a toujours une tuile supplémentaire pour prendre un spare dans les groupes qui se chevauchent. Si #groupes >= 5, vérifiez alors si l'un des groupes est une paire. Sinon, vous n'avez que 4 groupes "légitimes". Les protogroupes de 2 sont appelés taatsu (t), les paires sont comptées séparément (p). En général, shanten = 8 - 2*g - t - p. Mon code extrait les paires avant les protogroupes lors du comptage. Sans groupes complets, le mieux que vous puissiez obtenir avec le comptage standard est 3-shanten.

Le comptage des terminaux est facile. Combien de terminaux, et combien de terminaux différents. Pour le jeu en direct, il suffit de chercher une paire et d'ignorer de les compter tous. shanten = 13 - diffTerminals() - if(numTerminals() > diffTerminals;1;0).

Le comptage des paires est également facile. shanten = 6 - p. Celui des 3 qui est le plus petit est votre compte de shanten.


Ce que fait le code, c'est qu'il peut prendre une main de taille 1, 2, 4, 5, 7, 8, 10, 11, 13 ou 14 et la décomposer. En cas d'alimentation de moins de 13 tuiles, g peut être préchargé avec le nombre de fusions formées. AFAIK, il ne comptera pas pour les protogroupes auto-parois si vous lui donnez 45p55777z avec 2 mélanges, et qu'ils se trouvent être deux kans de 3333p et 6666p, il n'enregistrera pas que la main est 1-shanten au lieu de tenpai... et de même avec 4578p si les 3 mélanges sont 3333p6666p9999p, alors la main est 2-shanten, pas 1 ou 0. C'est le plus délicat des cas limites cependant.

Ce qu'il fait cependant, c'est que s'il finit en tenpai, il essaiera d'ajouter toutes les tuiles pour obtenir une main complète (-1). Cependant, il ne peut pas ajouter une tuile déjà présente. Ainsi, même si l'une des vérifications intermédiaires indique que tout va bien, lorsqu'une main complète est présente, elle ne validera pas. Une main avec 4 groupes identiques et 3 groupes non apparentés n'est, par définition, pas tenpai, car la seule façon de la "compléter" est d'ajouter une 5e copie de la même tuile, ce qui n'est pas possible.

Pour ce qui est des éléments à prendre, il passe par tout. Est-ce qu'un groupe comme 34555p pourrait être mieux sous la forme 345 + 55 ou 34 + 555, surtout si vous avez besoin d'une paire pour compléter les éléments non groupés ? C'est possible, mais le système passe quand même en revue toutes les possibilités. La ligne qui détecte les groupes de trois ne fait qu'avancer tout de deux tuiles, car il n'y a aucun intérêt à détecter si dans les tuiles ABCDEFGHIJKLM : DGH, EGH et FGH font des courses si vous savez que DEF sont les mêmes. Faites juste DEF à ce moment-là, sautez, vérifiez FGH et continuez.

Lorsque j'applique cette formule à des cycles de distribution aléatoire de tuiles, j'obtiens environ 220 mains de Tenhou par 100M, ce qui correspond aux pages de statistiques mensuelles de Tenhou.

  // INPUT: hand is a sorted array of tile values from 0-135 without repeating
  // INPUT: g is the number of groups. The function works recursively by extracting a group and calling itself with stc(smallerHand, g+1)
function stc(hand, g) {
  // INIT
  var retVal = 8; var t = 0; var p = 0;
  // KOKUSHI
  if (hand.length >= 13) retVal = Math.min(retVal, 13 - diffTerminals(hand) - Math.max(Math.min(terminalPairs(hand), 1),0));
  // SUBHAND RECURSION: Sorted hands have relevant tiles spaced 4 apart or less. Order: O{4*4*(n-5)} instead of O{(n^3 / 6}
  if (hand.length > 3) {
      for (i=0; i<hand.length-2; i++) {
      for (j=i+1; j<hand.length-1 && (j-i<5); j++) {
      for (k=j+1; k<hand.length && (k-j<5); k++) {
          // Max subhands:: 14 tiles: 6635520; 13 tiles: 1351680.
          if (isGroup(hand[i], hand[j], hand[k])) {
              var subHand = [];
              for (var m in hand) {subHand[m] = hand[m];}
              subHand.splice(k,1); subHand.splice(j,1); subHand.splice(i,1);
              retVal = Math.min(retVal, this.stc(subHand, g+1));
          }
          if (isPair(hand[i],hand[j]) && isPair(hand[i],hand[k])) i++; j=999; k=999;
      }}}
  }
  // PROTOGROUP COUNT
  if (hand.length > 1) {
      // Priority: AB pair, AC pair, AB taatsu
      for (i = 0; i<hand.length-1; i++) {
          if(isPair(hand[i], hand[i+1])) {p++;i++;}
          else if(i<hand.length-2 && isPair(hand[i], hand[i+2])) {p++;i=i+2;}
          else if(isTaatsu(hand[i], hand[i+1])) {t++;i++;}
      }
  }
  // 5 to 7 pairs, immeditate return on 7
  if (p==7) return -1; if (p>4&&hand.length>=13) retVal = Math.min(retVal, 6-p); 
  // Standard count: 8 - 2*g - max(4-g,p+t) - min(1,max(0,p+t-(4-g)))  
  if(p+t>Math.floor(hand.length/3)) {
      if (p>0) retVal = Math.min(retVal, 8 - 2*g - Math.floor(hand.length/3) - 1);  
      else retVal = Math.min(retVal, 8 - 2*g - Math.floor(hand.length/3) - 0);
  } else retVal = Math.min(retVal, 8 - 2*g - p - t, 6 - p);
  // Tenpai check for shanten == 0.
  if(retVal==0 && hand.length==13) {
      for(var i=0;i<136;i++){
        var checkHand = [];
        var checkRes = 0;
        for (var m in hand) {checkHand[m] = hand[m];}
        checkHand.push(i);
        for (var n = 0; n<13;n++) {
            if (checkHand[n] == i) {n=999;}
            if (n == 12) {
              checkHand = checkHand.sort(doCompare);
              checkRes = stc(checkHand,0);
            }
        }
        retVal = Math.min(retVal, checkRes + 1); // checkRes would be -1 if legit tenpai, 0 if not. **EDIT2016: I think the +1 should be out of the parentheses**
      }
  } 
  return retVal;
}

1voto

unk1102 Points 973

Si tu prends plus d'une minute pour compter les shanten, tu penses trop fort. Le comptage de shanten peut être fait en quelques secondes en utilisant votre tête.
Regardez votre main. Pour chaque paire ou séquence incomplète, comptez 1. Pour les mélanges complets, comptez 2. Soustrayez le total de 8. Ne comptez pas les tuiles qui se chevauchent !

Exemple : 1225points 56bambous 23588caractères rouge est

12 points = 1
56bambous = 1
23caractères = 1
88caractères = 1
total = 4
shanten = 8 - 4 = 4

AlleGamers.com

AlleGamers est une communauté de gamers qui cherche à élargir la connaissance des jeux vidéo.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X