//===================================================================== // 面子構成判定関数 // このメソッドの中で、stateクラスのメンバを変更しないこと! //===================================================================== public static State CountMentsuAndKouho(Dictionary<int, int> kind, int level, State state) { State result = state; int minShanten = state.getShanten(); for (int j = 1; j < PAI_MAX; j++) { if (!kind.ContainsKey(j)) continue; if (kind[j] >= 3) { Dictionary<int, int> tmp = new Dictionary<int, int>(kind); tmp[j] -= 3; string partition = string.Format("{0} {1} {2} /", j, j, j); State st = state.Clone(); st.mentsu += 1; st.partition += partition; // とった残りの手牌を再帰的に計算 State subResult = CountMentsuAndKouho(tmp, level + 1, st); if (subResult.getShanten() < minShanten) { minShanten = subResult.getShanten(); result = subResult; } } if (j < 29 && kind[j] >= 1 && kind[j + 1] >= 1 && kind[j + 2] >= 1) { Dictionary<int, int> tmp = new Dictionary<int, int>(kind); tmp[j] -= 1; tmp[j+1] -= 1; tmp[j+2] -= 1; string partition = string.Format("{0} {1} {2} /", j, j+1, j+2); State st = state.Clone(); st.mentsu += 1; st.partition += partition; // とった残りの手牌を再帰的に計算 State subResult = CountMentsuAndKouho(tmp, level + 1, st); if (subResult.getShanten() < minShanten) { minShanten = subResult.getShanten(); result = subResult; } } // 刻子候補があればとる if (kind[j] >= 2) { Dictionary<int, int> tmp = new Dictionary<int, int>(kind); tmp[j] -= 2; string partition = string.Format("{0} {1} /", j, j); State st = state.Clone(); st.kouho += 1; st.partition += partition; // とった残りの手牌を再帰的に計算 State subResult = CountMentsuAndKouho(tmp, level + 1, st); if (subResult.getShanten() < minShanten) { minShanten = subResult.getShanten(); result = subResult; } } // 順子候補があればとる(ペンチャンorリャンメン) if (j <= 29 && kind[j] >= 1 && kind[j + 1] >= 1) { Dictionary<int, int> tmp = new Dictionary<int, int>(kind); tmp[j] -= 1; tmp[j+1] -= 1; string partition = string.Format("{0} {1} /", j, j+1); State st = state.Clone(); st.kouho += 1; st.partition += partition; // とった残りの手牌を再帰的に計算 State subResult = CountMentsuAndKouho(tmp, level + 1, st); if (subResult.getShanten() < minShanten) { minShanten = subResult.getShanten(); result = subResult; } } // 順子候補があればとる(カンチャン) if (j <= 29 && kind[j] >= 1 && kind[j + 2] >= 1 && (j/10 == (j+2)/10)) { Dictionary<int, int> tmp = new Dictionary<int, int>(kind); tmp[j] -= 1; tmp[j + 2] -= 1; string partition = string.Format("{0} {1} /", j, j + 2); // Stateクラスインスタンスの簡易コピーを生成 State st = state.Clone(); // 面子候補数を+1 st.kouho += 1; // 手牌分割文字列を+1 st.partition += partition; // とった残りの手牌を再帰的に計算 State subResult = CountMentsuAndKouho(tmp, level + 1, st); if (subResult.getShanten() < minShanten) { minShanten = subResult.getShanten(); result = subResult; } } } return result; }
//===================================================================== // 向聴数を求めるアルゴリズム - あらの(一人)麻雀研究所 // http://mahjong.ara3.net/etc/shanten/index.htm // を実装 // // テーブルは使用していない //===================================================================== public static State ShantenCheck2(List<int> tehai) { // テーブル構成関数より手牌を構築 Dictionary<int, int> kind = GroupByKind(tehai); List<State> results = new List<State>(); foreach (int pai in kind.Keys) { int cnt = kind[pai]; // 雀頭(同じ牌が2つ)ある場合 if (cnt >= 2) { Dictionary<int, int> tmp = new Dictionary<int,int>(kind); tmp[pai] -= 2; // 雀頭判定フラグをtrueにして面子構成関数を起動 State state = new State(); state.hasAtama = true; State subResult = CountMentsuAndKouho(tmp, 0, state); results.Add(subResult); // リストresults } } // 雀頭判定フラグをfalseにして面子構成関数を起動 State state2 = new State(); state2.hasAtama = false; State subResultr2 = CountMentsuAndKouho(kind, 0, state2); results.Add(subResult2); int min_shanten = 999; State ret = null; foreach (State r in results) { if (min_shanten > r.getShanten()) { min_shanten = r.getShanten(); ret = r; } } return ret; }