Source: shanten.js

// require: mjcommon.js

/** 向聴数などを計算した結果を文字列表現にして返す… 
 * 複雑な文字列で返されるので使い道がない…
 * 関数内部でmjcommon.jsの{@link paiName34}を使用している
 * @param {number[]} pai 牌ID(0-135)のリスト(手牌相当)
 * @param {number} len
 * @returns {string}
 */
function calc(pai, len) {
    var j;
    var pais = new Array(34);
    for (j = 0; j < 34; ++j) {
        pais[j] = 0;
    }
    for (j = 0; j < len; ++j) {
        p = pai[j] >> 2;
        ++pais[p];
    }
    function mentzName(pai, type) {
        switch (type) {
        case 101:
            return paiName34(pai);
        case 201:
            return paiName34(pai) + paiName34(pai);
        case 202:
            return paiName34(pai) + paiName34(pai + 1);
        case 203:
            return paiName34(pai) + paiName34(pai + 2);
        case 301:
            return paiName34(pai) + paiName34(pai) + paiName34(pai);
        case 302:
            return paiName34(pai) + paiName34(pai + 1) + paiName34(pai + 2);
        }
        return "Er";
    }
    function TInfo() {
        var self = this;
        this.mentz = 0;
        this.toitz = 0;
        this.tartz = 0;
        this.tanki = 0;
        this.error = 0;
        this.getShanten = function () {
            if (self.error > 0) {
                return -1;
            }
            var m = (self.mentz * 3) + ((self.toitz + self.tartz) * 2) + self.tanki;
            var mc = (m - (m % 3)) / 3;
            var t = self.tartz + (self.toitz > 1 ? self.toitz - 1 : 0);
            m -= self.mentz * 3;
            if ((m % 3) == 0) {
                if (self.toitz > 0) {
                    ++t;
                }
            } else {
                if (self.toitz > 0) {
                    m -= 2;
                } else {
                    m -= 1;
                }
            }
            if (self.mentz < mc) {
                if (t > (mc - self.mentz)) {
                    m -= (mc - self.mentz) * 2;
                } else {
                    m -= t * 2 + ((mc - self.mentz) - t);
                }
            }
            return m;
        };
    }
    function MyList (p,t) {
        var self = this;
        this.pai = p;
        this.type = t;
        this.list = null;
        this.name = mentzName(p, t);
        this.next = null;
        this.clone = function () { return new MyList(self.pai, self.type); };
        this.conString = function () {
            if (self.next == null) {
                return "[" + self.name + "]";
            } else {
                return "[" + self.name + "]" + self.next.conString();
            }
        };
        this.conClone = function () {
            var c = new MyList(self.pai, self.type);
            if (self.next != null) {
                c.next = self.next.conClone();
            }
            return c;
        };
        this.append = function (a) {
            if (self.next == null) {
                self.next = a;
            } else {
                self.next.append(a);
            }
            return self;
        };
        this.getTInfo = function () {
            var info;
            if (self.next == null) {
                info = new TInfo();
            } else {
                info = self.next.getTInfo();
            }
            if (self.type > 300) {
                ++info.mentz;
            } else if (self.type == 201) {
                ++info.toitz;
            } else if (self.type > 200) {
                ++info.tartz;
            } else if (self.type > 100) {
                ++info.tanki;
            } else {
                ++info.error;
            }
            return info;
        };
    }

    function check3(pais, n, o) {
        var j, i = 0, p, ml;
        var list = new Array();
        for (j = o; j < 9; ++j) {
            p = j + n;
            if (pais[p] == 0) {
                continue;
            }
            if (pais[p] > 1) {
                list = new Array();
                list[0] = ml = new MyList(p, -1);
                ml.list = new Array();
                return list;
            }
            //--pais[p];
            list[0] = ml = new MyList(p, 101);
            ml.list = check3(pais, n, j + 1);
            //++pais[p];
            return list;
        }
        return list;
    }                

    function check2(pais, n, o) {
        var j, i = 0, p, ml, k = -1;
        var list = new Array();
        for (j = o; j < 9; ++j) {
            if ((k >= 0) && (j > k)) {
                break;
            }
            p = j + n;
            if (pais[p] == 0) {
                continue;
            }
            if ((j < 7) && (pais[p + 2] > 0)) {
                if (pais[p + 1] > 0) {
                    list = new Array();
                    list[0] = ml = new MyList(p, -302);
                    ml.list = new Array();
                    return list;
                }
                --pais[p]; --pais[p + 2];
                list[i++] = ml = new MyList(p, 203);
                ml.list = check2(pais, n, j);
                ++pais[p]; ++pais[p + 2];
                if (k < 0) {
                    k = j + 2;
                }
            }
            if ((j < 8) && (pais[p + 1] > 0)) {
                --pais[p]; --pais[p + 1];
                list[i++] = ml = new MyList(p, 202);
                ml.list = check2(pais, n, j);
                ++pais[p]; ++pais[p + 1];
                if (k < 0) {
                    k = j + 1;
                }
            }
            if (pais[p] == 2) {
                pais[p] -= 2;
                list[i++] = ml = new MyList(p, 201);
                ml.list = check2(pais, n, j + 1);
                pais[p] += 2;
                break;
            }
        }
        if (i == 0) {
            return check3(pais, n, 0);
        }
        return list;
    }
                    
    function check1(pais, n, o) {
        var j, i = 0, p, ml, k = -1;
        var list = new Array();
        for (j = o; j < 9; ++j) {
            if ((k >= 0) && (j - k > 2)) {
                break;
            }
            p = j + n;
            if (pais[p] == 0) {
                continue;
            }
            if ((j < 7) && (pais[p + 1] > 0) && (pais[p + 2] > 0)) {
                --pais[p]; --pais[p + 1]; --pais[p + 2];
                list[i++] = ml = new MyList(p, 302);
                ml.list = check1(pais, n, j);
                ++pais[p]; ++pais[p + 1]; ++pais[p + 2];
                if (k < 0) {
                    k = j;
                }
            }
            if (pais[p] > 2) {
                pais[p] -= 3;
                list[i++] = ml = new MyList(p, 301);
                ml.list = check1(pais, n, j);
                pais[p] += 3;
                break;
            }
        }
        if (i == 0) {
            return check2(pais, n, 0);
        }
        return list;
    }
    
    function check4(pais) {
        var j, i = 0, ml;
        var list = new Array();
        for (j = 27; j < 34; ++j) {
            if (pais[j] == 0) {
                continue;
            }
            switch (pais[j]) {
            case 2:
                pais[j] -= 2;
                list[i++] = ml = new MyList(j, 201);
                ml.list = check4(pais);
                pais[j] += 2;
            case 1:
                --pais[j];
                list[i++] = ml = new MyList(j, 101);
                ml.list = check4(pais);
                ++pais[j];
                break;
            case 3:
                pais[j] -= 2;
                list[i++] = ml = new MyList(j, 201);
                ml.list = check4(pais);
                pais[j] += 2;
            case 4:
                pais[j] -= 3;
                list[i++] = ml = new MyList(j, 301);
                ml.list = check4(pais);
                pais[j] += 3;
                break;
            }
            break;
        }
        return list;
    }
    
    function str1(kk) {
        var list = new Array();
        var j, i = 0, k, ls;
        for (j = 0; j < kk.length; ++j) {
            ls = str1(kk[j].list);
            if (ls.length == 0) {
                list[i++] = "[" +kk[j].name + "]";
            } else {
                for (k = 0; k < ls.length; ++k) {
                    list[i++] = "[" + kk[j].name + "]" + ls[k];
                }
            }
        }
        return list;
    }
    function con1(kk) {
        var list = new Array();
        var j, i = 0, k, ls;
        for (j = 0; j < kk.length; ++j) {
            ls = con1(kk[j].list);
            if (ls.length == 0) {
                list[i++] = kk[j].clone();
            } else {
                for (k = 0; k < ls.length; ++k) {
                    list[i] = kk[j].clone();
                    list[i++].next = ls[k];
                }
            }
        }
        return list;
    }
    
    var ss = "---- MANZ ----";
    
    var ls = con1(check1(pais, 0 ,0));
    var rs = new Array();
    var rsi = 0, mn = 99, sh = 0;
    for (j = 0; j < ls.length; ++j) {
        sh = ls[j].getTInfo().getShanten();
        ss += ls[j].conString() + "(" + sh + ")----   ----";
        if (sh < 0) {
            continue;
        }
        if (sh < mn) {
            mn = sh;
        }
    }
    if (mn == 99) {
        rs = ls;
    } else {
        for (j = 0; j < ls.length; ++j) {
            sh = ls[j].getTInfo().getShanten();
            if (sh < 0) {
                continue;
            }
            if (sh == mn) {
                rs[rsi++] = ls[j];
            }
        }
    }
    
    ss += " " + rs.length + " ----";
    
    ss += " PINZ ----";
    
    var tp = new Array();
    var tpi = 0, k;
    ls = con1(check1(pais, 9, 0));
    mn = 99;
    for (j = 0; j < ls.length; ++j) {
        sh = ls[j].getTInfo().getShanten();
        ss += ls[j].conString() + "(" + sh + ")----   ----";
        if (sh < 0) {
            continue;
        }
        if (sh < mn) {
            mn = sh;
        }
    }
    if (ls.length == 0) {
        tp = rs;
    } else if (rs.length == 0) {
        tp = ls;
    } else if (mn == 99) {
        for (j = 0; j < ls.length; ++j) {
            for (k = 0; k < rs.length; ++k) {
                tp[tpi++] = rs[k].conClone().append(ls[j]);
            }
        }
    } else {
        for (j = 0; j < ls.length; ++j) {
            sh = ls[j].getTInfo().getShanten();
            if (sh < 0) {
                continue;
            }
            if (sh == mn) {
                for (k = 0; k < rs.length; ++k) {
                    tp[tpi++] = rs[k].conClone().append(ls[j]);
                }
            }
        }
    }
    
    ss += " " + tp.length + " ----";
    ss += " SOUZ ----";
    
    rs = new Array();
    rsi = 0;
    mn = 99;
    ls = con1(check1(pais, 18, 0));
    for (j = 0; j < ls.length; ++j) {
        sh = ls[j].getTInfo().getShanten();
        ss += ls[j].conString() + "(" + sh + ")----   ----";
        if (sh < 0) {
            continue;
        }
        if (sh < mn) {
            mn = sh;
        }
    }
    if (ls.length == 0) {
        rs = tp;
    } else if (tp.length == 0) {
        rs = ls;
    } else if (mn == 99) {
        for (j = 0; j < ls.length; ++j) {
            for (k = 0; k < tp.length; ++k) {
                rs[rsi++] = tp[k].conClone().append(ls[j]);
            }
        }
    } else {
        for (j = 0; j < ls.length; ++j) {
            sh = ls[j].getTInfo().getShanten();
            if (sh < 0) {
                continue;
            }
            if (sh == mn) {
                for (k = 0; k < tp.length; ++k) {
                    rs[rsi++] = tp[k].conClone().append(ls[j]);
                }
            }
        }
    }
    
    ss += " " + rs.length + " ----";
    ss += " JIHAI ----";
    
    tp = new Array();
    tpi = 0;
    ls = con1(check4(pais));
    mn = 99;
    for (j = 0; j < ls.length; ++j) {
        sh = ls[j].getTInfo().getShanten();
        ss += ls[j].conString() + "(" + sh + ")----   ----";
        if (sh < 0) {
            continue;
        }
        if (sh < mn) {
            mn = sh;
        }
    }
    if (ls.length == 0) {
        tp = rs;
    } else if (rs.length == 0) {
        tp = ls;
    } else if (mn == 99) {
        for (j = 0; j < ls.length; ++j) {
            for (k = 0; k < rs.length; ++k) {
                tp[tpi++] = rs[k].conClone().append(ls[j]);
            }
        }
    } else {
        for (j = 0; j < ls.length; ++j) {
            sh = ls[j].getTInfo().getShanten();
            if (sh < 0) {
                continue;
            }
            if (sh == mn) {
                for (k = 0; k < rs.length; ++k) {
                    tp[tpi++] = rs[k].conClone().append(ls[j]);
                }
            }
        }
    }
    
    ss += " " + tp.length + " ----";
    ss += " ALL RESULT ----";

    mn = 99;
    for (j = 0; j < tp.length; ++j) {
        sh = tp[j].getTInfo().getShanten();
        if (sh < 0) {
            continue;
        }
        if (sh < mn) {
            mn = sh;
        }
    }
                    
    for (j = 0; j < tp.length; ++j) {
        sh = tp[j].getTInfo().getShanten();
        if (sh < 0) {
            continue;
        }
        if (sh == mn) {
            ss += tp[j].conString() + "(" + tp[j].getTInfo().getShanten() + ")----   ----";
        }
    }
    
    if (mn == 0) {
        ss += " 和了";
    } else if (mn == 1) {
        ss += " 聴牌";
    } else if (mn > 1) {
        ss += " " + (mn - 1) + " 向聴";
    }
    
    return ss;
}