再帰に思い至らなかった不覚

麻雀の待ち判定アルゴリズム
http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html
実は以前考えたことある*1
愚直な場合わけをしてたけど、再帰で書き直したらすっきりした。 OpenSourceコミュニティに貢献するという想定で英語でコメント&変数名を書いてある。コアとなる判定ルーチンは以下だけ

// Check if given hand has one pair plus (4-ntriplet) triplets
//  May call itself recursively.
//
//  input:
//  Hand hand: hand data to check
//  int start: hand has no triplets nor pair in a range 0 .. start-1
//  int pair_idx: -1 if pair is needed. When we already have a pair, pair_idx= piece id
//  int ntriplet: number of triplets we already have
bool Win(Hand hand, int start, int pair_idx,int ntriplet)
{
    int i=start;
    bool result = false;

    if(ntriplet==4 && pair_idx !=-1)
    {
	// Win!
	return true;
    }

    while(hand[i] ==0)
    { 
	// no more pieces in hand
	if (hand[i] == -1) return false;
	i++;
    }

    //fork: possible pair found
    //  hand[i]=3, 4 case is handled after 123-type is removed
    if(hand[i] ==2 && pair_idx == -1) 
    {
	hand[i]-=2;
	result |= Win(hand, i+1, i, ntriplet);
	hand[i]+=2;
    }

    //fork: possible 111-type found
    //  hand[i]=4 case is handled after 123-type is removed
    if(hand[i]==3)
    {
	hand[i]-=3;
	result |= Win(hand, i+1, pair_idx, ntriplet+1);
	hand[i]+=3;
    }

    //fork: possible 123-type found
    if(hand[i+1]>0 && hand[i+2]>0 )
    {
	hand[i]--;
	hand[i+1]--;
	hand[i+2]--;
	result |= Win(hand, i, pair_idx, ntriplet+1);
	hand[i]++;
	hand[i+1]++;
	hand[i+2]++;
    }
    return result;
}

http://d.hatena.ne.jp/ita/00010416/p1
待ちを探すというより、「1−9それぞれ一枚仮想的に持ってきて和れるか」とした方が分かりやすい。
各牌を何個持っているかの histogram bin で手を記憶しているけど、ソーズ、ピンズ、字を混ぜるときでも配列を拡張して個数を入れて、マンズとソーズ、ソーズとピンズの間に1つ空白を入れて番兵の0を入れておけば同じコードで判定できる。字牌も2つ間隔で0を入れればいいけど、こっちはIF文でシュンツの判定をoffにしたほうがいいかも。
1牌の無駄もあってはいけないので、もっと探索を減らせるかもしんない。