//=============================================================================
// 第1版 - 初版
// 第2版 - 食い平和形が30符になるように修訂
// 第3版 - 対局中ボイスに対応
// 第4版 - 三麻の赤ドラ指定の記述を修訂
//           国士無双が役満未満に設定されている場合、または青天井ルールにおいて 
//           国士聴牌からの暗槓に対する槍槓で槍槓の役が付くように修訂
// 第5版 - CPUの刷新に伴いCPU呼び出し項目を更新
//           向聴数を求める関数を追加
// 第8版 - 積み込み予約にて 赤ドラが指定できない問題を修訂
//           和了点数を求める関数の処理を変更
// 第9版 - CPU打牌の例外処理の変更
//           河、及び副露の表示処理を変更
// 第9版2刷 - ドラ牌の強調表示処理を変更
// 第9版3刷 - 流局時ドラの強調表示を非表示にするよう変更
//            立直宣言牌が副露されたときの表示がずれる問題を修訂
//            副露後聴牌時 プレイヤーの待ち牌ウィンドウが表示されない不具合修訂
// 第12版 - 黒一色、及び三色同刻の役判定の不具合を修訂
//           CPU呼び出し時に エラー回避を行うように変更
//           一部メソッドの誤字修訂
// 
// 
//=============================================================================

/*:
 * @target MZ
 * @plugindesc 麻雀 (オブジェクト)
 * @author 
 * @base AP1S_MajiangCore
 * @orderAfter AP1S_MajiangCore
 *
 * @help
 * 
 * ミニゲーム 麻雀
 * 
 * 詳細な使用方法は 同梱の説明書をご覧ください
 * 
 */







function Game_MajiangPaishan()
{
	this.initialize(...arguments);
}

function Game_MajiangBattler ()
{
	this.initialize(...arguments);
}

function Game_MajiangBattlers ()
{
	this.initialize(...arguments);
}

function Game_MajiangPai(id)
{
	this.initialize(...arguments);
};



(function() 
{
	"use strict";
	
	Game_MajiangPaishan.prototype.initialize = function()
	{
		this._data = [];
		
		this.clear();
		
		this._parseHele = new ParseHele();
		this._pointResult = new PointResult();
		this._countXiangting = new CountXiangting();
		
		this.createPai();
	};
	
	Game_MajiangPaishan.prototype.clear = function()
	{
		this._paishan = [];
		
		this._echoDora = [];
		this._echoUradora = [];
		this._dora = [];
		this._uradora = [];
		
		this._peipai = [[],[],[],[]];
		
		this._stack = null;
		
		this._reserved = [];
		
		this._echoDoraIndex = 0;
	};
	
	Game_MajiangPaishan.prototype.unusedPaicode = function()
	{
		let ret = [];
		if(MajiangManager.samma())
		{
			ret = ret.concat([2,3,4,5,6,7,8]);
		}
		const configData = $gameMajiangSystem.config.rsPai;
		if(configData)
		{
			ret = ret.concat(configData);
		}
		
		return ret;
	};
	Game_MajiangPaishan.prototype.setUsepai = function()
	{
		const unused = this.unusedPaicode();
		this._usePaicode = $dataMajiangSystem.allPaicode.filter(code => !unused.includes(code));
	};
	Game_MajiangPaishan.prototype.checkExist = function(code)
	{
		return this.allPaicode().includes(code);
	};
	
	Game_MajiangPaishan.prototype.allPaicode = function()
	{
		return this._usePaicode;
	};
	
	
	Game_MajiangPaishan.prototype.createPai = function()
	{
		this.setUsepai();
		$dataMajiangSystem.paiData.forEach(function (pai , i)
		{
			if(pai && this.checkExist(pai.code))
			{
				this._data.push(new Game_MajiangPai(pai.id));
			}
		},this);
		
		if($gameMajiangSystem.gamerule.akadora)
		{
			this.setAkadora();
		}
	};
	
	
	
	Game_MajiangPaishan.prototype.setAkadora = function()
	{
		const samma = MajiangManager.samma();
		
		const codeTable = (samma) ? [25,15] : [25,5,15];
		const pieces = (samma) ? $gameMajiangSystem.settings.akadoraPiecesSamma : $gameMajiangSystem.settings.akadoraPieces;
		for(let i = 0; i < pieces ; i++)
		{
			const code = codeTable[i % codeTable.length];
			const pai = this._data.find(p => (p.code() === code && !p.isAkadora()));
			if(!pai) continue;
			pai.setAkadora();
		};
	};
	
	Game_MajiangPaishan.prototype.allData = function()
	{
		return this._data;
	};
	
	Game_MajiangPaishan.prototype.selectPai = function(code,akadora)
	{
		const value = code % 10;
		
		if((value === 5 && akadora) || value === 0)
		{
			if(value === 0) code = code - 5;
			return this._data.find(p => p.code() === code && p.isAkadora());
		}
		else
		{
			return this._data.find(p => p.code() === code);
		}
	};
	
	Game_MajiangPaishan.prototype.xipai = function()
	{
		this._paishan = [];
		let temp = [...this._data];
		while(temp.length > 0)
		{
			const length = temp.length;
			const rand = Math.randomInt(length);
			const pai = temp.splice(rand,1)[0];
			this._paishan.push(pai);
		}
	};
	
	
	Game_MajiangPaishan.prototype.loadStack = function()
	{
		this._stack = $gameMajiangSystem.loadStack();
		if(this.stackOk())
		{
			this.setReserved();
		}
	};
	
	Game_MajiangPaishan.prototype.stackOk = function()
	{
		return !!this._stack;
	};
	
	
	
	
	
	
	
	Game_MajiangPaishan.prototype.qipai = function()
	{
		this.clear();
		this.xipai();
		this.loadStack();
		
		
		
		this._peipai = [];
		for(let i = 0; i < 4; i++)
		{
			if(!MajiangManager.checkPosition(i))
			{
				continue;
			}
			if(this.stackOk())
			{
				this._peipai[i] = this.selectZimoPaizi(this._stack.peipai[$gameMajiangSystem.gpPosition(i)]);
			}
			else
			{
				this._peipai[i] = this._paishan.splice(0,13);
			}
		}
		
		this.setDealerFirstZimo();
		this.setWangpai();
		this.addExDora();
		this.setAkadoraEffect();
	};
	
	Game_MajiangPaishan.prototype.setDealerFirstZimo = function()
	{
		const dealerPosition = MajiangManager.dealerPosition();
		
		this._peipai[dealerPosition].push(this.zimo(dealerPosition));
		
	};
	
	Game_MajiangPaishan.prototype.setWangpai = function()
	{
		if($gameMajiangSystem.gamerule.dora)
		{
			if(this.stackOk())
			{
				this._echoDora = this.selectZimoPaizi(this._stack.echoDora);
				this._echoUradora = this.selectZimoPaizi(this._stack.echoUradora);
			}
			else
			{
				this._echoDora = this._paishan.splice(0,5);
				this._echoUradora = this._paishan.splice(0,5);
			}
			this._echoDora.forEach(p => p.setHidden(true));
			this._echoUradora.forEach(p => p.setHidden(true));
		}
		
	};
	
	//ドラを一個めくる
	Game_MajiangPaishan.prototype.nextDora = function()
	{
		if(!$gameMajiangSystem.gamerule.dora) return ;
		
		const index = this._echoDoraIndex;
		
		const echoDora = this._echoDora[index];
		const echoUradora = this._echoUradora[index];
		if(!echoDora)  return ;
		
		echoDora.setHidden(false);
		echoUradora.setHidden(false)
		
		const xianwu = this.findNextPai(echoDora);
		const xianwuUra = this.findNextPai(echoUradora);
		
		for(const pai of xianwu)
		{
			pai.addDora();
		}
		for(const pai of xianwuUra)
		{
			pai.addUradora();
		}
		
		this._dora.push(xianwu[0]);
		this._uradora.push(xianwuUra[0]);
		
		this._echoDoraIndex++;
	};
	
	Game_MajiangPaishan.prototype.selectZimoPaizi = function(codeArray)
	{
		return codeArray.map(code => this.selectZimo(code),this);
	};
	
	
	Game_MajiangPaishan.prototype.nextPaiCode = function(code)
	{
		
		const type = Math.floor(code / 10);
		const value = code % 10;
		const isSanyuan = (code >= 35);
		const maxNum = (type !== 3) ? 9 : ((isSanyuan) ? 7 : 4);
		const minNum = (isSanyuan) ? 5 : 1;
		let next = value + 1;
		for(let i = 0 ; i < 10; i++)
		{
			if(next > maxNum)
			{
				next = minNum;
			}
			const nextCode = (type * 10) + next;
			if(this.checkExist(nextCode))
			{
				return nextCode;
			}
			next++;
		}
		return code;
	};
	
	Game_MajiangPaishan.prototype.setAkadoraEffect = function()
	{
		this.allData().forEach(p =>
		{
			if(p.isAkadora())
			{
				p.setDoraEffect(true);
			}
		});
	};
	
	Game_MajiangPaishan.prototype.addExDora = function()
	{
		if($gameMajiangSystem.config.addExDora)
		{
			this.allData().forEach(p =>
			{
				if($gameMajiangSystem.config.addExDora.includes(p.code()))
				{
					p.addDora();
				}
			});
		}
	};
	
	
	
	Game_MajiangPaishan.prototype.findNextPai = function(pai)
	{
		const code = pai.code();
		const next = this.nextPaiCode(code);
		
		return this._data.filter(p => p.code() === next);
	};
	
	Game_MajiangPaishan.prototype.peipai = function()
	{
		return this._peipai;
	};
	
	Game_MajiangPaishan.prototype.zimo = function(position)
	{
		if(this.stackOk())
		{
			const code = this.stackZimoCode(position);
			return this.selectZimo(code);
		}
		return this.executeZimo();
	};
	Game_MajiangPaishan.prototype.stackZimoCode = function(position)
	{
		return this._stack.zimo[$gameMajiangSystem.gpPosition(position)].shift() || 0;
	};
	
	Game_MajiangPaishan.prototype.setReserved = function ()
	{
		this._reserved = [];
		const tempPaishan = [...this._paishan];
		for(const code of this._stack.reserve)
		{
			const index = this.findPaishanIndex(code,tempPaishan);
			if(index !== -1)
			{
				const pai = tempPaishan.splice(index,1)[0];
				
				this._reserved.push(pai);
			}
		}
	};
	
	Game_MajiangPaishan.prototype.lingshangZimo = function(position)
	{
		let retpai;
		if(this.stackOk())
		{
			const code = this.stackZimoCode(position);
			retpai = this.selectZimo(code);
		}
		else
		{
			retpai = this.executeLingshangZimo();
		}
		
		retpai.setLingshangPai(true);
		
		return retpai;
	};
	
	Game_MajiangPaishan.prototype.executeZimo = function()
	{
		
		if(this.stackOk() && this._reserved.includes(this._paishan[0]))
		{
			const nonReservedPai = this.findNonReserved();
			if(nonReservedPai)
			{
				return nonReservedPai;
			}
		}
		return this._paishan.shift();
	};
	
	Game_MajiangPaishan.prototype.executeLingshangZimo = function()
	{
		const lastIndex = this._paishan.length - 1;
		
		if(this.stackOk() && this._reserved.includes(this._paishan[lastIndex]))
		{
			const nonReservedPai = this.findNonReserved();
			if(nonReservedPai)
			{
				return nonReservedPai;
			}
		}
		return this._paishan.splice(lastIndex)[0];
	};
	
	Game_MajiangPaishan.prototype.findNonReserved = function()
	{
		const reserved = this._reserved;
		const index = this._paishan.findIndex(p => !reserved.includes(p) );
		if(index !== - 1)
		{
			return this._paishan.splice(index,1)[0];
		}
		else
		{
			return null;
		}
	};
	
	Game_MajiangPaishan.prototype.findPaishanIndex = function(code,paishan)
	{
		let index = -1;
		if ([10,20,30].includes(code))
		{
			code = code - 5;
			index = paishan.findIndex(p => p.code() == code && p.isAkadora());
		}
		else if ([5,15,25].includes(code))
		{
			index = paishan.findIndex(p => p.code() == code && !p.isAkadora());
		}
		if(index === -1)
		{
			index = paishan.findIndex(p => p.code() == code);
		}
		return index;
		
	};
	
	Game_MajiangPaishan.prototype.selectZimo = function(code,lingshang)
	{
		const index = this.findPaishanIndex(code,this._paishan);
		
		if(index !== -1)
		{
			if(this.stackOk())
			{
				this._reserved = this._reserved.filter(p => p !== this._paishan[index]);
			}
			return this._paishan.splice(index,1)[0];
		}
		else
		{
			return (lingshang) ? this.executeLingshangZimo() : this.executeZimo();
		}
	};
	
	
	Game_MajiangPaishan.prototype.clearStatus = function()
	{
		this._data.forEach(pai =>
		{
			pai.clear();
		})
	};
	
	Game_MajiangPaishan.prototype.makeDummyPai = function(code,akadora)
	{
		let value = code % 10;
		
		if(value === 0)
		{
			code -= 5;
			akadora = true;
		}
		
		const data = $dataMajiangSystem.paiData.find(pai => 
			pai && pai.code === code);
		const id = data.id;
		
		const pai = new Game_MajiangPai(id,true);
		if(akadora && value === 5)
		{
			pai.setAkadora();
		}
		return pai;
	};
	
	
	Game_MajiangPaishan.prototype.makeDummyPaiById = function(id)
	{
		const gp = this._data.find(p => p.paiId() === id);
		const akadora = gp.isAkadora();
		const pai = new Game_MajiangPai(id,true);
		if(akadora)
		{
			pai.setAkadora();
		}
		return pai;
	};
	
	
	Game_MajiangPaishan.prototype.makeDummyPaiAll = function()
	{
		const paizi = [];
		for(const code of $dataMajiangSystem.allPaicode)
		{
			const pai = this.makeDummyPai(code,false);
			paizi.push(pai);
		}
		return paizi;
		
	};
	
	Game_MajiangPaishan.prototype.echoDora = function()
	{
		return this._echoDora;
	};
	
	Game_MajiangPaishan.prototype.echoUradora = function()
	{
		return this._echoUradora;
	};
	
	Game_MajiangPaishan.prototype.shengyu = function()
	{
		return (this._paishan.length - 4) - this.additionalShengyu();
	};
	
	Game_MajiangPaishan.prototype.additionalShengyu = function()
	{
		if(MajiangManager.duo())
		{
			return $gameMajiangSystem.duoSettings.leave;
		}
		return 0;
	};
	
	
	
	Game_MajiangPaishan.prototype.checkHelexing = function(tehai)
	{
		this._parseHele.setTehai(tehai);
		return this._parseHele.helexings();
	};
	
	Game_MajiangPaishan.prototype.makeMianzi = function(paizi,kind)
	{
		return new Mianzi(paizi,kind);
	};
	
	Game_MajiangPaishan.prototype.countXiangting = function(codeArray,option)
	{
		return this._countXiangting.count(codeArray,option);
	}
	
	Game_MajiangPaishan.prototype.pointResult = function(helexings,fulu,allTehai,battler,isRong)
	{
		this._pointResult.setup(helexings,fulu,allTehai,battler,isRong)
		return this._pointResult.result();
	};
	
	Game_MajiangPaishan.prototype.dora = function()
	{
		return this._dora.map(p => this.makeDummyPai(p.code()),this);
	};
	
	Game_MajiangPaishan.prototype.uradora = function()
	{
		return this._uradora.map(p => this.makeDummyPai(p.code()),this);
	};
	
	Game_MajiangPaishan.prototype.requestResetSelectionEffect = function()
	{
		return this._data.forEach(p => p.setRequestResetSelectionEffect(true))
	};
	
	Game_MajiangPaishan.prototype.echoDoraPaizi = function()
	{
		return this._echoDora.slice(0 , this._echoDoraIndex);
	};
	
	Game_MajiangPaishan.prototype.maxPaiNum = function(code)
	{
		return (this.checkExist(code)) ? 4 : 0;
	};
	
	
	
	
	
	
	
	
	
	//------------------
	
	
	Game_MajiangBattler.prototype.initialize = function(actorId)
	{
		this._actorId = actorId;
		this._dataId = $dataMajiangBattlers.findIndex(b => b.actorId === actorId);
		this._dataActorId = actorId;
		if(this._dataId === -1)
		{
			this._dataActorId = 0;
			this._dataId = $dataMajiangBattlers.findIndex(b => b.actorId === 0);
		}
		
		this.clear();
		
	};
	
	Game_MajiangBattler.prototype.battlerData = function()
	{
		return $dataMajiangBattlers[this._dataId];
	};
	//完全初期化
	Game_MajiangBattler.prototype.clear = function()
	{
		
		this._point = {
			"prev" : 0 ,
			"add" : 0 ,
			"current" : 0
			} 
		
		
		
		this._position = 0;
		this._qijia = false;
		
		
		this._menfeng = 1; // 自風 1→ 東 2→ 南 ...
		
		this._isPlayer = false;
		this._lianHele = 0;
		this._yakitori = true;
		
		this.clearYiju();
	};
	//一局初期化
	Game_MajiangBattler.prototype.clearYiju = function()
	{
		this._yishun = true; //一巡目 ダブル立直、天和、地和判定用
		this._nagashi = true; //流し満貫判定用
		this._tehai = [];
		this._fulu = [];
		this._he = [];
		this._realHe = [];
		this._rongpai = null;
		
		this._lizhi = false;
		this._doubleLizhi = false;
		
		this._yifa = false;
		this._furiten = false;
		this._permanentFuriten = false;
		
		this._tingpaiMachi = [];
		this._canTingpaiResult = [];
		this._heleResult = null;
		this._hele = false;
		this._isRong = false;
		this._isTianhe = false;
		
		this._dapaiCount = 0;
		this._actual = [];
		this._tempActual = [];
		this._suji = [];
		this._visiblePaizi = [];
		this._visiblePaiziAll = [];
	};
	
	Game_MajiangBattler.prototype.isDealer = function()
	{
		return (this._menfeng === 1);
	};
	
	Game_MajiangBattler.prototype.position = function()
	{
		return this._postion;
	};
	
	Game_MajiangBattler.prototype.setPosition = function(position)
	{
		this._postion = position;
	};
	
	Game_MajiangBattler.prototype.gpPosition = function()
	{
		return $gameMajiangSystem.gpPosition(this.position());
	};
	
	Game_MajiangBattler.prototype.setQijia = function()
	{
		this._qijia = true;
	};
	
	Game_MajiangBattler.prototype.isQijia = function()
	{
		return this._qijia;
	};
	
	Game_MajiangBattler.prototype.setHeleTehai = function()
	{
		if(this._rongpai)
		{
			this._rongpai.setHidden(false);
			this._rongpai.clearPosition();
			this.pushTehai(this._rongpai);
		}
		this.tehai().forEach(p => {
			p.setTajiaHidden(false); 
			p.clearLizhiPosition()
		});
		this.allPaizi().forEach(p => p.setDoraEffect(false));
		
		if(!this.isTianhe())
		{
			const helepai = this.tehai().find(p => p.isHelePai());
			helepai.setNeedSpacing(true);
		}
		
	};
	
	Game_MajiangBattler.prototype.setTianhe = function(status)
	{
		this._isTianhe = status;
	};
	
	Game_MajiangBattler.prototype.isTianhe = function(status)
	{
		return this._isTianhe;
	};
	
	Game_MajiangBattler.prototype.needCPU = function()
	{
		return (!this.operatePlayer() || this.echoConsole());
	};
	Game_MajiangBattler.prototype.echoConsole = function()
	{
		const echoConsole = $dataMajiangSystem.temp.echoConsole;
		if(!echoConsole)
		{
			return false;
		}
		else if(echoConsole.includes(4))
		{
			return true;
		}
		return echoConsole.includes(this.position());
	};
	
	Game_MajiangBattler.prototype.callCPU = function(method)
	{
		if(this.needCPU())
		{
			this.CPU()[method]?.(this.position());
		}
	};
	
	
	
	
	
	Game_MajiangBattler.prototype.actor = function()
	{
		return $gameMajiangSystem.actorData(this._actorId);
	};
	
	Game_MajiangBattler.prototype.name = function()
	{
		return this.actor().name();
	};
	
	Game_MajiangBattler.prototype.actorId = function()
	{
		return this._actorId ;
	};
	Game_MajiangBattler.prototype.dataActorId = function()
	{
		return this._dataActorId ;
	};
	
	Game_MajiangBattler.prototype.setPlayer = function(status)
	{
		this._isPlayer = status;
	};
	
	Game_MajiangBattler.prototype.isPlayer = function()
	{
		return this._isPlayer;
	};
	
	Game_MajiangBattler.prototype.operatePlayer = function()
	{
		return (this.isPlayer() && !$dataMajiangSystem.temp.entrust);
	};
	
	Game_MajiangBattler.prototype.setTehai = function(tehai)
	{
		
		this._tehai = tehai || [];
	};
	
	Game_MajiangBattler.prototype.pushTehai = function(pai)
	{
		if(!this.isPlayer())
		{
			pai.setTajiaHidden(true);
		}
		this._tehai.push(pai)
	};
	
	
	Game_MajiangBattler.prototype.zimo = function(lingshang)
	{
		this.tehaiLipai();
		this.tehai().forEach(p => p.setZimoPai(false));
		const position = this.position();
		const pai = (lingshang) ? $gameMajiangPaishan.lingshangZimo(position) : $gameMajiangPaishan.zimo(position);
		this.executeZimo(pai);
	};
	
	Game_MajiangBattler.prototype.executeZimo = function(pai)
	{
		this.tehai().forEach(p => p.setNeedSpacing(false));
		pai.setZimoPai(true);
		
		pai.setNeedSpacing(true);
		if(!this.isPlayer())
		{
			pai.setTajiaHidden(true);
		}
		this._tehai.push(pai);
	};
	
	Game_MajiangBattler.prototype.clearZimoStatus = function()
	{
		this.allPaizi().forEach(p => {
			p.setLingshangPai(false);
			p.setZimoPai(false);
			p.setNeedSpacing(false);
		});
	};
	
	Game_MajiangBattler.prototype.dapai = function(pai,lizhi)
	{
		const index = this._tehai.indexOf(pai)
   		if(index != -1)
   		{	
   			pai.setZimoPai(false);
			pai.setNeedSpacing(false);
			pai.setLingshangPai(false);
   			const invisiblePai = $gameMajiangPaishan.makeDummyPaiById(pai.paiId());
    		
    		invisiblePai.setVisible(false);
    		this._tehai[index] = invisiblePai;
    		pai.setTajiaHidden(false);
    		this.addHe(pai);
    		this.addActual(pai);
    		
    		this.setTingpaiMachi();
    		
    		this.setYishun(false);
    		this.setYifa(false);
    		
    		
    		this._dapaiCount++;
    		
    		this.setLizhiDeclaration(pai,lizhi);
    		
	    	if(!pai.isYaojiu())
	    	{
	    		this._nagashi = false;
		    }
		    
    		this.setZijiaHeFuriten(lizhi);
    		this.clearSelectRestrict();
    		return pai;
   		}
	};
	
	Game_MajiangBattler.prototype.setLizhiDeclaration = function(dapai,lizhiDeclaration)
	{
		if(lizhiDeclaration)
    	{
    		dapai.setLizhi(true);
	    }
	    else if (this.isLizhi())
	    {
	    	if(!this.realHe().some(p => p.isLizhiPosition()))
	    	{
	    		dapai.setLizhi();
		    }
	    }
	};
	
	
	Game_MajiangBattler.prototype.setTingpaiMachi = function()
	{
		this._tingpaiMachi = this.checkTingpai(this.tehai().filter(p => !p.isDummy()));
	};
	
	Game_MajiangBattler.prototype.modaEnd = function()
	{
		this.clearZimoStatus();
		
		this._tehai = this._tehai.filter(p => !p.isDummy());
		this.tehaiLipai();
	};
	
	Game_MajiangBattler.prototype.setDapaiFuriten = function(dapaiCode,qianggang,lizhi)
	{
		if(qianggang && !this.checkQinaggangAdopt())
		{
			return ;
		}
		if(qianggang === "angang"
			&& !this.tingpaiGuoshi()
			&& this.canGuoshiQiangAngang()
		)
		{
			return ;
		}
		this.addOverlook(dapaiCode);
		
		if(this._tingpaiMachi.some(m => m.machi === dapaiCode))
		{
			this.setFuriten(true);
			if(this.isLizhi() || lizhi)
			{
				this.setPermanentFuriten();
			}
		}
	};
	
	Game_MajiangBattler.prototype.setZijiaHeFuriten = function(lizhi)
	{
		for(const pai of this.he())
		{
			this.setDapaiFuriten(pai.code(),false,lizhi);
			if(this._lizhi) break;
		}
	};
	
	
	
	Game_MajiangBattler.prototype.makeCanQianggangResult = function(jiagangpai,dapaijia,isAngang)
	{
		
		let canFulu = [];
		if(this.checkQinaggangAdopt()
			&& (!isAngang
				|| (this.canGuoshiQiangAngang() && this.tingpaiGuoshi())
			)
		)
		{
			canFulu = canFulu.concat(this.canRongResult(jiagangpai));
		}
		this._fuluActions = canFulu;
	};
	
	Game_MajiangBattler.prototype.checkQinaggangAdopt = function()
	{
		if(!$gameMajiangSystem.yakuData.qianggang.adopt)
		{
			return $gameMajiangSystem.settings.adoptQianggang;
		}
		return true;
	};
	
	Game_MajiangBattler.prototype.canGuoshiQiangAngang = function()
	{
		return !$gameMajiangSystem.config.rsGuoshiAngang;
	};
	
	Game_MajiangBattler.prototype.tingpaiGuoshi = function()
	{
		return this.tingpaiMachi().some(r => r.helexings.some(h => h.type === "guoshiwushuang"));
	};
	
	Game_MajiangBattler.prototype.makeCanFuluResult = function(dapai,dapaijia)
	{
		let canFulu = [];
		canFulu.push(...this.canRongResult(dapai));
		if(!this.isLizhi() && this.canFulu())
		{
			canFulu.push(...this.canFuluResult(dapai,dapaijia));
		}
		this._fuluActions = canFulu;
	};
	
	Game_MajiangBattler.prototype.canRongResult = function(dapai)
	{
		const ret = [];
		
		if(!this.isFuriten() && this.canHele(dapai) && this.canRongHele())
		{
			ret.push({"kind" : "rong", "dapai" : dapai ,"paizi" : []});
		}
		return ret;
	};
	
	Game_MajiangBattler.prototype.canRongHele = function()
	{
		return !$gameMajiangSystem.config.rsRong;
	};
	
	Game_MajiangBattler.prototype.canFuluActions = function()
	{
		return this._fuluActions;
	};
	
	Game_MajiangBattler.prototype.canFulu = function()
	{
		if(MajiangManager.duo() && !$gameMajiangSystem.duoSettings.canFulu)
		{
			return false;
		}
		return ($gameMajiangPaishan.shengyu() > 0 
			&& !MajiangManager.sikaigang()
			&& !$gameMajiangSystem.config.rsFulu
		);
	};
	
	
	
	
	
	
	//他家の捨て牌 dapai で副露ができる組み合わせを出力
	Game_MajiangBattler.prototype.canFuluResult = function(dapai,dapaijia)
	{
		let ret = [];
		ret = ret.concat(this.makeCanPengGangResult(dapai));
		ret = ret.concat(this.makeCanChiResult(dapai,dapaijia));
		return ret;
	};
	
	Game_MajiangBattler.prototype.makeCanPengGangResult = function(dapai)
	{
		const dapaiCode = dapai.code();
		const findPeng = this._tehai.filter(p => p.code() === dapaiCode);
		const ret = [];
		
		if(findPeng.length === 3 && this.canGang())
		{
			ret.push({"kind" : "daminggang", "dapai" : dapai , "paizi" : findPeng});
		}
		if(findPeng.length >= 2)
		{
			ret.push({"kind" : "peng", "dapai" : dapai , "paizi" : [findPeng[0],findPeng[1]]});
		}
		return ret;
	};
	
	Game_MajiangBattler.prototype.canChi = function(dapaijia)
	{
		if(MajiangManager.samma()
			 || (MajiangManager.duo() && !$gameMajiangSystem.duoSettings.canChi)
		)
		{
			return false;
		}
		
		if($gameMajiangSystem.config.rsChi)
		{
			return false;
		}
		
		if(this.relativePosition(dapaijia) !== "kamijia"
			&& !$gameMajiangSystem.config.everyChi
			&& !MajiangManager.duo()
		)
		{
			return false;
		}
		return true;
	}
	
	Game_MajiangBattler.prototype.makeCanChiResult = function(dapai,dapaijia)
	{
		if(!this.canChi(dapaijia)) 
		{
			return [];
		}
		const value = dapai.value();
		const type = dapai.type();
		const ret = [];
		
		if(type === 3) return [];
		
		let valueArray = [
			[value - 2, value - 1],
			[value - 1, value + 1],
			[value + 1, value + 2]
		].filter(ar => ar.every(v => (v <= 9 && v > 0)));
		
		const codeArray = valueArray.map(ar => ar.map(v => v + (type * 10)));
		
		for(const code of codeArray)
		{
			
			const pai1 = this._tehai.find(p => p.code() === code[0]);
			if(!pai1) continue;
			
			const pai2 = this._tehai.find(p => p.code() === code[1]);
			if(!pai2) continue;
			
			ret.push({"kind" : "chi", "dapai" : dapai , "paizi" : [pai1,pai2]});
		}
		
		return ret;
	};
	
	
	Game_MajiangBattler.prototype.applyFulu = function(fuluResult,battler)
	{
		
		const dapai = fuluResult.dapai;
		const relativePosition = this.relativePosition(battler);
		dapai.clearLizhiPosition();
		dapai.setFulu(relativePosition , fuluResult.kind);
		battler._realHe = battler._realHe.filter(p => p !== dapai);
		
		this.applyChiPengGang(fuluResult,battler);
	};
	
	
	Game_MajiangBattler.prototype.applyChiPengGang = function(fuluResult,battler)
	{
		const paizi = fuluResult.paizi;
		const dapai = fuluResult.dapai;
		this._tehai = this._tehai.filter(p => !paizi.includes(p)  );
		
		const fulu = $gameMajiangPaishan.makeMianzi([...paizi , dapai]);
		
		this._fulu.push(fulu);
	};
	
	Game_MajiangBattler.prototype.applyJiagang = function(fuluResult)
	{
		this.clearZimoStatus();
		
		const pai = fuluResult.paizi[0];
		
		this._tehai = this._tehai.filter(p => p !== pai );
		
		
		fuluResult.fulu.jiagang(pai);
		this.setTingpaiMachi();
	};
	
	Game_MajiangBattler.prototype.applyAngang = function(fuluResult)
	{
		this.clearZimoStatus();
		
		const paizi = fuluResult.paizi;
		
		this._tehai = this._tehai.filter(p => !paizi.includes(p) );
		paizi.forEach( p => p.setFulu(null,"angang"));
		const fulu = $gameMajiangPaishan.makeMianzi([...paizi]);
		this._fulu.push(fulu);
		this.setTingpaiMachi();
	};
	
	Game_MajiangBattler.prototype.setTestFulu = function(paiziCode,fuluType,position = "kamijia",index = 0)
	{
		const paizi = $gameMajiangPaishan.selectZimoPaizi(paiziCode);
		paizi[index].setFulu(position,fuluType);
		const fulu = $gameMajiangPaishan.makeMianzi(paizi);
		this._fulu.push(fulu);
	};
	
	
	
	Game_MajiangBattler.prototype.unitName = function()
	{
		return this.battlerData().unitName;
	};
	
	Game_MajiangBattler.prototype.CPU = function()
	{
		
		return $gameMajiangCPU[this.unitName()];
	};
	
	
	
	Game_MajiangBattler.prototype.cpuClass = function()
	{
		return this.battlerData().cpuClass;
	};
	
	
	
	Game_MajiangBattler.prototype.queryFuluCPU = function(dapaijia)
	{
		try
		{
			return this.CPU().fuluAction(this.position(),this._fuluActions,dapaijia.position());
		}
		catch(e)
		{
			return null;
		}
	};
	
	Game_MajiangBattler.prototype.queryZimoActionCPU = function()
	{
		try
		{
			return this.CPU().zimoAction(this.position(),this._zimoActions);
		}
		catch(e)
		{
			return null;
		}
	};
		
	Game_MajiangBattler.prototype.queryDapaiSelectCPU = function()
	{
		try
		{
			return this.CPU().dapai(this.position());
		}
		catch(e)
		{
			return 0;
		}
	};
	
	Game_MajiangBattler.prototype.queryDapaiCPU = function()
	{
		const paiId = this.queryDapaiSelectCPU();
		
		const tehai = this.tehai();
		let dapai = tehai.find(p => p.isDapaiEnabled() && p.paiId() === paiId);
		if(!dapai)
		{
			dapai = tehai.find(p => p.isDapaiEnabled());
			return dapai || tehai[0];
		}
		return dapai;
	};
	
	Game_MajiangBattler.prototype.queryFuluPlayer = function(dapaijia)
	{
		if(this.needCPU())
		{
			this.queryFuluCPU(dapaijia);
		}
	};
	
	Game_MajiangBattler.prototype.queryZimoActionPlayer = function()
	{
		if(this.needCPU())
		{
			this.queryZimoActionCPU();
		}
	};
	
	Game_MajiangBattler.prototype.queryDapaiPlayer = function()
	{
		if(this.needCPU())
		{
			this.queryDapaiCPU();
		}
	};
	
	
	Game_MajiangBattler.prototype.makeZimoActionResult = function()
	{
		const zimoActions = [];
		
		zimoActions.push(...this.canZimoHeleResult());
		zimoActions.push(...this.canLizhiResult());
		zimoActions.push(...this.canJiuzhongJiupaiResult());
		if(this.canGang()
			&& ($gameMajiangPaishan.shengyu() > 0)
		)
		{
			zimoActions.push(...this.canAngangResult());
			zimoActions.push(...this.canJiagangResult());
		}
		
		this._zimoActions = zimoActions;
	};
	
	Game_MajiangBattler.prototype.canGang = function()
	{
		if(MajiangManager.duo() && !$gameMajiangSystem.duoSettings.canGang)
		{
			return false;
		}
		return (MajiangManager.canGang() && !$gameMajiangSystem.config.rsGang);
	};
	
	
	Game_MajiangBattler.prototype.zimoActions = function()
	{
		return this._zimoActions;
	};
	
	Game_MajiangBattler.prototype.canLizhiResult = function()
	{
		
		if(this.canLizhi())
		{
			
			return [{"kind" : "lizhi"}];
			
		}
		return [];
	};
	
	Game_MajiangBattler.prototype.canLizhi = function()
	{
		return (!this._lizhi
		 && this.isMenqian()
		 && this.checkLizhiAdopt()
		 && (this.point() >= MajiangManager.lizhiDeposit())
		 && ($gameMajiangPaishan.shengyu() >= this.canLizhiShengyu())
		 && this.canTingpai()
		);
	};
	
	Game_MajiangBattler.prototype.canLizhiShengyu = function()
	{
		return (MajiangManager.numBattlers());
	};
	
	Game_MajiangBattler.prototype.checkLizhiAdopt = function()
	{
		if(this.yishun() && $gameMajiangSystem.yakuData.doubleLizhi.adopt)
		{
			return true;
		}
		
		return $gameMajiangSystem.yakuData.lizhi.adopt;
	};
	
	Game_MajiangBattler.prototype.canJiuzhongJiupaiResult = function()
	{
		if($gameMajiangSystem.gamerule.jiuzhongJiupai
			 && this.yishun()
			 && this.checkJiuzhongJiupai()
		)
		{
			return [{"kind" : "jiuzhongJiupai"}];
		}
		return [];
	};
	
	Game_MajiangBattler.prototype.canZimoHeleResult = function()
	{
		if(this.canHele())
		{
			let helePai = this.tehai().find(p => p.isZimoPai());
			if(!helePai)
			{
				helePai = this.tehai();
			}
			else
			{
				helePai = [helePai];
			}
			return [{"kind" : "zimo" , "helePai" : helePai}];
		}
		return [];
	};
	
	Game_MajiangBattler.prototype.canAngangResult = function()
	{
		let tehaiCode = this.tehai().map(p => p.code());
		
		tehaiCode.sort((a,b) => a - b);
		
		const length = tehaiCode.length;
		const ret = [];
		
		for(let i = 0 ; i < length ; i++)
		{
			const b = [];
			
			b[0] = tehaiCode[i];
			b[1] = tehaiCode[i + 1];
			b[2] = tehaiCode[i + 2];
			b[3] = tehaiCode[i + 3];
			
			if(!b.every(v => v)) break;
			
			if(b.every(v => v === b[0]))
			{
				const paizi = this.tehai().filter(p => p.code() === b[0]);
				if(!this.isLizhi() || this.checkLizhiAngang(paizi))
				{
					ret.push({"kind" : "angang" , "paizi" : paizi});
				}
			}
		}
		return ret;
	};
	
	Game_MajiangBattler.prototype.checkLizhiAngang = function(paizi)
	{
		let results = this.checkTingpai(this.tehai().filter(p => !p.isZimoPai()));
		const resultsLength = results.length;
		results = results.filter(r => r.helexings.every(h => h.type === "normal"));
		if(results.length === 0 || resultsLength !== results.length)
		{
			return false;
		};
		const gangziCode = paizi[0].code();
		
		for(const result of results)
		{
			const helexings = result.helexings;
			for (const helexing of helexings)
			{
				const check = helexing.mianzi.some(m => 
					{
						return (m.isShunzi() 
						&& m.mianziCode().includes(gangziCode)
						);
					}
				);
				if(check)
				{
					return false;
				}
			}
		}
		
		return true;
	};
	
	Game_MajiangBattler.prototype.canJiagangResult = function()
	{
		const ret = [];
		const keziFulu = this._fulu.filter(m => m.fuluType() === "peng");
		
		for(const fulu of keziFulu)
		{
			const code = fulu.keziCode();
			
			const pai = this.tehai().find(p => p.code() === code);
			
			if(pai)
			{
				ret.push({"kind" : "jiagang" , "paizi" : [pai] , "fulu" : fulu});
			}
		}
		return ret;
	};
	
	
	Game_MajiangBattler.prototype.startLizhiSelect = function()
	{
		const tehai = this.tehai();
		const lizhiSengenPaizi = [];
		for(const result of this._canTingpaiResult)
		{
			lizhiSengenPaizi.push(result.qiepai);
		}
		
		tehai.forEach(p =>
			{
				if(!lizhiSengenPaizi.includes(p))
				{
					p.setDapaiEnabled(false);
					
					if(this.isPlayer())
					{
						p.setEnabled(false);
					}
				}
				
			});
		
		this._lizhiSengenPaizi = lizhiSengenPaizi;
	};
	
	Game_MajiangBattler.prototype.kuigaePaizi = function(action)
	{
		const ret = [];
		const dapai = action.dapai;
		const code = dapai.code();
		const dapaiValue = dapai.value();
		const dapaiType = dapai.type();
		
		switch(action.kind)
		{
			case "peng" : 
				ret.push(code);
			break;
			case "chi" :
				ret.push(code); 
				const paiziValue = [...action.paizi.map(v => v.value())].sort((a,b) => a - b);
				let suji = 0;
				if(dapaiValue === (paiziValue[0] + 2))
				{
					suji = paiziValue[0] - 1;
				}
				else if(dapaiValue === (paiziValue[1] - 2))
				{
					suji = paiziValue[1] + 1;
				}
				if((suji >= 1 && suji <= 9))
				{
					ret.push((dapaiType * 10) + suji);
				}
			break;
		}
		return ret;
	};
	
	Game_MajiangBattler.prototype.setKuigaeSelect = function(action)
	{
		const kuigaeCode = this.kuigaePaizi(action);
		this.tehai().forEach(p =>
			{
				if(kuigaeCode.includes(p.code()))
				{
					p.setDapaiEnabled(false);
					
					if(this.isPlayer())
					{
						p.setEnabled(false);
					}
				}
				
			});
	};
	
	Game_MajiangBattler.prototype.clearSelectRestrict = function()
	{
		this.tehai().forEach(p => {p.setEnabled(true); p.setDapaiEnabled(true)});
	};
	
	Game_MajiangBattler.prototype.clearLizhiSelect = function()
	{
		this.clearSelectRestrict();
	};
	
	Game_MajiangBattler.prototype.applyLizhi = function(req)
	{
		
		this._lizhi = true;
		this.setYifa(true);
		this.gainPoint(-MajiangManager.lizhiDeposit());
		if(req.isDouble)
		{
			this._doubleLizhi = true;
		}
	};
	
	
	Game_MajiangBattler.prototype.relativePosition = function(battler)
	{
		const z = this.position();
		const t = battler.position();
		
		if((z + 1) % 4 === t)
		{
			return "shimojia";
		}
		else if((z + 3) % 4 === t)
		{
			return "kamijia";
		}
		else if(z === t)
		{
			return "zijia";
		}
		else
		{
			return "duimian";
		}
	};
	
	
	
	Game_MajiangBattler.prototype.point = function()
	{
		return this._point.current;
	};
	
	Game_MajiangBattler.prototype.finalPoint = function()
	{
		return this.point();
	};
	
	Game_MajiangBattler.prototype.pointData = function()
	{
		return this._point;
	};
	
	Game_MajiangBattler.prototype.resetPointHistory = function()
	{
		this._point.prev = 0;
		this._point.add = 0;
	};
	
	Game_MajiangBattler.prototype.gainPoint = function(value)
	{
		const prev = this.point();
		this._point = {
			"prev" : prev ,
			"add" : value ,
			"current" : prev + value
			} 
		
	};
	
	Game_MajiangBattler.prototype.tehai = function()
	{
		return this._tehai;
	};
	
	Game_MajiangBattler.prototype.allPaizi = function()
	{
		return this.tehai().concat(...this.fulu().map(m => m.paizi()));
	};
	
	Game_MajiangBattler.prototype.addHe = function(pai)
	{
		this._he.push(pai);
		this._realHe.push(pai);
	};
	
	Game_MajiangBattler.prototype.he = function()
	{
		return this._he;
	};
	Game_MajiangBattler.prototype.realHe = function()
	{
		return this._realHe;
	};
	
	
	
	
	
	
	Game_MajiangBattler.prototype.fulu = function()
	{
		return this._fulu;
	};
	
	Game_MajiangBattler.prototype.setFulu = function(fulu)
	{
		this._fulu = fulu;
	};
	
	Game_MajiangBattler.prototype.fuluPaizi = function()
	{
		return this._fulu.map(m => m.paizi()).flat();
	};
	
	Game_MajiangBattler.prototype.setMenfeng = function(status)
	{
		this._menfeng = status;
	};
	Game_MajiangBattler.prototype.menfeng = function()
	{
		return this._menfeng;
	};
	
	Game_MajiangBattler.prototype.zhuangFeng = function()
	{
		return this._zhuangFeng;
	};
	
	Game_MajiangBattler.prototype.lipai = function(paizi)
	{
		paizi.sort(function(a,b){ return a.paiId() - b.paiId() });
	};
	
	Game_MajiangBattler.prototype.tehaiLipai = function()
	{
		this.lipai(this._tehai);
	};
	
	Game_MajiangBattler.prototype.isLizhi = function()
	{
		return this._lizhi;
	};
	
	Game_MajiangBattler.prototype.isDoubleLizhi = function()
	{
		return this._doubleLizhi;
	};
	
	Game_MajiangBattler.prototype.setFuriten = function(status)
	{
		if(this.canFuriten())
		{
			this._furiten = status;
		}
	};
	
	Game_MajiangBattler.prototype.setPermanentFuriten = function()
	{
		if(this.canFuriten())
		{
			this._permanentFuriten = true;
		}
	};
	
	Game_MajiangBattler.prototype.canFuriten = function()
	{
		return !$gameMajiangSystem.config.rsFuriten
	};
	
	
	Game_MajiangBattler.prototype.isFuriten = function()
	{
		return (this._furiten || this._permanentFuriten);
	};
	
	Game_MajiangBattler.prototype.isMenqian = function()
	{
		return this._fulu.every(fulu => !fulu.isQiuren());
	};
	
	Game_MajiangBattler.prototype.isHele = function(tehai)
	{
		return (this.heleResult() && this.heleResult().point.gainPoint > 0);
	};
	
	Game_MajiangBattler.prototype.setYishun = function(status)
	{
		this._yishun = status;
	};
	
	Game_MajiangBattler.prototype.setYifa = function(status)
	{
		this._yifa = status;
	};
	
	Game_MajiangBattler.prototype.yishun = function()
	{
		return this._yishun;
	};
	
	Game_MajiangBattler.prototype.yifa = function()
	{
		return this._yifa;
	};
	
	Game_MajiangBattler.prototype.daopai = function()
	{
		this._tehai.forEach(p => p.setTajiaHidden(false));
	};
	
	Game_MajiangBattler.prototype.liujuResultDaopai = function()
	{
		this._tehai.forEach(p => p.setTajiaHidden(false));
		if(this.isTingpai())
		{
			this._tehai.forEach(p => p.setHidden(false));
		}
		else
		{
			this._tehai.forEach(p => p.setHidden(true));
		}
		this.allPaizi().concat(this.he()).forEach(p => p.setDoraEffect(false));
	};
	
	
	
	
	Game_MajiangBattler.prototype.checkTingpai = function (tehai)
	{
		const allPaizi = [...tehai];
		const nominees = $gameMajiangPaishan.allPaicode();
		const ret = [];
		for(const nominee of nominees)
		{
			const dummyPai = $gameMajiangPaishan.makeDummyPai(nominee);
			const tempTehai = [...tehai , dummyPai];
			const helexings = $gameMajiangPaishan.checkHelexing(tempTehai);
			
			if(helexings.length > 0)
			{
				ret.push({"machi" : nominee , "helexings" : helexings});
			}
		}
		return ret;
	};
	
	Game_MajiangBattler.prototype.startModa = function (afterZimo)
	{
		this.setFuriten(false);
		this.clearTempActual();
		if(!afterZimo)
		{
			this.makeCanTingpaiResult();
		}
	};
	
	Game_MajiangBattler.prototype.makeCanTingpaiResult = function ()
	{
		this._canTingpaiResult = [];
		for(const qiepai of this._tehai)
		{
			const tempTehai = this._tehai.filter(p => p !== qiepai);
			const result = this.checkTingpai(tempTehai);
			if(result.length > 0)
			{
				const machi = result.map(r => r.machi);
				const helexings = result.map(r => r.helexings);
				this._canTingpaiResult.push(
				{
					"qiepai" : qiepai , //この牌を切ったとき
					"machi" : machi , // 待ちがこれ
					"helexings" : helexings
				});
			}
		}
		
	};
	
	Game_MajiangBattler.prototype.canHele = function (rongpai)
	{
		const result = this.pointResult(this.tehai(),rongpai);
		
		return (result && result.point.gainPoint !== 0);
		
		return false;
	};
	
	Game_MajiangBattler.prototype.clearLianHele = function()
	{
		this._lianHele = 0;
	};
	
	Game_MajiangBattler.prototype.lianHele = function()
	{
		return this._lianHele;
	};
	
	Game_MajiangBattler.prototype.yakitori = function()
	{
		return this._yakitori;
	};
	
	
	Game_MajiangBattler.prototype.setHele = function (rongpai)
	{
		this._yakutori = false;
		this._lianHele++;
		this._hele = true;
		this._yakitori = false;
		
		if(rongpai)
		{
			this._rongpai = rongpai
			rongpai.setRongPai(true);
			this._isRong = true;
		}
		if(this.isMenqian() && this.yishun() && !rongpai && this.isDealer())
		{
			this.setTianhe(true);
		}
		
		this.setHeleResult(this.helePoint(this.tehai(),rongpai));
		if(this.isPlayer())
		{
			$gameMajiangSystem.addResult("hele");
			if(this.isYakumanHele())
			{
				$gameMajiangSystem.addResult("yakuman");
			}
			
			this.setYakuResult(this._heleResult);
		}
	};
	
	Game_MajiangBattler.prototype.setFangchong = function ()
	{
		if(this.isPlayer())
		{
			$gameMajiangSystem.addResult("fangchong");
		}
	};
	
	Game_MajiangBattler.prototype.setHeleResult = function (heleResult)
	{
		this._heleResult = heleResult;
	};
	
	Game_MajiangBattler.prototype.setYakuResult = function(heleResult)
	{
		for(const yaku of heleResult.yaku.yakuResult)
		{
			if($dataMajiangSystem.yakuData[yaku.name])
			{
				$gameMajiangSystem.addYakuResult(yaku.name);
			}
		}
	};
	
	Game_MajiangBattler.prototype.isHele = function ()
	{
		return this._hele;
	};
	
	Game_MajiangBattler.prototype.helePoint = function (tehai,rongpai)
	{
		return this.pointResult(tehai,rongpai);
	};
	
	
	
	
	
	Game_MajiangBattler.prototype.checkJiuzhongJiupai = function ()
	{
		const codeArray = [1,9,11,19,21,29,31,32,33,34,35,36,37];
		const tehai = this.tehai();
		const result = codeArray.map(code =>
		{
			return tehai.filter(p => p.code() === code).length;
		})
		return (result.filter(c => c > 0 ).length >= 9);
	};
	
	Game_MajiangBattler.prototype.isRong = function ()
	{
		return this._isRong;
	};
	
	Game_MajiangBattler.prototype.heleResult = function()
	{
		return this._heleResult;
	};
	
	Game_MajiangBattler.prototype.isYakumanHele = function()
	{
		if(!this._heleResult) return false;
		if($gameMajiangSystem.gamerule.aotenjo)
		{
			return this._heleResult.point.fan >= 13;
		}
		return ["yakuman","kazoeYakuman"].includes(this._heleResult.point.mangan);
	};
	
	Game_MajiangBattler.prototype.isSambaimanHele = function()
	{
		if(!this._heleResult) return false;
		
		if($gameMajiangSystem.gamerule.aotenjo)
		{
			if(this.isYakumanHele()) return false;
			return this._heleResult.point.fan >= 11;
		}
		return (this._heleResult.point.mangan === "sambaiman");
	};
	
	Game_MajiangBattler.prototype.isBaimanHele = function()
	{
		if(!this._heleResult) return false;
		
		if($gameMajiangSystem.gamerule.aotenjo)
		{
			if(this.isSambaimanHele()) return false;
			return this._heleResult.point.fan >= 8;
		}
		return (this._heleResult.point.mangan === "baiman");
	};
	
	Game_MajiangBattler.prototype.isHanemanHele = function()
	{
		if(!this._heleResult) return false;
		
		if($gameMajiangSystem.gamerule.aotenjo)
		{
			if(this.isBaimanHele()) return false;
			return this._heleResult.point.fan >= 6;
		}
		return (this._heleResult.point.mangan === "haneman");
	};
	
	Game_MajiangBattler.prototype.isManganHele = function()
	{
		if(!this._heleResult) return false;
		
		if($gameMajiangSystem.gamerule.aotenjo)
		{
			if(this.isHanemanHele()) return false;
			const manganPoint = (this.isDealer()) ? 12000 : 8000;
			return this._heleResult.point.gainPoint >= manganPoint;
		}
		return (this._heleResult.point.mangan === "mangan");
	};
	
	Game_MajiangBattler.prototype.manganRank = function()
	{
		if(this.isYakumanHele())
		{
			return 5;
		}
		else if(this.isSambaimanHele())
		{
			return 4;
		}
		else if(this.isBaimanHele())
		{
			return 3;
		}
		else if(this.isHanemanHele())
		{
			return 2;
		}
		else if(this.isManganHele())
		{
			return 1;
		}
		return 0;
	};
	
	Game_MajiangBattler.prototype.canTingpai = function()
	{
		return (this._canTingpaiResult.length > 0);
	};
	
	Game_MajiangBattler.prototype.heleAnimation = function()
	{
		const rank = this.manganRank();
		return this.findHeleAnimation(this.battlerData().heleAnimation,rank)
		 || this.findHeleAnimation($dataMajiangSystem.heleAnimation,rank);
	};
	
	Game_MajiangBattler.prototype.findHeleAnimation = function(data,rank)
	{
		
		const table = ["yakuman" , "sambaiman" , "baiman" , "haneman" , "mangan","hele"];
		for(let i = 0 ; i < 6 ; i++)
		{
			const key = table[(5 - rank) + i];
			if(!key) break;
			const animationId = data[key];
			if(animationId)
			{
				return animationId;
			}
		}
		return 0;
	};
	
	Game_MajiangBattler.prototype.lizhiBgm = function()
	{
		const key = "actorLizhi_" + this.dataActorId();
		if($dataMajiangSystem.audioData[key].enabled)
		{
			return key;
		}
		else
		{
			return "lizhiBgm";
		}
	};
	
	
	Game_MajiangBattler.prototype.canTingpaiResult = function()
	{
		return this._canTingpaiResult;
	};
	
	Game_MajiangBattler.prototype.tingpaiMachi = function()
	{
		
		return this._tingpaiMachi;
	};
	
	Game_MajiangBattler.prototype.isTingpai = function()
	{
		return (this._tingpaiMachi.length > 0);
	};
	
	Game_MajiangBattler.prototype.pointResult = function(tehai,rongpai)
	{
		let helepaizi = [];
		let isRong = false;
		
		const tempTehai = [...tehai];
		
		if(rongpai)
		{
			isRong = true;
			const dummyRongpai = JsonEx.makeDeepCopy(rongpai);
			tempTehai.push(dummyRongpai);
			dummyRongpai.setRongPai(true);
		}
		
		const helexings = $gameMajiangPaishan.checkHelexing(tempTehai);
		if(helexings.length === 0) return null;
		
		return $gameMajiangPaishan.pointResult(helexings,this.fulu(),tempTehai,this , isRong);
	};
	
	Game_MajiangBattler.prototype.echoFulu = function()
	{
		return this._fulu.map(f => f.echoFulu());
	};
	
	Game_MajiangBattler.prototype.lines = function(key)
	{
		key = (["daminggang" , "jiagang" , "angang"].includes(key)) ? "gang" : key;
		
		const lines = this.battlerData().lines[key];
		const index = Math.randomInt(lines.length);
		
		const text = lines[index];
		const voiceLinesKey = "voiceLines_%1_%2_%3".format(this.dataActorId(),key,index);
		return {"text" : text , "audio" : voiceLinesKey};
	};
	
	Game_MajiangBattler.prototype.isNagashiMangan = function()
	{
		return ($gameMajiangSystem.gamerule.nagashiMangan && this._nagashi);
	};
	
	Game_MajiangBattler.prototype.gangCount = function()
	{
		return this._fulu.reduce((r,c) => r + ((c.isGangzi()) ? 1 : 0),0);
	};
	Game_MajiangBattler.prototype.addOverlook = function(code)
	{
		if(this.isLizhi())
		{
			this.addActual(code);
		}
		else
		{
			this.addTempActual(code);
			this.refreshSuji();
		}
	};
	
	Game_MajiangBattler.prototype.addActual = function(code)
	{
		if(!this._actual.includes(code))
		{
			this._actual.push(code);
		}
		this.refreshSuji();
	};
	
	Game_MajiangBattler.prototype.addTempActual = function(code)
	{
		if(!this._actual.includes(code) && !this._tempActual.includes(code))
		{
			this._tempActual.push(code);
		}
	};
	
	Game_MajiangBattler.prototype.clearTempActual = function()
	{
		this._tempActual = [];
	};
	
	Game_MajiangBattler.prototype.actual = function()
	{
		return this._actual.concat(this._tempActual);
	};
	
	Game_MajiangBattler.prototype.refreshSuji = function()
	{
		const actual = this.actual();
		const ret = [];
		for(const code of $dataMajiangSystem.allPaicode)
		{
			const type = Math.floor(code / 10);
			
			const value = code % 10;
			if(type === 3 || (MajiangManager.samma() && type === 0)) continue;
			switch(value)
			{
				case 1 : 
				case 2 : 
				case 3 : 
					if(actual.includes(code + 3))
					{
						ret.push(code);
					}
				break;
				case 4 : 
				case 5 : 
				case 6 : 
					if(actual.includes(code - 3) && actual.includes(code + 3))
					{
						ret.push(code);
					}
				break;
				case 7 : 
				case 8 : 
				case 9 : 
					if(actual.includes(code - 3))
					{
						ret.push(code);
					}
				break;
			}
		}
		this._suji = ret;
	};
	
	Game_MajiangBattler.prototype.suji = function()
	{
		return this._suji;
	};
	
	Game_MajiangBattler.prototype.visiblePaiziObject = function()
	{
		const paizi = [];
		let all = $gameMajiangBattlers.allBattlers();
		
		for(const battler of all)
		{
			paizi.push(...battler.he());
			paizi.push(...battler.fuluPaizi());
		}
		paizi.push(...$gameMajiangPaishan.echoDoraPaizi());
		
		return paizi;
	};
	
	Game_MajiangBattler.prototype.makeVisiblePaiziData = function(argsPaizi)
	{
		const paizi = [...new Set(argsPaizi)].filter(p => !p.isDummy());
		const data = [];
		for(const code of $dataMajiangSystem.allPaicode)
		{
			data[code] = paizi.reduce((r,c) => (c.code() === code) ? r + 1 : r , 0);
		}
		return data;
	};
	
	Game_MajiangBattler.prototype.refreshVisiblePaizi = function()
	{
		let paizi = this.visiblePaiziObject();
		
		this._visiblePaizi = this.makeVisiblePaiziData(paizi,434);
		
		this._visiblePaiziAll = this.makeVisiblePaiziData(paizi.concat(this.tehai()),98);
	};
	Game_MajiangBattler.prototype.visiblePaizi = function(includeTehai)
	{
		if(includeTehai)
		{
			return this._visiblePaiziAll;
		}
		else
		{
			return this._visiblePaizi;
		}
	}
	
	
	//----------------------------
	
	
	Game_MajiangBattlers.prototype.initialize = function()
	{
		this.clear();
	};
	
	Game_MajiangBattlers.prototype.clear = function()
	{
		this._battlers = [];
	};
	
	
	Game_MajiangBattlers.prototype.setGameBattlers = function(...args)
	{
		this._battlers = [];
		
		
		args.forEach(function(actorId,i)
		{
			if(actorId === 0)
			{
				this._battlers[i] = null;
				return;
			}
			const battler = new Game_MajiangBattler(actorId);
			
			
			battler.setPosition(i);
			
			
			this._battlers[i] = battler;
			if(i === 0)
			{
				battler.setPlayer(true);
			}
			
			battler.callCPU("setBattler");
			
		},this);
		
	};
	
	
	
	
	
	
	//親IDから 全バトラーの自風IDを設定
	Game_MajiangBattlers.prototype.setFeng = function(dealerPosition)
	{
		this.allBattlers(dealerPosition).forEach(function(battler , i )
		{
			battler.setMenfeng(i + 1);
		});
	};
	
	
	Game_MajiangBattlers.prototype.allBattlers = function(startPosition = 0)
	{
		if(startPosition === 0)
		{
			return this._battlers.filter(b => !!b);
		}
		const ret = [];
		let position = startPosition;
		for(let i = 0; i < MajiangManager.numBattlers() ; i++)
		{
			ret.push($gameMajiangBattlers.battler(position));
			position = MajiangManager.nextPositionId(position);
		}
		
		return ret;
	};
	
	Game_MajiangBattlers.prototype.battler = function(positionId)
	{
		return this._battlers[positionId];
	};
	
	Game_MajiangBattlers.prototype.battlerClear = function()
	{
		this.allBattlers().forEach( battler => battler.clear());
	};
	//一局クリア
	Game_MajiangBattlers.prototype.clearYiju = function()
	{
		this.allBattlers().forEach( battler => battler.clearYiju());
	};
	
	Game_MajiangBattlers.prototype.dealer = function()
	{
		return this.allBattlers().find(battler => battler.isDealer());
	};
	
	Game_MajiangBattlers.prototype.zijia = function()
	{
		return this._battlers[0];
	};
	
	Game_MajiangBattlers.prototype.shimojia = function()
	{
		return this._battlers[1];
	};
	
	Game_MajiangBattlers.prototype.duimian = function()
	{
		return this._battlers[2];
	};
	
	Game_MajiangBattlers.prototype.kamijia = function()
	{
		return this._battlers[3];
	};
	
	Game_MajiangBattlers.prototype.player = function()
	{
		return this._battlers.find(b => b.isPlayer());
	};
	
	
	
	
	
	
	
	Game_MajiangPai.prototype.initialize = function(id,dummy = false)
	{
		this._id = id;
		this._dummy = dummy;
		
		this._akadora = false;
		this.setTypeValue();
		this.clear();
	};
	
	Game_MajiangPai.prototype.clear = function()
	{
		this._select = false;
		this._hidden = false;
		this._tajiaHidden = false;
		
		
		this._lizhi = false;
		this._fuluTajia = ""; // ["" , "shimojia" , "duimian" ...]
		this._fuluType = "";
		this._jiagang = false;
		this._dora = 0;
		this._uradora = 0;
		this._zimoPai = false;
		this._rongPai = false;
		
		this._lingshangPai = false;
		this._requestResetSelectionEffect = false;
		
		this._enabled = true;
		this._dapaiEnabled = true;
		this._visible = true;
		this._needSpacing = false;
		
		this._doraEffect = false;
		
		
		
		
		this.clearPosition();
		
		
	};
	Game_MajiangPai.prototype.clearPosition = function()
	{
		this._position = ""; // ["" , "lizhi", "jiagangBottom" , "jiaganTop"]
		this._horizontal = ""; //["" , "left" ,"right"];
	}
	
	Game_MajiangPai.prototype.paiId = function()
	{
		return this._id;
	};
	
	Game_MajiangPai.prototype.object = function()
	{
		return $dataMajiangSystem.paiData[this._id];
	};
	
	Game_MajiangPai.prototype.setLizhi = function(declaration)
	{
		this._horizontal = "left";
		this._position = "lizhi";
		if(declaration)
		{
			this._lizhi = true;
		}
	};
	
	Game_MajiangPai.prototype.lizhiDeclaration = function()
	{
		return this._lizhi;
	};
	Game_MajiangPai.prototype.isLizhiPosition = function()
	{
		return (this._position === "lizhi");
	};
	
	Game_MajiangPai.prototype.clearLizhiPosition = function()
	{
		this.clearPosition();
	};
	
	
	
	Game_MajiangPai.prototype.setLingshangPai = function(status)
	{
		this._lingshangPai = status;
	};
	Game_MajiangPai.prototype.isLingshangPai = function()
	{
		return this._lingshangPai;
	};
	
	Game_MajiangPai.prototype.setPosition = function(status)
	{
		this._position = status;
	}
	
	Game_MajiangPai.prototype.setHorizontal = function(status)
	{
		
		this._horizontal = status;
	};
	
	Game_MajiangPai.prototype.setFulu = function(tajia,fuluType)
	{
		this._fuluTajia = tajia;
		this._fuluType = fuluType; // ["chi","peng" , "angang" , "jiagang" , "daminggang"]
	};
	
	
	Game_MajiangPai.prototype.fuluTajia = function()
	{
		return this._fuluTajia;
	};
	
	Game_MajiangPai.prototype.setFuluType = function(fuluType)
	{
		this._fuluType = fuluType;
	}
	
	Game_MajiangPai.prototype.fuluType = function()
	{
		return this._fuluType;
	}
	
	Game_MajiangPai.prototype.setJiagangPai = function()
	{
		this._jiagang = true;
	};
	
	Game_MajiangPai.prototype.isJiagangPai = function()
	{
		return this._jiagang;
	};
	
	Game_MajiangPai.prototype.horizontal = function()
	{
		return this._horizontal;
	};
	Game_MajiangPai.prototype.position = function()
	{
		return this._position;
	};
	
	Game_MajiangPai.prototype.setZimoPai = function(status)
	{
		this._zimoPai = status;
	};
	
	Game_MajiangPai.prototype.setRongPai = function(status)
	{
		this._rongPai = status;
	};
	
	Game_MajiangPai.prototype.isZimoPai = function()
	{
		return this._zimoPai;
	};
	
	Game_MajiangPai.prototype.isRongPai = function()
	{
		return this._rongPai;
	};
	
	Game_MajiangPai.prototype.isHelePai = function()
	{
		return (this._rongPai || this._zimoPai);
	};
	
	
	
	
	
	Game_MajiangPai.prototype.addDora = function()
	{
		this._dora++;
		this.setDoraEffect(true);
	};
	
	Game_MajiangPai.prototype.addUradora = function()
	{
		this._uradora++;
	};
	
	Game_MajiangPai.prototype.dora = function()
	{
		return this._dora + ((this.isAkadora()) ? 1 : 0);
	};
	
	Game_MajiangPai.prototype.uradora = function()
	{
		return this._uradora;
	};
	
	Game_MajiangPai.prototype.setDoraEffect = function(status)
	{	
		if($dataMajiangSystem.params.setDoraColor)
		{
			this._doraEffect = status;
		}
	};
	
	Game_MajiangPai.prototype.doraEffect = function()
	{
		if(this.isTajiaHidden()
			|| this.isHidden()
			|| this.isDummy()
		)
		{
			return false;
		}
		return this._doraEffect;
	};
	
	
	
	Game_MajiangPai.prototype.setHidden = function(status)
	{
		this._hidden = status;
	};
	
	
	Game_MajiangPai.prototype.isHidden = function()
	{
		return this._hidden;
	};
	Game_MajiangPai.prototype.setEnabled = function(status)
	{
		this._enabled = status;
	};
	
	Game_MajiangPai.prototype.setVisible = function(status)
	{
		this._visible = status;
	};
	
	Game_MajiangPai.prototype.visible = function(status)
	{
		return this._visible;
	};
	
	Game_MajiangPai.prototype.setNeedSpacing = function(status)
	{
		this._needSpacing = status;
	};
	
	Game_MajiangPai.prototype.needSpacing = function()
	{
		return this._needSpacing ;
	};
	
	
	
	Game_MajiangPai.prototype.isEnabled = function()
	{
		return this._enabled;
	};
	
	Game_MajiangPai.prototype.setTajiaHidden = function(status)
	{
		
		if(!$dataMajiangSystem.temp.tehaiOpen)
		{
			this._tajiaHidden = status;
		}
	};
	
	Game_MajiangPai.prototype.isTajiaHidden = function()
	{
		
		return this._tajiaHidden;
	};
	
	Game_MajiangPai.prototype.isDummy = function()
	{
		return this._dummy;
	};
	
	Game_MajiangPai.prototype.setDapaiEnabled = function(status)
	{
		
		this._dapaiEnabled = status;
	};
	
	Game_MajiangPai.prototype.isDapaiEnabled = function(status)
	{
		
		return this._dapaiEnabled;
	};
	
	
	
	Game_MajiangPai.prototype.isYaojiu = function()
	{
		return (this.isZipai() || this.isLaotou());
	};
	
	Game_MajiangPai.prototype.isQuandai = function()
	{
		return (this.isShupai() && [1,2,3,7,8,9].includes(this.value()));
	};
	
	Game_MajiangPai.prototype.isSanyuanYipai = function()
	{
		if(!this.isSanyuan()) return false;
		switch(this.code())
		{
			case 35 : return $gameMajiangSystem.yakuData.baiban.adopt;
			case 36 : return $gameMajiangSystem.yakuData.lufa.adopt;
			case 37 : return $gameMajiangSystem.yakuData.hongzhong.adopt;
		}
		return false;
	};
	
	
	Game_MajiangPai.prototype.isYipai = function(battler)
	{
		return (this.isQuanfengPai() || this.isMenfengPai(battler) || this.isSanyuanYipai() || this.isSammaBei());
	};
	Game_MajiangPai.prototype.isSammaBei = function()
	{
		return (MajiangManager.samma()
			 && $gameMajiangSystem.yakuData.bei.adopt
			 && this.code() === 34
		);
	};
	Game_MajiangPai.prototype.isSammaWanzi = function()
	{
		return (MajiangManager.samma() && (this.code() === 1 || this.code() === 9));
	};
	
	Game_MajiangPai.prototype.isQuanfengPai = function()
	{
		if(!$gameMajiangSystem.yakuData.quanfeng.adopt) return false;
		const feng = MajiangManager.currentQuanfeng();
		return (this.code() === (30 + feng));
	};
	Game_MajiangPai.prototype.isMenfengPai = function(battler)
	{
		if(!$gameMajiangSystem.yakuData.menfeng.adopt) return false;
		const feng = battler.menfeng();
		return (this.code() === (30 + feng));
	}
	
	Game_MajiangPai.prototype.isLaotou = function()
	{
		return (this.isShupai() && (this.value() === 1 || this.value() === 9));
	};
	
	Game_MajiangPai.prototype.isZhongzhang = function()
	{
		return (this.isShupai() && this.value() !== 1 && this.value() !== 9 );
	};
	
	Game_MajiangPai.prototype.isZipai = function()
	{
		return (this.type() === 3);
	};
	
	Game_MajiangPai.prototype.isShupai = function()
	{
		return (this.type() !== 3);
	};
	
	Game_MajiangPai.prototype.isFeng = function()
	{
		return (this.isZipai() && !this.isSanyuan());
	};
	Game_MajiangPai.prototype.isSanyuan = function()
	{
		return (this.isZipai() && this.value() > 4);
	};
	
	Game_MajiangPai.prototype.constituteLuyise = function()
	{
		return [12,13,14,16,18,36].includes(this.code());
	};
	
	
	Game_MajiangPai.prototype.setAkadora = function()
	{
		this._akadora = true;
	};
	
	Game_MajiangPai.prototype.isAkadora = function()
	{
		return this._akadora;
	};
	
	Game_MajiangPai.prototype.type = function()
	{
		return this._type;
	};
	Game_MajiangPai.prototype.value = function()
	{
		return this._value;
	};
	
	Game_MajiangPai.prototype.isSelected = function()
	{
		return this._select;
	};
	Game_MajiangPai.prototype.select = function()
	{
		this._select = true;
	};
	
	Game_MajiangPai.prototype.deselect = function()
	{
		this._select = false;
	};
	
	Game_MajiangPai.prototype.setTypeValue = function()
	{ 
		const code = this.getCode();
		this._code = code;
		this._type = Math.floor(code / 10);
		this._value = code % 10;
	};
	Game_MajiangPai.prototype.getCode = function()
	{
		return this.object().code;
	};
	Game_MajiangPai.prototype.code = function()
	{
		return this._code;
	};
	Game_MajiangPai.prototype.exportCode = function()
	{
		return this.code() + ((this.isAkadora()) ? + 5 : 0);
	};
	
	Game_MajiangPai.prototype.requestResetSelectionEffect = function()
	{
		return this._requestResetSelectionEffect;
	};
	
	Game_MajiangPai.prototype.setRequestResetSelectionEffect = function(status)
	{
		this._requestResetSelectionEffect = status;
	};
	
	
	//----------------------------
	
	
	
	class Mianzi
	{
		constructor(paizi,kind = "mianzi")
		{
			this._paizi = paizi;
			this._kind = kind;
			
			this.lipai();
			
			this.setMianziType();
			
			this.setEnabled();
			
		}
		setMianziType()
		{
			this._kezi = this.checkSame();
			if(this._kind ===  "mianzi")
			{
				this._shunzi = this.checkShunzi();
				
			}
			else if (this._kind === "dazi" )
			{
				
			}
		}
		
		isEnabled()
		{
			return this._enabled;
		}
			
		setEnabled()
		{
			this._enabled = this.check();
		}
		
		check()
		{
			if(!this._paizi.every(p => p)) return false;
			switch(this._kind)
			{
				case "duizi" : 
					return (this._paizi.length === 2 && this.checkSame());
				case "mianzi" : 
					return ((this._paizi.length === 3 || this._paizi.length === 4) && (this.isKezi() || this.isShunzi()));
				case "dazi" :
					return (this._paizi.length === 2 && (this.isKezi() || this.isShunzi()));
				default  : return false;
			}
		}
		
		jiagang(pai)
		{
			const fuluPai = this.findFuluPai();
			fuluPai.setFuluType("jiagang");
			pai.setNeedSpacing(false);
			pai.setTajiaHidden(false);
			
			pai.setJiagangPai();
			
			this._paizi.push(pai);
			this.lipai();
			this.setEnabled();
		}
		
		fuluType()
		{
			const fuluPai = this.findFuluPai();
			
			return fuluPai ? fuluPai.fuluType() : "";
		}
		findFuluPai()
		{
			return this._paizi.find(p => p.fuluType() !== "");
		}
		findJiagangPai()
		{
			return this._paizi.find(p => p.isJiagangPai());
		}
		
		fuluTajia()
		{
			for(const p of this._paizi)
			{
				const fuluTajia = p.fuluTajia();
				if(fuluTajia) return fuluTajia;
			}
			return "";
		}
		
		isQiuren()
		{
			const fulu = this.fuluType();
			return (fulu && fulu !== "angang");
		}
		
		lipai()
		{
			this._paizi.sort(function(a,b){ return a.paiId() - b.paiId() });
		}
		
		copy(pai)
		{
			return $gameMajiangPaishan.makeDummyPaiById(pai.paiId());
		}
		
		realFuluPai(pai)
		{
			return pai;
		}
		
		echoFulu()
		{
			const fulu = this.fuluType();
			
			
			if(fulu === "") return this._paizi;
			
			
			switch(fulu)
			{
				case "chi" :
				case "peng" :
				case "daminggang" :
					return this.setEchoFulu();
				case "angang" :
					return this.setEchoFuluAngang();
				case "jiagang" :
					return this.setEchoFuluJiagang();
				
			}
		}
		fuluHorizontal (fuluTajia)
		{
			return (fuluTajia === "shimojia") ? "right" : "left";
		}
		findInsertIndex (fuluTajia , isDaminggang)
		{
			const insertTable = ["kamijia" , "duimian" , "shimojia"];
			if(isDaminggang)
			{
				insertTable.splice(2 , 0 , ""); 
			}
			return insertTable.indexOf(fuluTajia);
		}
		
		setEchoFulu()
		{
			const fuluPai = this.findFuluPai();
			
			const paizi = this._paizi.filter (p => p !== fuluPai);
			
			const fuluTajia = fuluPai.fuluTajia();
			const retPaizi = [];
			const isDaminggang = this._paizi.length === 4
			
			const horizontal = this.fuluHorizontal(fuluTajia);
			
			const retFuluPai = this.realFuluPai(fuluPai);
			retFuluPai.setHorizontal(horizontal) ;
			
			const insert = this.findInsertIndex(fuluTajia,isDaminggang);
			
			retPaizi[insert] = retFuluPai;
			
			let is = 0;
			for(let i = 0; i < this._paizi.length ; i++)
			{
				if(!retPaizi[i] && paizi[is])
				{
					retPaizi[i] = this.realFuluPai(paizi[is]);
					retPaizi[i].setTajiaHidden(false);
					is++;
				}
			}
			
			return retPaizi;
		}
		
		setEchoFuluAngang()
		{
			const retPaizi = this._paizi;
			retPaizi.forEach(p => 
			{
				p.setTajiaHidden(false);
				p.setNeedSpacing(false);
			});
			retPaizi[0].setHidden(true);
			retPaizi[3].setHidden(true);
			return retPaizi;
		}
		setEchoFuluJiagang()
		{
			const fuluPai = this.findFuluPai();
			const jiagangPai = this.findJiagangPai();
			const paizi = this._paizi.filter (p => p !== fuluPai && p!== jiagangPai);
			const fuluTajia = fuluPai.fuluTajia();
			const retPaizi = [];
			
			
			const insert = this.findInsertIndex(fuluTajia,false);
			
			const retFuluPai = this.realFuluPai(fuluPai);
			const retJiagangPai = (jiagangPai);
			retPaizi[insert] = retFuluPai;
			retPaizi[insert + 1] = retJiagangPai;
			
			const horizontal = this.fuluHorizontal(fuluTajia);
			
			retFuluPai.setHorizontal(horizontal) ;
			retJiagangPai.setHorizontal(horizontal) ;
			
			retFuluPai.setPosition("jiagangBottom");
			retJiagangPai.setPosition("jiagangTop");
			
			let is = 0;
			for(let i = 0; i < 4 ; i++)
			{
				if(!retPaizi[i] && paizi[is])
				{
					retPaizi[i] = this.realFuluPai(paizi[is]);
					is++;
				}
			}
			return retPaizi;
		}
		
		
		
		includeZimo()
		{
			return this._paizi.some(pai => pai.isZimoPai())
		}
		
		includeRong()
		{
			return this._paizi.some(pai => pai.isRongPai())
		}
		
		includeHele()
		{
			return this._paizi.some(pai => pai.isHelePai())
		}
		
		checkShunzi()
		{
			return this._paizi.every(function( p , i)
			{
				return (p && p.type() !== 3 && p.code() === this._paizi[0].code() + i );
			},this);
		}
		
		checkSame()
		{
			return this._paizi.every(p => 
			{
				return (p && p.code() === this._paizi[0].code() );
			},this);
		}
		
		isKezi()
		{
			return this._kezi;
		}
		isShunzi()
		{
			return this._shunzi;
			
		}
		
		value()
		{
			return this._paizi.map(p => p.value());
		}
		value3()
		{
			return this.deleteGangzi(this.value());
		}
		
		type()
		{
			return this._paizi[0].type();
		}
		
		isGangzi()
		{
			return (this.isKezi() && this._paizi.length === 4);
		}
		paizi()
		{
			return this._paizi;
		}
		mianziCode()
		{
			return this._paizi.map(p => p.code());
		}
		mianziCode3()
		{
			return this.deleteGangzi(this.mianziCode());
		}
		statusCode()
		{
			return this._paizi[0].code();
		}
		
		keziCode()
		{
			if(!this.isKezi()) return 0;
			return this.statusCode();
		}
		
		isBian()
		{
			return (this.isShunzi() && (this._paizi[0].value() === 1 || this._paizi[2].value() === 9));
		}
		isAllLaotou()
		{
			return (this._paizi.every(p => p.isLaotou()));
		}
		
		isAllYaojiu()
		{
			return (this._paizi.every(p => p.isYaojiu()));
		}
		isAllZipai()
		{
			return (this._paizi.every(p => p.isZipai()));
		}
		isAllShupai()
		{
			return (this._paizi.every(p => p.isShupai()));
		}
		isAllFeng()
		{
			return this._paizi[0].isFeng();
		}
		isAllSanyuan()
		{
			return this._paizi[0].isSanyuan();
		}
		isAnke()
		{
			return (this.isKezi() && !this.isQiuren() && !this.includeRong());
		}
		isMingke()
		{
			return (this.isKezi() && (this.isQiuren() || this.includeRong()));
		}
		
		isAllLianfeng(battler)
		{
			return (this.isAllMenfeng(battler) && this.isAllQuanfeng());
		}
		isAllMenfeng(battler)
		{
			
			return (this.isKezi() && this._paizi[0].isMenfengPai(battler));
		}
		
		isAllQuanfeng()
		{
			
			return (this.isKezi() && this._paizi[0].isQuanfengPai());
		}
		
		isAllYipai(battler)
		{
			return (this.isKezi() && this._paizi[0].isYipai(battler)); 
		}
		
		
		
		isQianzhangHele()
		{
			return this._paizi[1].isHelePai();
		}
		isBianzhangHele()
		{
			if(!this.isBian()) return false;
			if(this._paizi[0].value() === 1)
			{
				return this._paizi[2].isHelePai();
			}
			else
			{
				return this._paizi[0].isHelePai();
			}
		}
		
		isLiangmianHele()
		{
			return(this.isShunzi() && !this.isBianzhangHele() && 
			(this._paizi[0].isHelePai() || this._paizi[2].isHelePai())
			);
		}
		
		isShuangpengHele()
		{
			return (this.isKezi()
				&& this._paizi.some(p => p.isHelePai())
			); 
		}
		
		deleteGangzi(array)
		{
			const t = [...array];
			t.splice(3);
			return t;
		}
		
		equal(mianzi)
		{
			return this.equalCode(mianzi.mianziCode());
		}
		
		equalValue(mianzi)
		{
			if(this.isAllZipai() || mianzi.isAllZipai())
			{
				return false;
			}
			return this.equalCodeValue( mianzi.value());
		}
		
		equalCode(codeArray)
		{
			const code1 = this.deleteGangzi(this.mianziCode());
			const code2 = this.deleteGangzi(codeArray);
			if(code1.length !== code2.length)
			{
				return false;
			}
			return code1.every(function (code , i){return (code === code2[i])});
		}
		
		equalCodeValue(valueArray)
		{
			const value1 = this.deleteGangzi(this.value());
			const value2 = this.deleteGangzi(valueArray);
			if(value1.length !== value2.length)
			{
				return false;
			}
			return value1.every(function (value , i){return (value === value2[i])});
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	class ParseHele
	{
		constructor()
		{
			this.clear();
		}
		clear()
		{
			this._tehai = [];
			
			
			this._tempMianzi = [];
			this._helexings = [];
		}
		
		setTehai(tehai)
		{
			this.clear();
			this._tehai = this.lipai(tehai);
			
		}
		
		lipai(tehai)
		{
			
			return [...tehai].sort(function(a,b){return a.code() - b.code()});
		}
		
		
		shunziLipai(tehai)
		{
			const temp = this.lipai(tehai);
			const length = tehai.length;
			const ret = [];
			let prevValue = 0;
			for(let i = 0; i < length ; i++)
			{
				let index = temp.findIndex(p => p.code() === prevValue + 1);
				if(index === -1) index = 0;
				ret.push(temp[index]);
				prevValue = temp[index].code();
				temp.splice(index,1);
			}
			return ret;
		}
		
		exceptJiangtou(jiangtou,tehai)
		{
			return [...tehai].filter(pai => !jiangtou.includes(pai));
		}
		
		
		
		
		findJiangtou()
		{
			const tehai = this._tehai;
			const tempJiangtou = [];
			const length = tehai.length;
			for(let i = 0; i < length  -1; i++)
			{
				const cp = tehai[i];
				const np = tehai[i + 1];
				const jiangtou = new Mianzi([cp,np],"duizi");
				if(jiangtou.isEnabled())
				{
					tempJiangtou.push(jiangtou);
				}
			}
			return tempJiangtou;
		}
		
		parseMianzi()
		{
			
			for(const tempJiangtou of this.findJiangtou())
			{
				this._tempMianzi = [];
				const paizi = this.exceptJiangtou(tempJiangtou.paizi(),this._tehai);
				
				this.allocateMianzi(paizi,[],tempJiangtou.keziCode());
				for(let decisionMianzi of this._tempMianzi)
				{
					this._helexings.push({"type" : "normal" , "jiangtou": tempJiangtou , "mianzi" : [...decisionMianzi]});
				}
			}
		}
		
		filterMianzi(paizi,type)
		{
			const lower = Math.min(...paizi.map(p => p.code()));
			if(type === "shunzi" && lower > 30) return false;
			
			const codeArray = (type === "shunzi") ? [lower , lower + 1 , lower + 2 ] : [lower,lower,lower];
			
			return this.discoverMianzi(paizi,codeArray);
			
			
		}
		discoverMianzi(paizi,codeArray)
		{
			let temp = [...paizi];
			const mianzi = [];
			for(let i = 0; i < 3 ; i++)
			{
				const index = temp.findIndex(p => (p.code() === codeArray[i]));
				
				if(index === -1) return false;
				const pai = temp.splice(index,1);
				
				mianzi.push(pai[0]);
			}
			return {"mianzi" : mianzi , "paizi" : temp}
		}
		
		
		
		allocateMianzi(paizi,temp,jiangtouCode)
		{
			if(!paizi[0])
			{
				this._tempMianzi.push(temp);
			}
			else
			{
				
				for(const type of ["shunzi","kezi"])
				{
					const fil = this.filterMianzi(paizi,type);
					if(fil)
					{
						const mianzi = new Mianzi(fil.mianzi);
						if(mianzi.isKezi() && mianzi.keziCode() === jiangtouCode) continue;
						this.allocateMianzi(fil.paizi , [...temp,mianzi] , jiangtouCode);
					}
				}
				
			}
			
		}
		
		parseQiduizi()
		{
			const tehai = this.lipai(this._tehai);
			if(tehai.length !== 14) return ;
			const resultCode = [];
			const duiziArray = [];
			
			for(let i = 0; i < 7 ; i++)
			{
				const duizi1 = tehai[i * 2];
				const duizi2 = tehai[i * 2 + 1];
				const duizi = new Mianzi([duizi1 , duizi2],"duizi");
				
				if(!duizi.isEnabled() || 
					(!$gameMajiangSystem.settings.siqidui && resultCode.includes(duizi1.code()))
				)
				{
					return ;
				}
				resultCode.push(duizi1.code());
				duiziArray.push(duizi);
			}
			
			this._helexings.push({"type" : "qiduizi" , "duizi" : duiziArray});
		}
		
		parseGuoshiwushuang()
		{
			const tehai = this._tehai;
			if(tehai.length !== 14) return ;
			const resultCode = tehai.map(p => p.code());
			const allYaojiuCode = [1,9,11,19,21,29,31,32,33,34,35,36,37];
			
			const tempJiangtou = this.findJiangtou();
			if(tempJiangtou.length !== 1) return ;
			if(!allYaojiuCode.every(code => resultCode.includes(code))) return ;
			
			
			this._helexings.push({"type" : "guoshiwushuang" , "jiangtou" : tempJiangtou[0] , "paizi" : tehai});
		}
		
		helexings()
		{
			this._helexings = [];
			this.parseMianzi();
			if($gameMajiangSystem.adoptQiduiziHele())
			{
				this.parseQiduizi();
			}
			if($gameMajiangSystem.adoptGuoshiHele())
			{
				this.parseGuoshiwushuang();
			}
			
			return this._helexings;
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	class FindYaku
	{
		constructor()
		{
			this.clear();
			this.setData();
		}
		
		clear()
		{
			this._type = "";
			this._jiangtou = [];
			this._mianzi = [];
			this._fulu = [];
			this._duizi = [];
			
			this._cache = {};
			
		}
		setup(helexing,fulu,battler)//jiangtou,mianzi,fulu,duizi,isQiduizi
		{
			this.clear();
			this._type = helexing.type;
			
			this._battler = battler;
			
			this._jiangtou = helexing.jiangtou;
			this._mianzi = helexing.mianzi || [];
			this._fulu = fulu || [];
			this._duizi = helexing.duizi || [];
			this._paizi = helexing.paizi || [];
			
		}
		isNormal()
		{
			return (this._type === "normal");
		}
		
		isQiduizi()
		{
			return (this._type === "qiduizi");
		}
		isGuoshiwushuang()
		{
			return (this._type === "guoshiwushuang");
		}
		isMenqian()
		{
			if(this._cache.menqian === void 0)
			{
				this._cache.menqian = this._battler.isMenqian();
			}
			return this._cache.menqian;
		}
		
		allMianzi()
		{
			if(!this.isNormal())
			{
				return [];
			}
			return [...this._mianzi , ...this._fulu];
		}
		allPaizi()
		{
			if(this._cache.allPaizi === void 0)
			{
				if(this.isQiduizi())
				{
					this._cache.allPaizi = this._duizi.map(m => m.paizi()).flat();
				}
				else if(this.isGuoshiwushuang())
				{
					this._cache.allPaizi = [...this._paizi];
				}
				else
				{
					this._cache.allPaizi = [].concat(...this._fulu.map(m => m.paizi()), ...this._mianzi.map(m => m.paizi()), this._jiangtou.paizi());
				}
				this._cache.allPaizi.sort((a,b) => a.paiId() - b.paiId());
			}
			return this._cache.allPaizi;
		}
		isDanqi()
		{
			return (this.isQiduizi() || this._jiangtou.includeHele()) ;
		}
		
		
		
		
		
		
		
		
		
		
		check_guoshiwushuang()
		{
			return (this.isGuoshiwushuang());
			
		}
		check_chunzhengGuoshi()
		{
			return (this.isGuoshiwushuang() && this.isDanqi());
		}
			
		check_qiduizi()
		{
			return this.isQiduizi()
		}
		
		isDuanyaojiu()
		{
			if(this._cache.duanyaojiu === void 0)
			{
				this._cache.duanyaojiu = this.allPaizi().every(pai => !pai.isYaojiu());
			}
			return this._cache.duanyaojiu;
		}
		
		check_duanyaojiu()
		{
			
			if(!$gameMajiangSystem.gamerule.kuiduan && !this.isMenqian())
			{
				return false;
			}
			
			return this.isDuanyaojiu();
		}
		
		check_pinghe()
		{
			if(!this.isNormal()) return false;
			
			return (this.isMenqian()
			  && !this._jiangtou.isAllYipai(this._battler)
			  && !this.isDanqi()
			  && this.allMianzi().every(mianzi => 
				{
					return (mianzi.isShunzi() && (!mianzi.includeHele() || mianzi.isLiangmianHele()));
				},this)
			)
		}
		
		countBeikou()
		{
			if(!this.isNormal())
			{
				return 0;
			}
			if(this._cache.beikou === void 0)
			{
				const mianziCode = this.allMianzi().filter(m => m.isShunzi()).map(m => m.mianziCode());
				const countMianzi = mianziCode.map(m => mianziCode.reduce((r,c) => r + ((c.equals(m)) ? 1 : 0),0));
				
				const count = countMianzi.reduce((r,c) => r + ((c > 1)? 1 : 0) , 0);
				
				const beikou = Math.floor(count / 2);
				const shun = Math.max(...countMianzi);
				
				this._cache.beikou = {"beikou" : beikou , "shun" : shun};
			}
			return this._cache.beikou;
		}
		
		check_yibeikou()
		{
			if(!this.isMenqian()) return false;
			return (this.countBeikou().beikou === 1);
		}
		check_liangbeikou()
		{
			if(!this.isMenqian()) return false;
			return (this.countBeikou().beikou === 2);
		}
		
		check_yiseSanshun()
		{
			return (this.countBeikou().shun === 3);
		}
		check_yiseSishun()
		{
			return (this.countBeikou().shun === 4);
		}
		
		countLianke()
		{
			if(!this.isNormal())
			{
				return 0;
			}
			if(this._cache.lianke === void 0)
			{
				const all = this.allMianzi().filter(m => m.isKezi() && m.isAllShupai());
				if(all.length < 3)
				{
					this._cache.lianke = 0;
					return 0;
				}
				
				let allCode = all.map(m => m.keziCode());
				
				allCode.sort((a,b) => a - b);
				
				const result = [allCode];
				
				if(allCode.length === 4)
				{
					result.push(allCode.slice(1))
					result.push(allCode.slice(0,-1))
				}
				for(const code of result)
				{
					if(code.every((c,i,a) => (!a[i + 1] || c + 1 === (a[i + 1]))))
					{
						this._cache.lianke = code.length;
						break;
					}
				}
			}
			return this._cache.lianke;
		}
		check_sanlianke()
		{
			return (this.countLianke() === 3);
		}
		
		check_silianke()
		{
			return (this.countLianke() === 4);
		}
		
		check_duiduihe()
		{
			if(!this.isNormal()) return false;
			
			return (this.allMianzi().every(mianzi => mianzi.isKezi() , this));
		}
		
		check_sananke()
		{
			
			return (this.countAnke() === 3);
		}
		
		countAnke()
		{
			
			if(!this.isNormal())
			{
				return 0;
			}
			if(this._cache.anke === void 0)
			{
				let count = 0;
				for(const mianzi of this.allMianzi())
				{
					
					if(mianzi.isAnke())
					{
						count++;
					}
				}
				this._cache.anke = count;
			}
			return this._cache.anke;
		}
		
		check_sianke()
		{
			return (this.countAnke() === 4);
		}
		check_siankeDanqi()
		{
			return (this.countAnke() === 4 && this.isDanqi());
		}
			
		countGangzi()
		{
			if(!this.isNormal())
			{
				return 0;
			}
			if(this._cache.gangzi === void 0)
			{
				let count = 0;
				for(const fulu of this._fulu)
				{
					if(fulu.isGangzi())
					{
						count++;
					}
				}
				this._cache.gangzi = count;
			}
			return this._cache.gangzi;
		}
		
		check_sangangzi()
		{
			return (this.countGangzi() === 3);
		}
		
		check_sigangzi()
		{
			return (this.countGangzi() === 4);
		}
		
		countSanse()
		{
			if(!this.isNormal())
			{
				return false;
			}
			if(this._cache.sanse === void 0)
			{
				this._cache.sanse = false;
				const allMianzi = this.allMianzi();
				const valueArray = allMianzi.map(m => m.value3());
				let correct;
				
				for(const va of valueArray)
				{
					const count = allMianzi.reduce(function(r,c)
					{
						return r + ((c.value3().equals(va) && c.isAllShupai()) ? 1 : 0);
					} , 0);
					
					if(count >= 3)
					{
						correct = va;
						break;
					}
				}
				
				if(correct)
				{
					const pt = [];
					for(const type of [0,10,20])
					{
						const mianzi = allMianzi.find(m => m.mianziCode3().equals(correct.map(v => v + type)));
						if(!mianzi) break;
						pt.push(mianzi);
					}
					if(pt.length === 3)
					{
						this._cache.sanse = (pt[0].isShunzi()) ? "shunzi" : "kezi";
					}
					
				}
			}
			return this._cache.sanse;
		}
		
		check_sanseTongshun()
		{
			return (this.countSanse() === "shunzi");
		}
		
		check_sanseTongke()
		{
			
			return (this.countSanse() === "kezi");
		}
		
		
		isQuandai()
		{
			if(!this.isNormal())
			{
				return false;
			}
			if(this._cache.quandai === void 0)
			{
				
				this._cache.quandai = (!this.isLaotou() && this.allMianzi().every(m => (m.isAllYaojiu() || m.isBian())) 
				&& !this.isZiyise()
				&& this._jiangtou.isAllYaojiu());
			}
			return this._cache.quandai;
		}
		
		isLaotou()
		{
			if(this.isGuoshiwushuang())
			{
				return false;
			}
			if(this._cache.laotou === void 0)
			{
				this._cache.laotou = (this.allPaizi().every(p => p.isYaojiu()) && !this.isZiyise());
			}
			return this._cache.laotou;
		}
		
		isHun()
		{
			if(this.isGuoshiwushuang())
			{
				return false;
			}
			if(this._cache.hun === void 0)
			{
				this._cache.hun = (this.allPaizi().some(p => p.isZipai()) && !this.isZiyise());
			}
			return this._cache.hun;
		}
		
		check_hunquandai()
		{
			return (this.isHun() && this.isQuandai());
		}
		
		check_chunquandai()
		{
			return (!this.isHun() && this.isQuandai());
		}
		
		check_hunlaotou()
		{
			return (this.isHun() && this.isLaotou());
		}
		
		check_qinglaotou()
		{
			return (!this.isHun() && this.isLaotou());
		}
		
		countSanyuan()
		{
			if(!this.isNormal())
			{
				return 0;
			}
			if(this._cache.sanyuan === void 0)
			{
				let count = 0;
				for(const mianzi of this.allMianzi())
				{
					if(mianzi.isAllSanyuan())
					{
						count++;
					}
				}
				this._cache.sanyuan = count;
			}
			return this._cache.sanyuan;
		}
		
		check_xiaosanyuan()
		{
			return ( this.countSanyuan() === 2 && this._jiangtou.isAllSanyuan());
		}
		
		check_dasanyuan()
		{
			return (this.countSanyuan() === 3);
		}
		
		check_yiqiTongguan()
		{
			if(!this.isNormal()) return false;
			const allMianzi = this.allMianzi();
			
			for(const color of [0,1,2])
			{
				if(	allMianzi.some(m => (m.equalCodeValue([1,2,3]) && m.type() === color))
				&&	allMianzi.some(m => (m.equalCodeValue([4,5,6]) && m.type() === color))
				&&	allMianzi.some(m => (m.equalCodeValue([7,8,9]) && m.type() === color))
				)
				{
					return true;
				}
			}
			return false;
		}
		
		isZiyise()
		{
			
			if(this._cache.ziyise === void 0)
			{
				this._cache.ziyise = this.allPaizi().every(p => p.isZipai());
			}
			
			return this._cache.ziyise;
		}
		
		check_ziyise()
		{
			return this.isZiyise();
		}
		check_daqixing()
		{
			return (this.isZiyise() && this.isQiduizi());
		}
		countFengpai()
		{
			if(!this.isNormal()) return 0;
			if(this._cache.fengpai === void 0)
			{
				let count = 0;
				for(const mianzi of this.allMianzi())
				{
					if(mianzi.isAllFeng())
					{
						count++;
					}
				}
				this._cache.fengpai = count;
			}
			
			return this._cache.fengpai;
		}
		
		check_xiaosixi()
		{
			return ( this.countFengpai() === 3 && this._jiangtou.isAllFeng());
		}
		check_dasixi()
		{
			return (this.countFengpai() === 4);
		}
		check_sanfengke()
		{
			return (this.countFengpai() === 3);
		}
		check_kefengSanke()
		{
			const battler = this._battler;
			return (this.countFengpai() >= 3 && this.allMianzi().filter(p => p.isFeng()).every(m => !m.isAllYipai(battler)));
		}
		
		isShupaiYise()
		{
			if(this.isGuoshiwushuang())
			{
				return false;
			}
			if(this._cache.shupaiYise === void 0)
			{
				const paizi = this.allPaizi().filter(p => p.isShupai());
				if(paizi.length > 0 )
				{
					const color = paizi[0].type();
					this._cache.shupaiYise = (paizi.every(p => p.type() === color));
				}
				else
				{
					this._cache.shupaiYise = false;
				}
			}
			return this._cache.shupaiYise;
		}
		
		isQingyise()
		{
			if(this._cache.qingyise === void 0)
			{
				this._cache.qingyise = (!this.isHun() && this.isShupaiYise());
			}
			return this._cache.qingyise;
		}
		
		check_hunyise()
		{
			return (this.isHun() && this.isShupaiYise())
		}
		
		check_qingyise()
		{
			return this.isQingyise();
		}
		
		isJiulianBaodeng()
		{
			if(this._cache.jiulianBaodeng === void 0)
			{
				if(this.isNormal() && 
					this._fulu.length === 0 && 
					this.isQingyise() && 
					this.jiulianPaizi(this.allPaizi())
				)
				{
					const paizi = this.allPaizi().filter(p => !p.isHelePai());
					this._cache.jiulianBaodeng = (this.jiulianPaizi(paizi)) ? 2 : 1;
				}
				else
				{
					this._cache.jiulianBaodeng = 0;
				}
			}
			
			return this._cache.jiulianBaodeng;
			
		}
		
		jiulianPaizi(paizi)
		{
			let count = new Array(10).fill(0);
			count[0] = 1;
			for(const pai of paizi)
			{
				count[pai.value()]++;
			}
			
			return (count.every(c => c >= 1)
				&&	count[1] >= 3 && count[9] >= 3
			);
		}
		
		
		check_jiulianBaodeng()
		{
			return (this.isJiulianBaodeng() >= 1) ;
		};
		check_chunzhengJiulian()
		{
			return (this.isJiulianBaodeng() === 2) ;
		}
		
		check_qinghaihu()
		{
			return (this.isMenqian()
				&& this.isQingyise()
				&& this.isQuandai()
				&& (this.countBeikou().beikou === 2)
			);
		}
		
		check_luyise()
		{
			const all = this.allPaizi();
			return all.every(pai => pai.constituteLuyise());
		}
		
		check_dachelun()
		{
			if(!this.isNormal() || !this.isMenqian()) return false;
			
			const code = this.allPaizi().map(p => p.code());
			
			return code.equals([22,22,23,23,24,24,25,25,26,26,27,27,28,28]);
		}
		
		
		check_heiyise()
		{
			const all = this.allPaizi();
			const code = [22,24,28,31,32,33,34];
			
			return all.every(pai => code.includes(pai.code()));
		}
		check_hongkongque()
		{
			const all = this.allPaizi();
			const code = [11,15,17,19,37];
			
			return all.every(pai => code.includes(pai.code()));
		};
		check_shierLuotai()
		{
			if(!this.isNormal()) return false;
			return (!this.isZimo() && this._mianzi.length === 0 && this._fulu.every(m => m.isQiuren()));
		};
		
		check_wumenqi()
		{
			if(!this.isNormal()) return false;
			const result = [false,false,false,false,false];
			for(const mianzi of [...this.allMianzi() , this._jiangtou])
			{
				let type = mianzi.type();
				if(mianzi.isAllSanyuan()) type = 4;
				result[type] = true;
				
			}
			
			return result.every(v => v);
		};
		
		check_quandaiwu()
		{
			if(!this.isNormal()) return false;
			
			for(const mianzi of [...this.allMianzi() , this._jiangtou])
			{
				if(!mianzi.paizi().some(p => p.isShupai() && p.value() === 5))
				{
					return false;
				}
			}
			return true;
		};
		
		isQuanfeng()
		{
			if(this._cache.quanfeng === void 0)
			{
				this._cache.quanfeng = this.allMianzi().some(m => m.isAllQuanfeng());
			}
			return this._cache.quanfeng;
		}
		
		isMenfeng()
		{
			if(this._cache.menfeng === void 0)
			{
				this._cache.menfeng = this.allMianzi().some(m => m.isAllMenfeng(this._battler));
			}
			return this._cache.menfeng;
		}
		
		isLianfeng()
		{
			if(this._cache.lianfeng === void 0)
			{
				this._cache.lianfeng = this.allMianzi().some(m => m.isAllLianfeng(this._battler));
			}
			return this._cache.lianfeng;
		}
		
		check_quanfeng()
		{
			return this.isQuanfeng();
		}
		check_menfeng()
		{
			return this.isMenfeng();
		}
		check_lianfeng()
		{
			return (this.isLianfeng());
		}
		
		check_baiban()
		{
			return this.allMianzi().some(m => m.keziCode() === 35);
		}
		
		check_lufa()
		{
			return this.allMianzi().some(m => m.keziCode() === 36);
		}
		
		check_hongzhong()
		{
			return this.allMianzi().some(m => m.keziCode() === 37);
		}
		check_bei()
		{
			if(!MajiangManager.samma()) return false;
			return this.allMianzi().some(m => m.keziCode() === 34);
		}
		
		isZimo()
		{
			
			if(this._cache.zimo === void 0)
			{
				this._cache.zimo = !!this.allPaizi().find(p => p.isZimoPai());
			}
			return this._cache.zimo;
		}
		
		check_zimo()
		{
			return (this.isMenqian() && this.isZimo());
		}
		
		check_qianggang()
		{
			return this.allPaizi().some(p => p.isRongPai() && (p.isJiagangPai() || p.fuluType() === "angang"));
		}
		
		isLingshangKaihua()
		{
			if(this._cache.lingshangKaihua === void 0)
			{
				this._cache.lingshangKaihua = this.allPaizi().some(p => p.isHelePai() && p.isLingshangPai());
			}
			return this._cache.lingshangKaihua;
		}
		
		check_lingshangKaihua()
		{
			return this.isLingshangKaihua();
		}
		check_lizhi()
		{
			return this._battler.isLizhi();
		}
		check_doubleLizhi()
		{
			return this._battler.isDoubleLizhi();
		}
		check_yifa()
		{
			return ( this._battler.isLizhi() && this._battler.yifa());
		}
			
		isHaidi()
		{
			if(this._cache.haidi === void 0)
			{
				this._cache.haidi = ($gameMajiangPaishan.shengyu() === 0);
			}
			return this._cache.haidi;
		}
			
		check_haidiLaoyue()
		{
			return (this.isZimo() && this.isHaidi() && !this.isLingshangKaihua());
		}
		
		check_hediLaoyu()
		{
			return (!this.isZimo() && this.isHaidi());
		}
		
		
		
		check_tianhe()
		{
			return (this.isMenqian() && this._battler.yishun() && this.isZimo() && this._battler.isDealer());
		}
		check_dihe()
		{
			return (this.isMenqian() && this._battler.yishun() && this.isZimo() && !this._battler.isDealer());
		}
		check_renhe()
		{
			return (this.isMenqian() && this._battler.yishun() && !this.isZimo() && !this._battler.isDealer());
		}
		
		check_balianzhuang()
		{
			return (this._battler.lianHele() >= 8 );
		}
		
		
		basicData()
		{
			return [
				{"name" : "tianhe" , 		"downward" : []},
				{"name" : "dihe" , 			"downward" : []},
				{"name" : "renhe" , 		"downward" : []},
				
				{"name" : "balianzhuang" , 	"downward" : []},
				
				{"name" : "chunzhengGuoshi" ,"downward" : ["guoshiwushuang"]},
				{"name" : "guoshiwushuang" , "downward" : []},
				
				{"name" : "chunzhengJiulian" ,"downward" : ["jiulianBaodeng"]},
				{"name" : "jiulianBaodeng" ,"downward" : []},
				
				{"name" : "daqixing" , 		"downward" : ["ziyise"]},
				{"name" : "ziyise" , 		"downward" : []},
				
				{"name" : "luyise" , 		"downward" : []},
				{"name" : "heiyise" , 		"downward" : []},
				{"name" : "hongkongque" , 	"downward" : []},
				
				{"name" : "dachelun" ,		"downward" : []},
				{"name" : "qinghaihu" , 	"downward" : ["qingyise","chunquandai","liangbeikou"]},
				
				{"name" : "doubleLizhi" , 	"downward" : ["lizhi"]},
				{"name" : "lizhi" , 		"downward" : []},
				{"name" : "yifa" , 			"downward" : []},
				
				{"name" : "lingshangKaihua" , "downward" : []},
				{"name" : "zimo" , 			"downward" : []},
				
				{"name" : "haidiLaoyue" , 	"downward" : []},
				{"name" : "hediLaoyu" , 	"downward" : []},
				
				{"name" : "qianggang" , 	"downward" : []},
				
				{"name" : "lianfeng" , 		"downward" : ["quanfeng","menfeng"]},
				{"name" : "quanfeng" , 		"downward" : []},
				{"name" : "menfeng" , 		"downward" : []},
				
				
				{"name" : "baiban" , 		"downward" : []},
				{"name" : "lufa" , 			"downward" : []},
				{"name" : "hongzhong" , 	"downward" : []},
				{"name" : "bei" , 			"downward" : []},
				
				{"name" : "duanyaojiu" , 	"downward" : []},
				{"name" : "pinghe" , 		"downward" : []},
				
				{"name" : "duiduihe" , 		"downward" : []},
				{"name" : "qiduizi" , 		"downward" : []},
				
				{"name" : "yiqiTongguan" , 	"downward" : []},
				
				{"name" : "siankeDanqi" , 	"downward" : ["sianke","sananke"]},
				{"name" : "sianke" , 		"downward" : ["sananke"]},
				{"name" : "sananke" , 		"downward" : []},
				
				{"name" : "sigangzi" , 		"downward" : ["sangangzi"]},
				{"name" : "sangangzi" , 	"downward" : []},
				
				{"name" : "dasanyuan" ,		"downward" : ["xiaosanyuan"]},
				{"name" : "xiaosanyuan" ,	"downward" : []},
				
				{"name" : "dasixi" , 		"downward" : ["xiaosixi","kefengSanke","sanfengke"]},
				{"name" : "xiaosixi" , 		"downward" : ["kefengSanke","sanfengke"]},
				
				{"name" : "kefengSanke" , 	"downward" : ["sanfengke"]},
				{"name" : "sanfengke" , 	"downward" : []},
				
				{"name" : "qingyise" , 		"downward" : ["hunyise"]},
				{"name" : "hunyise" , 		"downward" : []},
				
				{"name" : "qinglaotou" , 	"downward" : ["hunlaotou","chunquandai"]},
				{"name" : "hunlaotou" , 	"downward" : ["hunquandai"]},
				
				{"name" : "chunquandai" , 	"downward" : ["hunquandai"]},
				{"name" : "hunquandai" , 	"downward" : []},
				
				{"name" : "sanseTongke" , 	"downward" : []},
				{"name" : "sanseTongshun" , "downward" : []},
				
				{"name" : "yiseSishun" , 	"downward" : ["yiseSanshun","yibeikou","liangbeikou"]},
				{"name" : "yiseSanshun" , 	"downward" : ["yibeikou","liangbeikou"]},
				
				{"name" : "liangbeikou" , 	"downward" : ["yibeikou"]},
				{"name" : "yibeikou" , 		"downward" : []},
				
				{"name" : "silianke" ,		"downward" : ["sanlianke"]},
				{"name" : "sanlianke" ,		"downward" : []},
				
				{"name" : "wumenqi" , 		"downward" : []},
				
				
				
				
				
				{"name" : "shierLuotai" ,	"downward" : []}
			];
		}
		
		
		setData()
		{
			const basic = this.basicData();
			
			this._yakuData = basic.map(data =>
			{
				data.yakuData = $gameMajiangSystem.yakuData[data.name];
				return data;
			})
		}
		
		resultYaku(dataArray)
		{
			const result = [];
			const exclude = [];
			for(const data of dataArray)
			{
				if(exclude.includes(data.name)) { continue};
				const methodName = "check_" + data.name;
				if(this[methodName]())
				{
					result.push(data.name);
					exclude.push(...data.downward)
				}
			}
			return result;
		}
			
		result()
		{
			
			const allYakuData = this._yakuData.filter(data => data.yakuData.adopt);
			const result = [];
			if($gameMajiangSystem.gamerule.aotenjo)
			{
				const yakuData = allYakuData;
				return this.resultYaku(yakuData);
			}
			else
			{
			
				const yakumanData = allYakuData.filter(data => data.yakuData.fan < 0);
				
				
				const yakumanResult = this.resultYaku(yakumanData);
				if(yakumanResult.length > 0) return yakumanResult;
				
				const yakuData = allYakuData.filter(data => data.yakuData.fan > 0);
				return this.resultYaku(yakuData);
			}
			
		}
	}
	
	
	class PointResult
	{
		constructor()
		{
			this._findYaku = new FindYaku();
			
		}
		
		lowest() //和了り最低翻
		{
			return MajiangManager.fanshiba();
		}
		
		
		
		
		
		setup(helexings,fulu,allTehai,battler,isRong)
		{
			this._isRong = isRong;
			this._battler = battler;
			this._helexings = helexings;
			this._fulu = fulu;
			
			this._allTehai = allTehai;
			this._countDora = this.countDora(allTehai,fulu);
			const helepai = allTehai.find(p => p.isHelePai());
			this._helepai = helepai;
			this.setSuspect(allTehai,helepai);
		}
		setSuspect(allTehai,helepai)
		{
			this._suspect = [];
			const helepaiCode = helepai?.code() || 0;
			for(const pai of allTehai)
			{
				if(helepaiCode === pai.code() || !helepai)
				{
					this._suspect.push(pai);
				}
			}
			
		}
		clearHelepai()
		{
			this._allTehai.forEach(p => {p.setZimoPai(false); p.setRongPai(false)});
		}
		setHelepai(helepai)
		{
			(this._isRong) ? helepai.setRongPai(true) : helepai.setZimoPai(true);
		}
		undoHelepai()
		{
			this.clearHelepai();
			const helepai = this._helepai;
			if(helepai)
			{
				this.setHelepai(helepai);
			}
		}
		countDora(tehai,fulu)
		{
			const all = tehai.concat(...fulu.map(m => m.paizi()));
			
			let count = 0;
			for(const pai of all)
			{
				count += pai.dora();
				if(this._battler.isLizhi() && this._battler.isHele())
				{
					count += pai.uradora();
				}
			}
			return count;
		}
		result()
		{
			const results = [];
			
			for(const helepai of this._suspect)
			{
				this.clearHelepai();
				
				this.setHelepai(helepai);
				
				for(const helexing of this._helexings)
				{
					this._findYaku.setup(helexing,this._fulu,this._battler);
					const yaku = this._findYaku.result();
					const heleYaku = this.resultYaku(this.heleYaku(yaku),false);
					
					if(this.countFan(heleYaku.yakuResult) >= this.lowest() || heleYaku.yakuman)
					{
						const ret = {};
						ret.yaku = this.resultYaku(yaku,true);
						ret.point = this.point(helexing,ret.yaku,false);
						
						results.push(ret);
					}
				}
			}
			this.undoHelepai();
			
			if(results.length === 0)
			{
				return {
						"yaku" : {"yakuman" : false , "yakuResult" : [] },
						"point":{"point" : 0 , "gainPoint" : 0},
				};
			}
			
			
			const result = this.findHighest(results);
			
			return result ;
		}
		heleYaku(yaku)
		{
			const notHeleYaku = ["balianzhuang"];
			return yaku.filter(y => !notHeleYaku.includes(y));
		}
		
		findHighest(results)
		{
			
			const highPoint = Math.max(...results.map(r => this.comparisonPoint(r.point),this));
			
			return results.find(r => this.comparisonPoint(r.point) === highPoint);
		}
		comparisonPoint(data)
		{
			let ret = data.gainPoint;
			if(data.yakuman)
			{
				ret *= 10;
			}
			else
			{
				if(data.fan) ret += data.fan;
				
				if(data.fu) ret += (data.fu / 1000);
			}
			
			return ret;
		}
		
		
		
		
		resultYaku(yaku,dora)
		{
			const ret = {"yakuman" : false , "yakuResult" : [] };
			
			for(const name of yaku)
			{
				const data = $gameMajiangSystem.yakuData[name];
				if(ret.yakuman === false && data.fan > 0)
				{
					
					const retFan = (data.fuluJian && !this._battler.isMenqian()) ? (data.fan  - 1) : data.fan;
					
					if(retFan > 0)
					{
						ret.yakuResult.push({"name" : name , "fan" : retFan});
					}
				}
				else if (data.fan < 0)
				{
					if($gameMajiangSystem.gamerule.aotenjo)
					{
						const retFan = (Math.abs(data.fan) * 13) - ((data.fuluJian && !this._battler.isMenqian()) ?  - 1 : 0);
						ret.yakuResult.push({"name" : name , "fan" : retFan});
					}
					else
					{
						ret.yakuman = true;
						ret.yakuResult.push({"name" : name , "fan" : Math.abs(data.fan)});
					}
				}
			
			}
			if(dora && !ret.yakuman && this._countDora > 0)
			{
				ret.yakuResult.push({"name" : "dora", "fan" : this._countDora});
			}
			return ret;
		}
		point(helexing,yaku,last)
		{
			let ret = {"point" : 0 , "gainPoint" : 0}
			const fan = this.countFan(yaku.yakuResult);
			if(yaku.yakuman) return this.pointMangan("yakuman",fan);
			
			if(fan === 0 ) return ret;
			
			
			
			const fu = this.countFu(helexing,yaku.yakuResult);
			
			const mangan = this.countMangan(fan,fu);
			
			
			if(mangan) 
			{
				ret = this.pointMangan(mangan,0);
			}
			else
			{
				ret = this.pointFanFu(fan,fu);
			}
			ret.fan = fan;
			
			ret.fu = fu;
			
			return ret;
		}
		
		countMangan(fan,fu)
		{
			if($gameMajiangSystem.gamerule.aotenjo)
			{
				return "";
			}
			if(fan >= 13)
			{
				return "kazoeYakuman";
			}
			else if (fan >= 11)
			{
				return "sambaiman";
			}
			else if (fan >= 8)
			{
				return "baiman";
			}
			else if (fan >= 6)
			{
				return "haneman";
			}
			else if (fan >= 5 ||
				( fan === 4 && fu > 30 ) || 
				( fan === 3 && fu > 60 )
			)
			{
				return "mangan";
			}
			
			else
			{
				return "";
			}
			
		}
		
		countFan(yaku)
		{
			
			return yaku.reduce(function (a , c) { return a + c.fan} , 0);
		}
		
		pointMangan(mangan , yakuman)
		{
			const maxYakuman = $gameMajiangSystem.settings.maxYakuman;
			if(maxYakuman !== 0)
			{
				yakuman = Math.min(maxYakuman , yakuman);
			}
			let base = 0;
			let point = 0;
			switch(mangan)
			{
				case "mangan" : base = 2000; break; 
				case "haneman" : base = 3000; break; 
				case "baiman" : base = 4000; break; 
				case "sambaiman" : base = 6000; break; 
				case "yakuman" : 
				case "kazoeYakuman" : base = 8000; break; 
			}
			if(mangan === "yakuman" && yakuman > 0)
			{
				base *= yakuman;
			}
			
			if(this._isRong || MajiangManager.duo())
			{
				point = (this._battler.isDealer()) ? base * 6 : base * 4;
			}
			else
			{
				point = (this._battler.isDealer()) ? [base * 2] : [base , base * 2];
			}
			
			return {"mangan" : mangan , "yakuman" : yakuman ,  "point" : point ,"gainPoint" : this.gainPoint(point)};
		}
		gainPoint(point)
		{
			if(!Array.isArray(point)) return point;
			if(MajiangManager.samma())
			{
				if(point.length === 1)
				{
					return point[0] * 2;
				}
				else
				{
					return (point[0] * 1) + point[1];
				}
			}
			else
			{
				if(point.length === 1)
				{
					return point[0] * 3;
				}
				else
				{
					return (point[0] * 2) + point[1];
				}
			}
		}
		
		ceilPoint(value,keta)
		{
			return Math.ceil(value / keta) * keta;
		}
		
		pointFanFu(fan,fu)
		{
			const dealer = this._battler.isDealer();
			const rong = this._isRong;
			const base = fu * (2 ** (fan + 2));
			const ret = {};
			if(rong || MajiangManager.duo())
			{
				ret.point = base * ((dealer) ? 6 : 4);
				ret.point = this.ceilPoint(ret.point,100);
			}
			else
			{
				ret.point = (dealer) ? [base * 2] : [base , base * 2];
				ret.point = ret.point.map(v => this.ceilPoint(v,100));
			}
			
			ret.gainPoint = this.gainPoint(ret.point);
			
			return ret;
		}
		
		countFu(helexing,yaku)
		{
			if($gameMajiangSystem.gamerule.funashi)
			{
				return 30;
			}
			if(helexing.type === "qiduizi")
			{
				return 25;
			}
			if(helexing.type === "guoshiwushuang") 
			{
				return (this._isRong) ? $gameMajiangSystem.settings.guoshiFuRong : $gameMajiangSystem.settings.guoshiFuZimo;
			}
			
			
			const menqian = this._battler.isMenqian();
			let fu = 20;
			if(!this._isRong && yaku.some(y => y.name == "pinghe"))
			{
				return fu;
			}
			if(!this._isRong) fu += 2;
			if(this._isRong && menqian) fu += 10;
			
			const allMianzi = helexing.mianzi.concat(...this._fulu);
			
			for(const mianzi of allMianzi)
			{
				if(!mianzi.isKezi()) continue;
				let add = 2;
				if(mianzi.isAnke())
				{
					add *= 2;
				}
				
				if(mianzi.isGangzi())
				{
					add *= 4;
				}
				
				if(mianzi.isAllYaojiu())
				{
					add *= 2;
				}
				fu += add;
			}
			
			if(helexing.jiangtou.isAllLianfeng(this._battler))
			{
				fu += $gameMajiangSystem.settings.liangfengFu;
			}
			else if(helexing.jiangtou.isAllYipai(this._battler))
			{
				fu += 2;
			}
			
			if(!helexing.mianzi.some(m => m.isLiangmianHele() || m.isShuangpengHele()))
			{
				fu += 2;
			}
			fu = Math.max(fu,30);
			
			return Math.ceil(fu / 10) * 10;
		}
		
	}
	
	
	
	class CountXiangting
	{
		constructor()
		{
			this.setCondition();
		}
		
		clear()
		{
			this._result = {};
			this._tehai = [];
			this._length = 0;
			this._tempMianzi = [];
			this._result = null;
		}
		
		setCondition()
		{
			this._siqidui = $gameMajiangSystem.settings.siqidui;
			this._adoptQiduizi = $gameMajiangSystem.adoptQiduiziHele();
			this._adoptGuoshi = $gameMajiangSystem.adoptGuoshiHele();
		}
		
		setTehai(tehai)
		{
			this.clear();
			this._tehai = [...tehai].sort(function(a,b){return a - b});
			this._length = this._tehai.length;
		}
		
		
		
		
		maxCount()
		{
			switch(this._length)
			{
				case 14 : 
				case 13 : return 4;
				case 11 : 
				case 10 : return 3;
				case 8 : 
				case 7 : return 2;
				case 5 : 
				case 4 : return 1;
				case 2 : 
				case 1 : return 0;
			}
			return Infinity;
		}
		
		exceptJiangtou(jiangtou,tehai)
		{
			return [...tehai].filter(pai => !jiangtou.includes(pai));
		}
		
		ex(array,valueArray)
		{
			let success = true;
			for(const value of valueArray)
			{
				const index = array.findIndex(v => v === value);
				if(index !== -1)
				{
					array.splice(index,1);
				}
				else
				{
					success = false;
				}
			}
			return success;
		}
		
		
		xiangtingNormal()
		{
			
			
			
			const paizi = this._tehai;
			const tPaizi = this.explode(paizi);
			this._tempMianzi = [];
			let temp = [];
			
			const sizhang = this.findSizhang(paizi);
			
			for(let i = 0; i < 4 ; i++)
			{
				this._tempMianzi[i] = [];
				this.allocateMianzi(tPaizi[i],[],i,this.maxCount() + 1,[],sizhang,false);
				
				const fm = this.filterDazi(this._tempMianzi[i])
				
				if(temp.length === 0)
				{
					for(const d of fm)
					{
						temp.push(d);
					}
				}
				else if (this._tempMianzi[i].length)
				{
					const n = [];
					const length = temp.length;
					for(let j = 0; j < length ; j++)
					{
						for(const d of fm)
						{
							n.push(temp[j].concat(d));
						}
					}
					temp = n;
				}
			}
			
			
			return (this.maxCount() * 2) - this.totalDaziPoint(temp);
		}
		
		findSizhang(codeArray)
		{
			const ret = codeArray.filter(c => codeArray.filter(v => v === c).length === 4);
			return (ret.length === 0) ? null : ret;
		}
		
		filterDazi(temp)
		{
			if(temp.length === 0)
			{
				return [];
			}
			const length = temp.length;
			const max = [[0],[0]];
			const pd = [[],[]];
			for(let i = 0; i < length; i++)
			{
				let point0 = 0;
				let point1 = 1;
				for(const m of temp[i])
				{
					if(m === 3)
					{
						point0 += 10;
						point1 += 2;
					}
					else if(m === 2)
					{
						point0 += 1.5;
						point1 += 1.5;
					}
					else 
					{
						point0 += 1;
						point1 += 1;
					}
					max[0] = Math.max(max[0],point0);
					max[1] = Math.max(max[1],point1);
					pd[0][i] = point0;
					pd[1][i] = point1;
				}
			}
			const ret = [];
				
			const count = (max[0] === max[1]) ? 1 : 2;
			
			for(let j = 0; j < count; j++)
			{
				const index = pd[j].findIndex(v => v === max[j]);
				const d = temp[index];
				ret.push(d);
			}
			
			return ret;
		}
		totalDaziPoint(temp)
		{
			let maxPoint = 0;
			for(const t of temp)
			{
				const point = this.totalPoint(t);
				maxPoint = Math.max(point,maxPoint);
			}
			return maxPoint;
		}
		
		
		totalPoint(temp)
		{
			let point = 0;
			temp.sort((a,b) => b - a);
			const maxCount = this.maxCount();
			
			const skipSizhang = temp.some(v => v === -1);
			const duiziChecked = temp.some(v => v === 2);
			
			let count = maxCount;
			for(const value of temp)
			{
				if(count <= 0 || !value)
				{
					break;
				}
				else if(value === 3)
				{
					point += 2;
					count--;
				}
				else if (value === 1)
				{
					point += 1;
					count--;
				}
			}
			if(duiziChecked)
			{
				point++;
			}
			else if(skipSizhang)
			{
				point--;
			}
			
			return point;
			
			
		}
		
		explode(paizi)
		{
			const ret = [];
			for(let i = 0; i < 4 ; i++)
			{
				ret[i] = paizi.filter(c => {return ((i * 10) < c && c < (i + 1) * 10)});
			}
			return ret;
		}
		
		filterMianzi(paizi,kind)
		{
			const lower = Math.min(...paizi);
			return this.discoverMianzi(paizi,this.codeArray(kind,lower));
			
		}
		codeArray(kind,v)
		{
			switch(kind)
			{
				case "shunzi": return [v , v + 1 , v + 2];
				case "kezi": return [v , v , v];
				case "lianDazi": return [v , v + 1];
				case "qianDazi": return [v , v + 2];
				case "duizi": return [v , v];
			}
			alert(kind);
		}
		discoverMianzi(paizi,codeArray)
		{
			let temp = [...paizi];
			const mianzi = [];
			const length = codeArray.length;
			const s = this.ex(temp,codeArray);
			if(!s)
			{
				return false;
			}
			return {"paizi" : codeArray , "shengyu" : temp};
		}
		
		
		
		allocateMianzi(paizi,temp,type,count,reject,sizhang,duiziChecked)
		{
			if(count <= 0 || paizi.length <= 1)
			{
				if(temp.length)
				{
					this._tempMianzi[type].push(temp);
				}
			}
			else
			{
				
				const checked = {};
				const currentPai = paizi[0];
				
				for(const kind of ["shunzi","kezi","lianDazi","qianDazi","duizi","skip"])
				{
					if(kind === "skip")
					{
						const nextPaizi = paizi.slice(1);
						const nextTemp = [...temp];
						
						if(sizhang && sizhang.includes(currentPai))
						{
							nextTemp.push(-1);
							sizhang = null;
						}
						this.allocateMianzi(nextPaizi , nextTemp , type,count,reject,sizhang,duiziChecked);
						continue;
					}
					if((type === 3 && ["shunzi","lianDazi","qianDazi"].includes(kind))
						 || (kind === "qianDazi" && checked.shunzi)
					)
					{
						continue;
					}
					const fil = this.filterMianzi(paizi,kind);
					if(fil)
					{
						const nextReject = [...reject];
						checked[kind] = true;
						let value = (["shunzi","kezi"].includes(kind)) ? 3 : 1;
						if(["kezi","duizi"].includes(kind))
						{
							if(reject.includes(currentPai))
							{
								continue;
							}
							nextReject.push(currentPai);
							if(!duiziChecked && kind === "duizi")
							{
								value = 2;
								duiziChecked = true;
							}
						}
						
						this.allocateMianzi(fil.shengyu , [...temp,value] , type,count - 1,nextReject,sizhang,duiziChecked);
					}
				}
				
			}
			
		}
		siqidui()
		{
			return this._siqidui;
			
		}
		
		xiangtingQiduizi()
		{
			if(this._length < 13) return Infinity;
			let point = 0;
			const tehai = this._tehai;
			let chongfu = 0;
			for(const code of $dataMajiangSystem.allPaicode)
			{
				const count = tehai.reduce(function (r,c)
				{
					return r + ((c === code) ? 1 : 0);
				},0);
				
				if(count >= 2)
				{
					if(this.siqidui())
					{
						point += ((count === 4) ? 2 : 1);
					}
					else
					{
						if(count >= 2)
						{
							point += 1;
							chongfu += (count - 2);
						}
						
						
							/*
						else if (count === 4)
						{
							point -=1;
						}
						if(count >= 3  && this._length === 14 && !keziChecked)
						{
							point += 1;
							keziChecked = true;
						}
						*/
					}
				}
				
			}
			
			
			return 6 - point;
			
		}
		
		xiangtingGuoshi()
		{
			
			if(this._length < 13) return Infinity;
			
			const allYaojiuCode = [1,9,11,19,21,29,31,32,33,34,35,36,37];
			let duiziChecked = false;
			let point = 0;
			for(const yaojiuCode of allYaojiuCode)
			{
				const num = this._tehai.filter(v => v === yaojiuCode).length;
				if(num >= 2 && !duiziChecked)
				{
					duiziChecked = true;
					point += 1;
				}
				if(num >= 1)
				{
					point += 1;
				}
			}
			
			return 13 - point;
		}
		
		adoptQiduizi()
		{
			return this._adoptQiduizi;
			
		}
		adoptGuoshi()
		{
			return this._adoptGuoshi;
			
		}
		execute(option)
		{
			const xt = {"normal" : Infinity , "qiduizi" : Infinity , "guoshi" : Infinity};
			const check = {};
			check.normal = (!option || option.includes("normal"));
			check.qiduizi = (!option || option.includes("qiduizi"));
			check.guoshi = (!option || option.includes("guoshiwushuang") || option.includes("guoshi"));
			if(check.normal)
			{
				xt.normal = this.xiangtingNormal();
			}
			if(this.adoptQiduizi() && check.qiduizi)
			{
				xt.qiduizi = this.xiangtingQiduizi();
			}
			if(this.adoptGuoshi() && check.guoshi)
			{
				xt.guoshi = this.xiangtingGuoshi();
			}
			this._result = xt;
		}
		
		count(tehai,option)
		{
			this.setTehai(tehai);
			this.execute(option);
			const xt = this._result;
			
			return Math.min(xt.normal,xt.qiduizi,xt.guoshi);
		}
	}
	
	
	



})();









