Source: mjcommon.js

/** n未満の乱数生成
 * 
 * @function
 * @param {number} n
 * @returns {number}
 */
var rand = (function () {
    if (MersenneTwister) {
        var _mtRand = new MersenneTwister();
        var _seed = new Array(
            (new Date()).getSeconds(), 
            Math.floor(0x7000 * Math.random()), 
            Math.floor(0x7000 * Math.random()), 
            Math.floor(0x7000 * Math.random()), 
            Math.floor(0x7000 * Math.random()));
        _mtRand.init_by_array(_seed, _seed.length);
        return function (n) {
            return _mtRand.genrand_int32() % n;
        };
    } else {
        return function (n) {
            return Math.floor(n * Math.random());
        };
    }
})();


/** 牌ID(0~135)から牌画像のURLの取得
 * 
 * @param {number} id
 * @returns {string}
 */
function getPaiImageSrc(id) {
    var p = id >> 2;
    var x = p % 9;
    var y = (p - x) / 9;
    if ((y < 3) && (x == 4) && (id % 4 == 0)) { // 赤ドラ
        x = 9;
    }
    return "img/slice_" + y + "_" + x + ".png";
}

/** 牌種番号(0~33)から牌の名前取得
 * 
 * @param {number} p
 * @param {boolean} a falseのみ (trueはバグ有)
 * @returns {string}
 */
function paiName34(p, a) {
    var x = (p % 9);
    var y = (p - x) / 9;
    if (a) {
        if ((y < 3) && (x == 4)) {
            x = 0;
        }
    }
    return (x + 1) + ['m', 'p', 's', 'z'][y];
}

/** 牌ID(0~135)から牌の名前取得
 * 
 * @param {number} id
 * @returns {string}
 */
function paiName136(id) {
    var p = i >> 2;
    var x = p % 9;
    var y = (p - x) / 9;
    if ((y < 3) && (x == 4) && (i % 4 == 0)) { // 赤ドラ
        x = -1;
    }
    return (x + 1) + ['m', 'p', 's', 'z'][y];
}

/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_TANKI = 110;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_TOITZ = 210;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_RTARZ = 220;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_PTARZ = 221;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_KTARZ = 230;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_ANKO  = 310;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_PON1  = 311;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_PON2  = 312;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_PON3  = 313;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_SHUNZ = 320;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_CHIIS = 321;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_CHIIM = 322;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_CHIIL = 323;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_AKAN  = 410;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_KKAN1 = 411;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_KKAN2 = 412;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_KKAN3 = 413;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_MKAN1 = 421;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_MKAN2 = 422;
/**
 * 
 * @constant {number}
 * @default
 */
var MITYPE_MKAN3 = 423;

/** 牌の組(MentzInfo)を文字列表現する
 * 
 * @param {MentzInfo} mi
 * @returns {string}
 */
function MentzInfoToString(mi) {
    var str = "";
    var p = mi.pai;
    var a = mi.aka;
    var f = function (p0) {
        return paiName34(p0, a);
    };
    var fp = f(p);
    switch (mi.type) {
    case MITYPE_TANKI: str = fp; break;
    case MITYPE_TOITZ: str = fp + fp; break;
    case MITYPE_RTARZ: 
    case MITYPE_PTARZ: str = fp + f(p + 1); break;
    case MITYPE_KTARZ: str = fp + f(p + 2); break;
    case MITYPE_ANKO:  str = fp + fp + fp; break;
    case MITYPE_PON1:  str = fp + fp + '(' + fp + ')'; break;
    case MITYPE_PON2:  str = fp + '(' + fp + ')' + fp; break;
    case MITYPE_PON3:  str = '(' + fp + ')' + fp + fp; break;
    case MITYPE_SHUNZ: str = fp + f(p + 1) + f(p + 2); break;
    case MITYPE_CHIIS: str = '(' + fp + ')' + f(p + 1) + f(p + 2); break;
    case MITYPE_CHIIM: str = '(' + f(p + 1) + ')' + fp + f(p + 2); break;
    case MITYPE_CHIIL: str = '(' + f(p + 2) + ')' + fp + f(p + 1); break;
    case MITYPE_AKAN:  str = fp + fp + fp + fp; break;
    case MITYPE_KKAN1: str = fp + fp + '(' + fp + fp + ')'; break;
    case MITYPE_KKAN2: str = fp + '(' + fp + fp + ')' + fp; break;
    case MITYPE_KKAN3: str = '(' + fp + fp + ')' + fp + fp; break;
    case MITYPE_MKAN1: str = fp + fp + fp + '(' + fp + ')'; break;
    case MITYPE_MKAN2: str = fp + '(' + fp + ')' + fp + fp; break;
    case MITYPE_MKAN3: str = '(' + fp + ')' + fp + fp + fp; break;
    default: str = "Err"; break;
    }
    return "[" + str + "]";
}

/** 1枚~4枚の牌の組(面子や塔子など)を表す
 * 
 * @class
 * @param {number} p 面子を構成する牌のうち最小の牌ID(0-135)
 * @param {number} t MITYPE_*の定数
 */
function MentzInfo(p, t) {
    /**  面子を構成する牌のうち最小の牌ID(0-135) 
     * @type {number}
     */
    this.pai = p;
    /**  MITYPE_*の定数  
     * @type {number}
     */
    this.type = t;
    /** trueなら赤ドラを含む構成
     * @type {Boolean}
     */
    this.aka = false;
}

/** 牌の組(MentzInfo)をリスト構造で保持する
 * 
 * @class
 * @param {MentzInfo} mi
 */
function MIList(mi) {
    /** @type {MentzInfo} */
    this.info = mi;
    /** @type {(MIList|null)} */
    this.next = null;
}

/** 手牌を表現したかったらしい
 * 
 * @class
 */
function Tehai() {
    var _pais = new Array(14);
    var _count = 0;
    /** 牌を加える
     * @function
     * @param {number} id 牌ID(0-135)
     * @returns {boolean}
     */    
    this.append = function (id) {
        if (_count < 14) {
            _pais[_count++] = id;
            return true;
        } else {
            return false;
        }
    };
    /** 牌を取り替える
     * @function
     * @param {number} oid
     * @param {number} iid
     * @returns {boolean}
     */    
    this.replace = function (oid, iid) {
        var i;
        for (i = 0; i < _count; ++i) {
            if (_pais[i] == oid) {
                _pais[i] = iid;
                return true;
            }
        }
        return false;
    };
    /** 牌を取り除く
     * @function
     * @param {number} id
     * @returns {boolean}
     */    
    this.remove = function (id) {
        var i, j;
        for (i = 0; i < _count; ++i) {
            if (_pais[i] == id) {
                --_count;
                for (j = i; j < _count; ++j) {
                    _pais[j] = _pais[j + 1];
                }
                return true;
            }
        }
        return false;
    };
    /** 牌を種別(0-34)に数えた結果の配列を返す
     * @function
     * @returns {number[]}
     */    
    this.getHist = function () {
        var hist = new Array(34);
        var i;
        for (i = 0; i < 34; ++i) {
            hist[i] = 0;
        }
        for (i = 0; i < _count; ++i) {
            ++hist[_pais[i] >> 2];
        }
        return hist;
    };
    /** 保有してる手牌を辿れるようにするつもりだったらしいが、これは実際は何も機能しない
     * @type {PaiIterator}
     */    
    this.iterator = (function (pais, count) {
        var idx = 0;
        /**
         * 
         * @class PaiIterator
         * @hideconstructor
         */
        return new function () {
            /**
             * @function PaiIterator#hasNext
             * @return {boolean}
             */
            this.hasNext = function () {
                return idx < count;
            };
            /**
             * @function PaiIterator#next
             * @returns {(number|null)}
             */
            this.next = function () {
                if (idx < count) {
                    return pais[idx++];
                } else {
                    return null;
                }
            };
            /**
             * @function PaiIterator#index
             * @returns {number}
             */
            this.index = function () {
                return idx;
            };
        };
    })(_pais, _count);
}