If you're eyeballing it, then you go with counting three things:
- the standard count from 8. (worst case scenario: 147m147p147s1234z or 13-sided kokushi)
- the terminal count from 13. (worst case scenario: all simples is 13-shanten)
- the seven pairs count from 6. (worst case scenario: anything without a pair)
Standard count from 8 is to identify up to 5 groupings. Groups of 3 (subtracting 2) are always better than 2 groups of 2 (subtracting 1 each), as there is always an extra tile to pick up a spare from overlapping groups. If #groupings >= 5, then verify if one of the groups is a pair. Otherwise, you only have 4 "legitimate" groups. Protogroups of 2 are called taatsu (t), pairs are counted separately (p). Generally, shanten = 8 - 2*g - t - p. My code extracts pairs before protogroups when counting. With no complete groups, the best you can achieve with the standard count is 3-shanten.
Terminal count is easy. How many terminals, and how many different terminals. For live play, just look for a pair and ignore counting them all. shanten = 13 - diffTerminals() - if(numTerminals() > diffTerminals;1;0).
Pair count is also easy. shanten = 6 - p. Whichever of the 3 is smallest is your shanten count.
Now what the code does is that it can take a hand with size 1,2,4,5,7,8,10,11,13 or 14 and just break it down. When feeding less than 13 tiles, g can be pre-loaded with the number of melds formed. AFAIK, it won't count for self-walled protogroups if you feed it 45p55777z with 2 melds, and they happen to be two kans of 3333p and 6666p, it won't register that the hand is 1-shanten instead of tenpai... and same with 4578p if the 3 melds are 3333p6666p9999p, then the hand is 2-shanten, not 1 or 0. This is the edgiest of edge cases though.
What it does do though, is if it ends up tenpai, it will test adding every tile to get a complete hand (-1). However, it cannot add a tile already there. So even if one of the intermediate checks says it's fine, when a full hand is present, it will not validate. A hand with 4 of the same and 3 unrelated groups is by definition, not tenpai, as the only way it could "complete" is with a 5th copy of the same tile, which is not possible.
As for what elements to take, it cycles through everything. Could a group like 34555p be better as 345 + 55 or 34 + 555, especially if you need a pair amoing the non-grouped items? Maybe, but it will still cycle through all possibilities. The line detecting three of a kind will just shift eveerything forward by two tiles as there's no point in detecting if in tiles ABCDEFGHIJKLM: DGH, EGH and FGH make runs if you know DEF are the same. Just do DEF at that point, jump, check FGH and move on.
When I run this formula on cycles of random tile distributions, I get in the area of 220 tenhou hands per 100M, which is in line with Tenhou's monthly running stat pages.
// 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;
}