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;
}