//=============================================================================
// 第1版 - 初版
// 第5版 - 全面的に改定
// 第6版 - 自摸切り設定でエラーが発生する問題を修訂
//         CPU設定(標準)における役の好みの設定が
//         正しく適用されていなかった問題を修訂
// 第9版 - 場に見えている牌の数の取得方式を変更
// 第12版 - 高度なCPU設定を廃止
//
//
//=============================================================================

/*:
 * @target MZ
 * @plugindesc 麻雀 (CPU)
 * @author 
 * @base AP1S_MajiangCore
 * @orderAfter AP1S_MajiangCore
 *
 * @help
 * 
 * ミニゲーム 麻雀
 * 
 * このプラグインのプラグインパラメータは 
 * 高度なCPU設定を使用しない場合 変更しないでください
 * 
 * 
 * 詳細な使用方法は 同梱の説明書をご覧ください
 * 
 * 
 */
 
 /*
 * 
 * 
 * @param cpuClass
 * @text CPU打ち筋登録(高度)
 * @desc 新しく打ち筋を作成し登録します
 * @type struct<cpuClass>[]
 * @default []
 * 
 */
 



 

 

 
 
/*~struct~cpuClass:
 * 
 * @param name
 * @text 打ち筋名
 * @desc この打ち筋に名前を付けてください 雀士登録での呼び出し時使用します
 * @type string
 * @default 
 * 
 * @param general
 * @text 全般評価値
 * @desc 
 * @type struct<baseGeneral>
 * @default {}
 * 
 * @param perspective
 * @text 和了気運評価値
 * @desc 
 * @type struct<basePerspective>
 * @default {}
 * 
 * @param hele
 * @text 和了、立直基準値
 * @desc 
 * @type struct<baseHele>
 * @default {}
 * 
 * @param dazi
 * @text 搭子評価値
 * @desc 
 * @type struct<baseDazi>
 * @default {}
 * 
 * @param yaku
 * @text 役評価値
 * @desc 
 * @type struct<baseYaku>
 * @default {}
 * 
 * @param ampai
 * @text 安牌評価値
 * @desc 
 * @type struct<baseAmpai>
 * @default {}
 * 
 * @param traits
 * @text 特殊特徴
 * @desc 
 * @type struct<cpuTraits>
 * @default {}
 * 
 * @param action
 * @text アクションを行う追加条件を設定
 * @desc 自摸、栄和了、チー、ポンは ほかの評価値による条件を満たした上での判定になります
 * @type struct<baseAction>[]
 * @default []
 * 
 * @param flex
 * @text 条件により打ち筋変動
 * @desc リスト1番から順に評価し 条件が満たされたらその打ち筋に変更されます
 * @type struct<flex>[]
 * @default []
 */
 

 
/*~struct~baseGeneral: 
 * 
 * 
 * @param daziWeight
 * @text 搭子の評価値割合
 * @desc 
 * @type number
 * @default 30
 * 
 * @param yakuWeight
 * @text 役の評価値割合
 * @desc 
 * @type number
 * @default 30
 * 
 * @param ampaiWeight
 * @text 安牌の評価値割合
 * @desc 
 * @type number
 * @default 30
 * 
 * @param badmove
 * @text 弱さ補正値割合
 * @desc 0～100の間の数値を入力してください. 0で補正なし
 * @type number
 * @max 100
 * @default 0
 */
 
/*~struct~basePerspective: 
 * 
 * @param atkThreshold
 * @text 全ツ閾値
 * @desc 和了気運が次の値以上で全ツ状態になります
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param defThreshold
 * @text ベタオリ閾値
 * @desc 和了気運が次の値以下でベタオリ状態になります
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param lizhiThreshold
 * @text リーチ和了気運閾値
 * @desc 和了気運が 次の値以下の場合 立直を行いません
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param lizhi_dealer
 * @text 親立直時の和了気運増加値
 * @desc 
 * @min -99999
 * @type number
 * @default 0
 * 
 * @param lizhi
 * @text 子立直時の和了気運増加値
 * @desc 
 * @min -99999
 * @type number
 * @default 0
 * 
 * @param fulu_dealer
 * @text 親副露時の和了気運増加値
 * @desc 副露一回当たりの増加値(暗槓含)
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param fulu
 * @text 子副露時の和了気運増加値
 * @desc 副露一回当たりの増加値(暗槓含)
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param yixiangting
 * @text 一向聴時の和了気運増加値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param liangxiangting
 * @text 二向聴時の和了気運増加値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param tingpai
 * @text 聴牌時の和了気運増加値
 * @desc 
 * @type number
 * @max 999
 * @min -999
 * @default 0
 * 
 * @param dealer
 * @text 自身が親の時の和了気運増加値
 * @desc 
 * @type number
 * @max 999
 * @min -999
 * @default 0

 * 
 */

/*~struct~baseHele: 
 * 
 * 
 * 
 * @param lizhiPointLower
 * @text 立直最低点
 * @desc 和了予想点が次の点以下の場合立直しません
 * @type number
 * @default 0
 * 
 * @param lizhiPointUpper
 * @text ダマ閾値
 * @desc 和了予想点が次の点以上の場合立直しません. -1で無制限
 * @type number
 * @min -1
 * @default -1
 * 
 * @param lizhiShengyu
 * @text リーチ残り枚数閾値
 * @desc 場に見えていない和了り牌が 次の枚数以下の場合立直しません
 * @type number
 * @min -1
 * @default 0
 * 
 * @param helePoint
 * @text 和了最低点
 * @desc 和了点が次の点以下の場合和了しません
 * @type number
 * @default 0
 * 
 * @param allLast
 * @text オーラス時の和了判断
 * @desc 
 * @type select
 * @option 標準 @value 0
 * @option ラス確なら和了らない @value 1
 * @option トップ確定でなければ和了らない @value 2
 * @default 0
 * 
 */

/*~struct~baseDazi: 
 * 
 * @param xiangting
 * @text 向聴数が増加する牌
 * @desc この牌をきると向聴数が増加する場合 牌の評価値に次の値を追加します 
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param shunzi
 * @text 順子評価値
 * @desc 手牌の牌が順子の時 牌の評価値に次の値を追加します 
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param duizi
 * @text 対子評価値
 * @desc 手牌の牌が対子の時 牌の評価値に次の値を追加します
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param kezi
 * @text 刻子評価値
 * @desc 手牌の牌が刻子の時 牌の評価値に次の値を追加します
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param liangmianDazi
 * @text 両面搭子評価値
 * @desc 手牌の牌が両面搭子の時 牌の評価値に次の値を追加します (1.5 ～ 1の値)
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param bianDazi
 * @text 辺張搭子評価値
 * @desc 手牌の牌が辺搭子の時 牌の評価値に次の値を追加します (1.5 ～ 1の値)
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param qianDazi
 * @text 嵌張搭子評価値
 * @desc 手牌の牌が嵌張搭子の時 対象牌の評価値に次の値を追加します (1.5 ～ 1の値)
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param danqi
 * @text 単騎評価値
 * @desc 手牌全ての評価値に次の値を追加します (1.5 ～ 1の値)
 * @type number
 * @min -99999
 * @default 0
 */
 
/*~struct~baseAmpai: 
 * 
 * @param actual
 * @text 他家の現物評価値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * 
 * @param suji
 * @text 他家の筋牌評価値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * @param lizhi
 * @text 対象が立直中の場合の加算値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * 
 * @param dealer
 * @text 対象が親の場合の加算値
 * @desc 
 * @type number
 * @min -99999
 * @default 0
 * 
 * 
 */
 

 
/*~struct~baseYaku: 
 * 
 * @param duanyaojiu
 * @text 断么九有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param pinghe
 * @text 平和有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param duiduihe
 * @text 対々和有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param qingyise
 * @text 清一色有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param hunyise
 * @text 混一色有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param ziyise
 * @text 字一色有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param luyise
 * @text 緑一色有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param chunquandai
 * @text 純全帯么九有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param hunquandai
 * @text 混全帯么九有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param qinglaotou
 * @text 清老頭有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param hunlaotou
 * @text 混老頭有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param sanseTongshun
 * @text 三色同順有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param sanseTongke
 * @text 三色同刻有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param yiqiTongguan
 * @text 一気通貫有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param anke
 * @text 三、四暗刻有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param qiduizi
 * @text 七対子有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param guoshiwushuang
 * @text 国士無双有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param sanyuan
 * @text 大、小三元有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param sixi
 * @text 大、小四喜有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param gangzi
 * @text 三、四槓子有効牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param yipai
 * @text 役牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 * 
 * @param dora
 * @text ドラ牌の評価
 * @desc 
 * @type struct<cpuYakuValue>
 * @default {"threshold":"0","decision":"0","add":"0","addDec":"0","fulu":"0"}
 */
 
/*~struct~cpuYakuValue: 
 * 
 * @param threshold
 * @text 有効牌閾値
 * @desc この役の有効牌がこの数以上集まった場合 牌の評価値に評価追加値を加算します
 * @type number
 * @default 0
 * 
 * @param decision
 * @text 決心値
 * @desc 有効牌の数がさらにこの値以上になった場合 その役を決心します
 * @type number
 * @default 0
 * 
 * @param add
 * @text 評価追加値
 * @desc 有効牌閾値に達した場合 牌の評価値にこの値を加算します 
 * @type number[]
 * @default 0
 * 
 * @param addDec
 * @text 決心乗算値
 * @desc 決心値に達した場合 この数を加算値にさらに加算します
 * @type number
 * @default 0
 * 
 * @param fulu
 * @text この役の為に副露を行う
 * @desc 決心状態になった場合 この役の為に副露を行います. 門前役の場合無効
 * @desc 
 * @type select
 * @option 行わない @value 0
 * @option 行う @value 1
 * @default 0
 * 
 * 
 */

/*~struct~cpuTraits:
 * 
 * 
 * @param lizhi
 * @text 立直特徴
 * @desc 
 * @type select
 * @option 常にしない @value -1
 * @option 標準 @value 0
 * @option 必ずする @value 1
 * @default 0
 * 
 * @param zimogiri
 * @text 常に自摸切り
 * @desc 有効の場合 常に自摸切りします
 * @type boolean
 * @default false 
 * 
 * @param fulu
 * @text 副露特徴
 * @desc 
 * @type select
 * @option 標準 @value 0 
 * @option 常にしない @value 1
 * @default 0
 * 
 */ 
 


/*~struct~baseAction: 
 * 
 * @param subject
 * @text 対象のアクション
 * @desc 
 * @type select
 * @option 自摸和了 @value zimo
 * @option 栄和了 @value rong
 * @option チー @value chi
 * @option ポン @value peng
 * @option 大明槓 @value daminggang
 * @option 暗槓 @value angang
 * @option 加槓 @value jiagang
 * @option 九種九牌 @value jiuzhongJiupai
 * @default zimo
 * 
 * @param conditions
 * @text 条件リスト
 * @desc 
 * @type select[]
 * @option 無条件にTRUE @value choiceTrue
 * @option 無条件にFALSE @value choiceTrue_n
 * @option 全ツ中 @value zentsu
 * @option 全ツ中ではない @value zentsu_n
 * @option ベタオリ中 @value betaori
 * @option ベタオリ中ではない @value betaori_n
 * @option トップ目 @value top
 * @option トップ目ではない @value top_n
 * @option ラス目 @value last
 * @option ラス目ではない @value last_n
 * @option 自身が親である @value dealer
 * @option 自身が子である @value dealer_n
 * @option 残り57枚以上 @value shengyu_n_56
 * @option 残り56枚以下 @value shengyu__56
 * @option 残り43枚以上 @value shengyu_n_42
 * @option 残り42枚以下 @value shengyu__42
 * @option 残り29枚以上 @value shengyu_n_28
 * @option 残り28枚以下 @value shengyu__28
 * @option 残り15枚以上 @value shengyu_n_14
 * @option 残り14枚以下 @value shengyu__14
 * @option 残り1枚以上 @value shengyu_n_0
 * @option 残り0枚以下 @value shengyu__0
 * @option 門前である @value menqian
 * @option 門前ではない @value menqian_n
 * @option オーラスである @value allLast
 * @option オーラスではない @value allLast_n
 * @option 門前役を決心している @value avoidFulu
 * @option 門前役を決心していない @value avoidFulu_n
 * @option 暗槓を避ける役を決心していない @value avoidAngang_n
 * @option 対象打牌がドラである @value dapaiDora
 * @option 対々和を決心している @value decision__duiduihe
 * @option 対々和を決心していない @value decision_n_duiduihe
 * @option 三、四暗刻を決心している @value decision__anke
 * @option 三、四暗刻を決心していない @value decision_n_anke
 * @option 国士無双を決心している @value decision__guoshiwushuang
 * @option 国士無双を決心していない @value decision_n_guoshiwushuang
 * @option 三、四槓子を決心している @value decision__gangzi
 * @option 三、四槓子を決心していない @value decision_n_gangzi
 * @option 四麻 @value yomma
 * @option 三麻 @value samma
 * @default []
 */

/*~struct~flex: 
 * 
 * 
 * @param conditions
 * @text 条件
 * @desc これらを全て満たしたとき 下項目で指定の打ち筋に変動します
 * @type select[]
 * @option トップ目 @value top
 * @option トップ目ではない @value top_n
 * @option ラス目 @value last
 * @option ラス目ではない @value last_n
 * @option 自身が親である @value dealer
 * @option 自身が子である @value dealer_n
 * @option 前局で和了している @value lastHele
 * @option 焼き鳥 @value yakitori
 * @option 現在3向聴以上 @value xiangting_n_2
 * @option 現在2向聴以下 @value xiangting__2
 * @option 現在2向聴以上 @value xiangting_n_1
 * @option 現在1向聴以下 @value xiangting__1
 * @option 現在聴牌ではない @value xiangting_n_0
 * @option 現在聴牌 @value xiangting__0
 * @option 残り57枚以上 @value shengyu_n_56
 * @option 残り56枚以下 @value shengyu__56
 * @option 残り43枚以上 @value shengyu_n_42
 * @option 残り42枚以下 @value shengyu__42
 * @option 残り29枚以上 @value shengyu_n_28
 * @option 残り28枚以下 @value shengyu__28
 * @option 残り15枚以上 @value shengyu_n_14
 * @option 残り14枚以下 @value shengyu__14
 * @option 残り1枚以上 @value shengyu_n_0
 * @option 残り0枚以下 @value shengyu__0
 * @option 門前である @value menqian
 * @option 門前ではない @value menqian_n
 * @option オーラスである @value allLast
 * @option オーラスではない @value allLast_n
 * @option 東場 @value quanFeng__1
 * @option 南場 @value quanFeng__2
 * @option 西場 @value quanFeng__3
 * @option 北場 @value quanFeng__4
 * @option 四麻 @value yomma
 * @option 三麻 @value samma
 * @default []
 * 
 * @param cpuClass
 * @text 変更後のCPU打ち筋名
 * @desc 条件を満たしたときに呼び出す打ち筋名を指定します
 * @type string
 * @default
 */




(function() 
{
	"use strict";
	
	const P_name = decodeURIComponent(document.currentScript.src).match(/([^\/]+)\.js$/)[1]; //プラグイン名
	
	const PP_unitName = $dataMajiangSystem.basicUnitName;
	
	const PD = {};
	
	
	
	PD.general = {};
	
	//搭子評価値割合
	PD.general.daziWeight = 50 ;
	//役評価値割合
	PD.general.yakuWeight = 20 ;
	//安牌評価値割合
	PD.general.ampaiWeight = 10 ;
	//弱さ補正割合
	PD.general.badmove = 0 ;
	
	
	PD.perspective = {};
	//全ツ状態になる和了気運閾値
	PD.perspective.atkThreshold = 0;
	//ベタオリ状態になる和了気運閾値
	PD.perspective.defThreshold = -25;
	//立直和了気運閾値
	PD.perspective.lizhiThreshold = -10 ;
	//親が立直した場合の和了気運加算値
	PD.perspective.lizhi_dealer = -20 ;
	//子が立直した場合の和了気運加算値
	PD.perspective.lizhi = -15 ;
	//親が副露した場合の和了気運加算値
	PD.perspective.fulu_dealer = -10 ;
	//子が副露した場合の和了気運加算値
	PD.perspective.fulu = -5 ;
	//自身が一向聴時の和了気運加算値
	PD.perspective.yixiangting = 10 ;
	//自身が二向聴時の和了気運加算値
	PD.perspective.liangxiangting = 5 ;
	//自身が聴牌時の和了気運加算値
	PD.perspective.tingpai = 15 ;
	//自身が親の時の和了気運加算値
	PD.perspective.dealer = 10 ;

	
	PD.hele = {};
	//立直最低点
	PD.hele.lizhiPointLower = 0 ;
	//立直最高点
	PD.hele.lizhiPointUpper = -1 ;
	//立直残り枚数閾値
	PD.hele.lizhiShengyu = 2 ;
	//和了最低点
	PD.hele.helePoint = 0;
	//オーラス時の和了判断
	PD.hele.allLast = 0 ;
	
	
	PD.dazi = {};
	//向聴増加牌評価値
	PD.dazi.xiangting = 9 ;
	//順子評価値
	PD.dazi.shunzi = 6 ;
	//刻子評価値
	PD.dazi.kezi = 6 ;
	//対子評価値
	PD.dazi.duizi = 4 ;
	//両面搭子評価値
	PD.dazi.liangmianDazi = 3 ;
	//辺張搭子評価値
	PD.dazi.bianDazi = 2;
	//嵌張搭子評価値
	PD.dazi.qianDazi = 3 ;
	//単騎評価値
	PD.dazi.danqi = 1 ;
	
	
	PD.ampai = {};
	//他家の現物評価値
	PD.ampai.actual = -3 ;
	//他家の筋牌評価値
	PD.ampai.suji = -1 ;
	//対象が立直している場合の加算値
	PD.ampai.lizhi = -9 ;
	//対象が親の場合の加算値
	PD.ampai.dealer = -1 ;
	
	
	PD.alter = {};
	
	PD.alter.atk = {};
	
	PD.alter.atk[-10] = [
		["perspective","atkThreshold",Infinity],
		["perspective","defThreshold",Infinity]
	];
	
	PD.alter.atk[-1] = [
		["general","ampaiWeight",20],
		["perspective","defThreshold",-15]
		
	];
	
	PD.alter.atk[1] = [
		["perspective","atkThreshold",-10],
		["perspective","defThreshold",-35]
	];
	
	PD.alter.atk[10] = [
		["perspective","atkThreshold",-Infinity],
		["perspective","defThreshold",-Infinity]
	];
	
	
	PD.alter.lizhi = {};
	
	PD.alter.lizhi[-10] = [
		["traits","lizhi",-1]
	];
	
	PD.alter.lizhi[-1] = [
		["hele","lizhiShengyu",4],
		["hele","lizhiPointLower",2000],
		["hele","lizhiPointUpper",4800],
		["perspective","lizhiThreshold",0]
	];
	
	PD.alter.lizhi[1] = [
		["hele","lizhiShengyu",0],
		["perspective","lizhiThreshold",-30]
	];
	
	PD.alter.lizhi[10] = [
		["traits","lizhi",-1]
	];
	
	
	
	
	
	PD.alter.yakuWeight = {};
	
	
	PD.alter.yakuWeight[-1] = [
		["general","daziWeight",100],
		["general","yakuWeight",10]
	];
	
	PD.alter.yakuWeight[1] = [
		["general","yakuWeight",30]
	];
	
	
	
	PD.alter.allLast = {};
	
	PD.alter.allLast[1] = [
		["hele","allLast",1]
	];
	
	PD.alter.allLast[2] = [
		["hele","allLast",2]
	];
	
	PD.alter.badmove = {};
	
	PD.alter.badmove[1] = [
		["general","badmove",25]
	];
	
	PD.alter.badmove[2] = [
		["general","badmove",50]
	];
	
	PD.alter.badmove[3] = [
		["general","badmove",75]
	];
	
	PD.alter.badmove[4] = [
		["general","badmove",100]
	];
	
	
	
	
	PD.yaku = {};
	//断么九
	PD.yaku.duanyaojiu = {
		"threshold":[15,13,11,9,7],
		"decision":	[15,14,13,10,8],
		"add":		[0,3,5,8,10],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,0,1,1]
	};
	//平和
	PD.yaku.pinghe = {
		"threshold":[15,11,10,8,6],
		"decision":	[15,12,12,10,9],
		"add":		[0,3,5,8,10],
		"addDec":	[0,10,10,10,10],
		"fulu":		[0,0,0,0,0]
	};
	//対々和
	PD.yaku.duiduihe = {
		"threshold":[15,12,9,6,6],
		"decision":	[15,12,9,9,6],
		"add":		[0,3,5,8,10],
		"addDec":	[0,10,10,10,10],
		"fulu":		[0,0,1,1,1]
	};
	//清一色
	PD.yaku.qingyise = {
		"threshold":[15,11,10,9,8],
		"decision":	[15,12,12,11,9],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//混一色
	PD.yaku.hunyise = {
		"threshold":[15,11,10,9,8],
		"decision":	[15,12,12,11,9],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//字一色
	PD.yaku.ziyise = {
		"threshold":[15,12,11,9,8],
		"decision":	[15,12,11,10,9],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//緑一色
	PD.yaku.luyise = {
		"threshold":[15,13,10,9,8],
		"decision":	[15,13,11,10,9],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//純全帯么九
	PD.yaku.chunquandai = {
		"threshold":[15,10,11,9,7],
		"decision":	[15,13,13,11,10],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//混全帯么九
	PD.yaku.hunquandai = {
		"threshold":[15,10,11,9,7],
		"decision":	[15,13,13,11,10],
		"add":		[0,3,5,8,10],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,0,1,1]
	};
	//混老頭
	PD.yaku.qinglaotou = {
		"threshold":[15,10,9,8,7],
		"decision ":[15,11,9,9,8],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//清老頭
	PD.yaku.hunlaotou = {
		"threshold":[15,10,9,8,7],
		"decision":	[15,13,11,10,10],
		"add":		[0,3,5,8,10],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//三色同順
	PD.yaku.sanseTongshun = {
		"threshold":[15,10,7,7,6],
		"decision":	[15,9,9,8,7],
		"add":		[0,5,10,10,10],
		"addDec":	[0,20,20,20,20],
		"fulu":		[0,0,0,1,1]
	};
	//三色同刻
	PD.yaku.sanseTongke = {
		"threshold":[15,9,8,7,6],
		"decision":	[15,9,9,7,7],
		"add":		[0,5,10,13,15],
		"addDec":	[0,10,10,10,10],
		"fulu":		[0,0,1,1,1]
	};
	//一気通貫
	PD.yaku.yiqiTongguan = {
		"threshold":[15,10,7,7,6],
		"decision":	[15,9,8,8,7],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,0,1,1]
	};
	//三、四暗刻
	PD.yaku.anke = {
		"threshold":[15,10,9,6,6],
		"decision":	[15,12,9,9,6],
		"add":		[0,5,10,10,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,0,0,0]
	};
	//七対子
	PD.yaku.qiduizi = {
		"threshold":[15,10,10,8,6],
		"decision":	[15,12,10,10,8],
		"add":		[0,5,5,10,10],
		"addDec":	[0,5,20,25,30],
		"fulu":		[0,0,0,0,0]
	};
	//国士無双
	PD.yaku.guoshiwushuang = {
		"threshold":[15,11,10,9,8],
		"decision":	[15,11,10,8,7],
		"add":		[0,5,10,10,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,0,0,0]
	};
	//小、大三元
	PD.yaku.sanyuan = {
		"threshold":[15,8,7,6,6],
		"decision":	[15,9,8,7,6],
		"add":		[0,3,5,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//小、大四喜
	PD.yaku.sixi = {
		"threshold":[15,12,9,8,7],
		"decision":	[15,12,10,9,9],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[0,0,1,1,1]
	};
	//三、四槓子
	PD.yaku.gangzi = {
		"threshold":[15,9,9,6,6],
		"decision":	[15,12,9,6,6],
		"add":		[0,5,10,13,15],
		"addDec":	[0,50,50,50,50],
		"fulu":		[1,1,1,1,1]
	};
	//役牌
	PD.yaku.yipai = {
		"threshold":[15,3,1,1,1],
		"decision":	[15,3,2,0,0],
		"add":		[0,3,5,6,7],
		"addDec":	[0,5,10,13,15],
		"fulu":		[0,0,1,1,1]
	};
	//ドラ
	PD.yaku.dora = {
		"threshold":[15,3,1,1,1],
		"decision":	[15,15,15,15,15],
		"add":		[0,3,5,6,7],
		"addDec":	[0,10,10,10,10],
		"fulu":		[0,0,0,0,0]
	};
	
	
	PD.action = [
		{"subject" : "zimo" , "conditions" : ["choiceTrue"]},
		{"subject" : "rong" , "conditions" : ["choiceTrue"]},
		{"subject" : "lizhi" , "conditions" : ["betaori_n"]},
		{"subject" : "chi" , "conditions" : ["betaori_n" , "avoidFulu_n"]},
		{"subject" : "peng" , "conditions" : ["betaori_n" , "avoidFulu_n"]},
		{"subject" : "daminggang" , "conditions" : ["betaori_n" , "decision__gangzi"]},
		{"subject" : "daminggang" , "conditions" : ["zentsu" , "avoidFulu_n" ,"menqian_n" , "dapaiDora"]},
		{"subject" : "daminggang" , "conditions" : ["zentsu" , "avoidFulu_n" , "decision__duiduihe"]},
		{"subject" : "angang" , "conditions" : ["betaori_n","avoidAngang_n"]},
		{"subject" : "jiagang" , "conditions" : ["betaori_n"]},
		{"subject" : "jiuzhongJiupai" , "conditions" : ["decision_n_guoshiwushuang"]},
		{"subject" : "jiuzhongJiupai" , "conditions" : ["betaori"]}
	];
	
	
	
	
	class CPUDataManager
	{
		constructor()
		{
			this.load();
		}
		
		baseClass(battler)
		{
			const data = battler.battlerData().cpuSettings;
			const base = JSON.parse(JSON.stringify(PD));
			const traits = data.traits || {};
			
			
			base.traits = {};
			
			base.traits.lizhi = 0;
			base.traits.zimogiri = !!( traits.zimogiri);
			base.traits.fulu = ( traits.fulu || 0 )
			
			
			for(const key in PD.alter)
			{
				const value = data[key];
				if(value && PD.alter[key][value])
				{
					for(const alter of PD.alter[key][value])
					{
						base[alter[0]][alter[1]] = alter[2];
					}
				}
			}
			
			
			for(const yakuName in base.yaku)
			{
				const yakuData = data.yaku.find(y => y.subject === yakuName);
				const value = ((yakuData) ? yakuData.value : 2);
				for(const key in base.yaku[yakuName])
				{
					base.yaku[yakuName][key] = base.yaku[yakuName][key][value];
				}
			}
			
			return base;
		}
		
		
		load()
		{
			const GP = PluginManager.parameters(P_name);
			
			this._classes = this.parseObject(GP.cpuClass);
			
			this._base = [];
			for(const battler of $gameMajiangBattlers.allBattlers())
			{
				const position = battler.position();
				this._base[position] = this.baseClass(battler);
			}
		}
		
		clase(clase)
		{
			
			if(typeof clase === "string")
			{
				return this._classes.find(c => c.name === clase);
			}
			return this._base[clase];
		}
		
		
		
		parseData(string)
		{
			if(string === "true" || string === "false")
			{
				return (string === "true");
			}
			else if (!isNaN(string))
			{
				return Number(string);
			}
			return string;
			
		}
		dataType(string)
		{
			if(typeof string === "string")
			{
				const a = string.slice(0,1);
				if(a === "{")
				{
					return "object";
				}
				else if(a === "[")
				{
					return "array";
				}
			}
			
			return "primitive";
		}
		
		parseObject(string)
		{
			let data;
			const type = this.dataType(string);
			if(type === "object")
			{
				data = JSON.parse(string || "{}");
				for (const key in data)
				{
					if(key !== "name")
					{
						data[key] = this.parseObject(data[key]);
					}
				}
			}
			else if(type === "array")
			{
				data = JSON.parse(string || "[]");
				data = data.map(d =>
				{
					return this.parseObject(d);
				});
			}
			else
			{
				data = this.parseData(string);
			}
			return data;
		}
		
	}
	
	
	
	
	
	const PF = {};
	
	(() =>
	{
		PF.start = function()
		{
			this._checkCondition = new Condition();
		}
		PF.terminate = function()
		{
			this._checkCondition = null;
		}
		
		PF.textPai = function(pai)
		{
			let text = "";
			let type;
			let value;
			let akadora = false;
			
			
			if(typeof pai === "number")
			{
				type = Math.floor(pai / 10);
				value = pai % 10;
			}
			else
			{
				type = pai.type();
				value = pai.value();
				akadora = pai.isAkadora();
			}
			
			if(type !== 3)
			{
				if(akadora)
				{
					text += "赤";
				}
				text += $gameMajiangSystem.shuzi(value) + ["萬","索","筒"][type];
			}
			else
			{
				text += ["","東","南","西","北","白","發","中"][value];
			}
			
			return text;
		}
		PF.textType = function(type)
		{
			return ["萬子","索子","筒子","字牌"][type] || "";
		}
		
		PF.textYaku = function(name,value)
		{
			let text = "";
			
			switch(name)
			{
				case "duanyaojiu" : 
					return "断么九";
				
				case "pinghe" : 
					return "平和";
				
				case "duiduihe" : 
					return "対々和";
				
				case "qingyise" :
					return this.textType(value) + "の清一色";
				
				case "hunyise" : 
					return this.textType(value) + "の混一色";
				case "ziyise" : 
					return "字一色";
				
				case "luyise" : 
					return "緑一色";
				
				case "chunquandai" : 
					return "純全帯么九";
				
				case "hunquandai" : 
					return "混全帯么九";
				
				case "qinglaotou" : 
					return "清老頭";
				
				case "hunlaotou" : 
					return "混老頭";
				
				case "sanseTongshun" : 
					for(let i = 0; i < 3;i++)
					{
						text += (value + i) + "";
					}
					return text + "の三色同順";
				
				case "sanseTongke" : 
					return (value) + "の三色同刻";
				
				case "yiqiTongguan" : 
					return this.textType(value) + "の一気通貫";
				
				case "anke" : 
					return "三、四暗刻";
				
				case "qiduizi" : 
					return "七対子";
				
				case "guoshiwushuang" : 
					return "国士無双";
				
				case "sanyuan" : 
					return "小、大三元";
				
				case "sixi" : 
					return "三、四喜和";
				
				case "gangzi" : 
					return "三、四槓子";
				
				case "yipai" : 
					return "役牌" + this.textPai(value);
				
				case "dora" : 
					return "ドラ";
			}
			return text;
		}
		PF.textAction = function(kind)
		{
			switch(kind)
			{
				case "rong" : return "栄和了";
				case "zimo" : return "自摸和了";
				case "lizhi" : return "立直";
				case "jiuzhongJiupai" : return "九種九牌";
				case "chi" : return "チー";
				case "peng" : return "ポン";
				case "daminggang" : return "大明槓";
				case "angang" : return "暗槓";
				case "jiagang" : return "加槓";
			}
		}
		PF.textFeng = function(feng)
		{
			return ["","東","南","西","北"][feng];
		}
		PF.textPosition = function(position)
		{
			return ["自家","下家","対面","上家"][position];
		}
		PF.horzLine = function(longish)
		{
			return (longish) ? "------------------------------" : "---------------";
		}
		
		PF.countXiangting = function(codeArray,option)
		{
			return $gameMajiangPaishan.countXiangting(codeArray,option);
		}
		
		PF.checkCondition = function()
		{
			return this._checkCondition;
		}
		PF.numMap = function(x,z,a,c,b)
		{
			return x + (z - x) * ((b - a) / (c - a)); 
		}
		
	})();
	
	
	class MajiangCPU
	{
		constructor()
		{
			this.clear();
		}
		
		start()
		{
			PF.start();
			this.loadCPUData();
			this.setYakuData();
		}
		
		setBattler(position)
		{
			this._battlers[position] = new MajiangCPUBattler(position);
			this._paizi[position] = new MajiangCPUPaizi(position);
			
		}
		
		terminate()
		{
			this.clear();
			PF.terminate();
		}
		
		dapai(position)
		{
			return this.battler(position).dapai();
		}
		
		zimoAction(position,actions)
		{
			return this.battler(position).zimoAction(actions);
		}
		
		fuluAction(position,actions,dapaiPosition)
		{
			return this.battler(position).fuluAction(actions,dapaiPosition);
		}
		peipai(position)
		{
			this.battler(position).updatePeipai()
		}
		zimo(position)
		{
			this.battler(position).updateZimo();
		}
		
		afterFulu(position)
		{
			this.battler(position).updateAfterFulu();
		}
		
		
		
		clear()
		{
			this._cpuData = null;
			this._yakuData = null;
			this._battlers = [];
			this._paizi = [];
		}
		
		loadCPUData()
		{
			this._cpuData = new CPUDataManager();
		}
		
		cpuClass(clase)
		{
			return this._cpuData.clase(clase);
		}
		
		
		
		battler(position)
		{
			return this._battlers[position];
		}
		
		paizi(position)
		{
			return this._paizi[position];
		}
		
		setYakuData()
		{
			this._yakuData = {
				"duanyaojiu" : {"correspond" : ["duanyaojiu"] , "exclude" : [], "occupy" : true},
				"pinghe" : {"correspond" : ["pinghe"] , "exclude" : []},
				"duiduihe" : {"correspond" : ["duiduihe"] , "exclude" : ["qiduizi"]},
				"qingyise" : {"correspond" : ["qingyise"], "exclude" : ["hunyise"], "occupy" : true},
				"hunyise" : {"correspond" : ["hunyise"] , "exclude" : [], "occupy" : true},
				"ziyise" : {"correspond" : ["ziyise"] , "exclude" : ["hunyise","chunquandai","hunquandai"], "occupy" : true},
				"luyise" : {"correspond" : ["luyise"] , "exclude" : [], "occupy" : true},
				"chunquandai" : {"correspond" : ["chunquandai"] , "exclude" : ["hunquandai"], "occupy" : true},
				"hunquandai" : {"correspond" : ["hunquandai"] , "exclude" : [], "occupy" : true},
				"qinglaotou" : {"correspond" : ["qinglaotou"], "exclude" : ["hunlaotou","chunquandai","hunquandai"], "occupy" : true},
				"hunlaotou" : {"correspond" : ["hunlaotou"] , "exclude" : ["hunquandai"], "occupy" : true},
				"sanseTongshun" : {"correspond" : ["sanseTongshun"] , "exclude" : []},
				"sanseTongke" : {"correspond" : ["sanseTongke"] , "exclude" : []},
				"yiqiTongguan" : {"correspond" : ["yiqiTongguan"] , "exclude" : ["duanyaojiu"]},
				"anke" : {"correspond" : ["sananke","sianke"] , "exclude" : ["qiduizi"]},
				"qiduizi" : {"correspond" : ["qiduizi"] , "exclude" : [], "occupy" : true},
				"guoshiwushuang" : {"correspond" : ["guoshiwushuang","chunzhengGuoshi"] , "exclude" : [] , "occupy" : true},
				"sanyuan" : {"correspond" : ["dasanyuan","xiaosanyuan"] , "exclude" : []},
				"sixi" : {"correspond" :  ["dasixi","xiaosixi"] , "exclude" : []},
				"gangzi" : {"correspond" : ["sangangzi","sigangzi"] , "exclude" : []},
				"yipai" : {"correspond" : [] , "exclude" : []},
				"dora" : {"correspond" : [] , "exclude" : []},
			};
			
			for(const name in this._yakuData)
			{
				this._yakuData[name].adopt = this.setAdoptYaku(this._yakuData[name].correspond);
			}
		}
		
		setAdoptYaku(correspond)
		{
			if(correspond.length === 0) return true;
			
			return correspond.some(y => 
			{
				return $gameMajiangSystem.yakuData[y].adopt;
			})
		}
		
		yakuCorrespond(name)
		{
			return this._yakuData[name].correspond;
		}
		
		checkAdoptYaku(name)
		{
			return this._yakuData[name].adopt;
		}
		yakuData()
		{
			return this._yakuData;
		}
	}
	
	
	
	$gameMajiangCPU[PP_unitName] = new MajiangCPU();
	
	
	
	
	
	
	class EvPai
	{
		constructor(pai,position)
		{
			this._position = position;
			this._point1 = {};
			this._point2 = {};
			this._point3 = {};
			this._drop = false;
			
			this._checkedShunzi = [];
			this._yakuReject = [];
			this._pai = pai;
			this._daziStatus = [];
			this._yakuStatus = [];
			this._ampaiStatus = [];
		}
		cpuBattler()
		{
			return $gameMajiangCPU[PP_unitName].battler(this._position);
		}
		cpuPaizi()
		{
			return $gameMajiangCPU[PP_unitName].paizi(this._position);
		}
		
		position()
		{
			return this._position;
		}
		clase()
		{
			return this.cpuBattler().clase();
		}
		pai()
		{
			return this._pai;
		}
		paiId()
		{
			return this.pai().paiId();
		}
		code()
		{
			return this.pai().code();
		}
		setFuluType(type)
		{
			this._fuluType = type;
		}
		fuluType()
		{
			return type;
		}
		daziStatus()
		{
			return this._daziStatus;
		}
		pushDaziStatus(status)
		{
			this._daziStatus.push(status);
		}
		yakuStatus()
		{
			return this._yakuStatus;
		}
		pushYakuStatus(status)
		{
			if(status.value !== 0 && !status.value)
			{
				status.value = -1;
			}
			this._yakuStatus.push(status);
		}
		setAmpaiStatus(status)
		{
			this._ampaiStatus = status;
		}
		ampaiStatus()
		{
			return this._ampaiStatus;
		}
		pushCheckedShunzi(code)
		{
			this._checkedShunzi.push(code)
		}
		checkedShunzi(code)
		{
			return this._checkedShunzi.includes(code);
		}
		
		
		filterYakuStatus()
		{
			
			this._yakuStatus.sort((a,b) => b.zimoEx - a.zimoEx);
			this._yakuStatus.sort((a,b) => ((a.isDazi) ? 1 : 0));
			let ret = [];
			for(const s of this._yakuStatus)
			{
				if(!s.chongfu)
				{
					if(
						ret.some(r => r.name === s.name && r.value === s.value)
					)
					{
						continue;
					}
				}
				
				ret.push(s);
			}
			this._yakuStatus = ret;
		}
		
		total()
		{
			this.setTotalDazi();
			this.setTotalYaku();
			this.setTotalAmpai();
		}
		
		setTotalDazi()
		{
			if(this.drop())
			{
				this._point1.dazi = 0;
				return;
			}
			let point = 0;
			const clase = this.clase();
			const decGuoshi = this.cpuPaizi().decision("guoshi");
			const decQiduizi = this.cpuPaizi().decision("qiduizi");
			for(const status of this._daziStatus)
			{
				let add = clase.dazi[status.name];
				if(decGuoshi)
				{
					if(["xiangting"].includes(status.name))
					{
						point += add;
					}
					else
					{
						add = 0;
					}
					
				}
				else if(decQiduizi)
				{
					
					if(["duizi","xiangting"].includes(status.name))
					{
						point += add;
					}
					else if(status.name === "danqi")
					{
						add = this.calcZimoExValue(add,status.zimoEx);
						point += add;
					}
					else
					{
						add = 0;
					}
				}
				else
				{
					if(status.name !== "danqi")
					{
						add = this.calcZimoExValue(add,status.zimoEx);
					}
					else
					{
						add = 0;
					}
					point += add;
				}
				status.add = add;
			}
			this._point1.dazi = point;
		}
		calcZimoExValue(add,zimoEx)
		{
			return add * (zimoEx ** 0.25);
		}
		
		setTotalYaku()
		{
			if(this.drop())
			{
				this._point1.yaku = 0;
				return;
			}
			let point = 0;
			const cpuPaizi = this.cpuPaizi();
			const clase = this.clase();
			
			for(const status of this._yakuStatus)
			{
				let add = 0;
				
				if(cpuPaizi.decision(status))
				{
					add += clase.yaku[status.name].add;
					add += clase.yaku[status.name].addDec;
					status.decision = true;
				}
				else if(cpuPaizi.attention(status))
				{
					add += clase.yaku[status.name].add;
					status.attention = true;
				}
				if(status.amount !== void 0)
				{
					add *= status.amount;
				}
				
				point += add;
				status.add = add;
			}
			this._point1.yaku = point;
		}
		setTotalAmpai()
		{
			let point = 0;
			
			const clase = this.clase();
			for(const status of this._ampaiStatus)
			{
				let add = 0;
				if(status.kind === "actual")
				{
					add += clase.ampai.actual;
				}
				else if(status.kind === "suji")
				{
					add += clase.ampai.suji;
				}
				for(const key of ["lizhi","dealer"])
				{
					if(status[key])
					{
						add += clase.ampai[key];
					}
				}
				point += add;
				status.add = add;
			}
			this._point1.ampai = point;
		}
		point1(kind)
		{
			return this._point1[kind];
		}
		point2(kind)
		{
			return this._point2[kind];
		}
		point3(kind)
		{
			return this._point3[kind];
		}
		point4()
		{
			let ret = 0;
			for(const kind in this._point3)
			{
				ret += this._point3[kind];
			}
			
			return ret;
		}
		setPoint(kind,min,max,weight)
		{
			const basePoint = this.point1(kind);
			
			this._point2[kind] = PF.numMap(0,1,min,max,basePoint) || 0;
			
			this._point3[kind] = this._point2[kind] * (weight);
			
		}
		setDrop()
		{
			this._drop = true;
		}
		drop()
		{
			return this._drop;
		}
		
		isDapaiEnabled()
		{
			return this.pai().isDapaiEnabled();
		}
		statusText()
		{
			const text = [];
			text.push(PF.horzLine());
			text.push("%1の詳細情報".format(PF.textPai(this.pai())));
			text.push("最終評価値 %1".format(this.point4()));
			const dropText = (this.drop()) ? "決心役により排除" : "";
			text.push("搭子評価値 [ %1 , %2 , %3 ] %4".format(this.point1("dazi"),this.point2("dazi"),this.point3("dazi"),dropText));
			text.push("搭子評価内訳");
			for(const status of this.daziStatus())
			{
				text.push(this.daziStatusText(status));
			}
			text.push("役評価値 [ %1 , %2 , %3 ] %4".format(this.point1("yaku"),this.point2("yaku"),this.point3("yaku"),dropText));
			text.push("役評価内訳");
			for(const status of this.yakuStatus())
			{
				text.push(this.yakuStatusText(status));
			}
			text.push("安牌評価値 [ %1 , %2 , %3 ]".format(this.point1("ampai"),this.point2("ampai"),this.point3("ampai")));
			text.push("安牌評価内訳");
			for(const status of this.ampaiStatus())
			{
				text.push(this.ampaiStatusText(status));
			}
			return text;
		}
		
		
		daziStatusText(status)
		{
			const textName = {
				"xiangting" : "向聴増加牌",
				"lianDazi" : "連塔子",
				"liangmianDazi" : "両面塔子",
				"bianDazi" : "辺塔子",
				"qianDazi" : "嵌搭子",
				"shunzi" : "順子",
				"kezi" : "刻子",
				"duizi" : "対子",
				"danqi" : "単騎"
			}[status.name];
			
			
			return " 種類 %1 , 評価値 %2 , 自摸可能性 %3 , 基準コード %4".format( textName, status.add ,status.zimoEx,status.statusCode);
		}
		yakuStatusText(status)
		{
			const textName = PF.textYaku(status.name,status.value);
			let decStatus;
			if(status.reject)
			{
				decStatus = "成立不可";
			}
			else if(status.decision)
			{
				decStatus = "決心";
			}
			else if(status.attention)
			{
				decStatus = "意識";
			}
			else
			{
				decStatus = "無効";
			}
			return " 種類 %1 , 評価値 %2 , 状況 : %3 , 自摸可能性 %4 , 有効牌所有数 %5".format(textName, status.add ,decStatus, status.zimoEx ,status.count);
		}
		ampaiStatusText(status)
		{
			const textPosition = PF.textPosition(status.position);
			let bs = "";
			
			if(status.dealer)
			{
				bs += "親 ";
			}
			if(status.lizhi)
			{
				bs += "立直中 ";
			}
			
			const textKind = textPosition + "の" + ((status.kind === "actual") ? "現物" : "筋牌");
			
			return " 種類 %1 , 評価値 %2 , 対象の状態 %3".format(textKind,status.add,bs);
		}
	}
	
	
	class MajiangCPUPaizi
	{
		constructor(position)
		{
			this._note = {};
			this._position = position;
		}
		cpuBattler()
		{
			return $gameMajiangCPU[PP_unitName].battler(this._position);
		}
		battler()
		{
			return $gameMajiangBattlers.battler(this.position());
		}
		position()
		{
			return this._position;
		}
		clase()
		{
			return this.cpuBattler().clase();
		}
		shengyu()
		{
			return $gameMajiangPaishan.shengyu();
		}
		
		allPai()
		{
			return $gameMajiangPaishan.allData();
		}
		setJian()
		{
			const battler = this.battler();
			this._note.jian = battler.visiblePaizi();
			
			this._note.jianAll = battler.visiblePaizi(true);
		}
		
		jianAll()
		{
			return this._note.jianAll;
		}
		
		jian()
		{
			return this._note.jian; 
		}
		countXiangting(tehai,option)
		{
			return PF.countXiangting(tehai.map(p => p.code()),option);
		}
		
		refreshXiangting(tehai)
		{
			
			this._currentXiangting = this.countXiangting(tehai,this.xiangtingOption());
		}
		xiangtingOption()
		{
			if(this.decision("qiduizi"))
			{
				return ["qiduizi"];
			}
			else if(this.decision("guoshiwushuang"))
			{
				return ["guoshiwushuang"];
			}
			return null
		}
		
		currentXiangting()
		{
			return this._currentXiangting;
		}
		
		setXiangtingProgress()
		{
			this._note.xiangtingProgress = [];
			
			const tehai = this.tehai();
			const tehaiCode = tehai.map(p => p.code());
			const length = tehai.length; 
			const xiangtingOption = this.xiangtingOption()
			for(let i = 0; i < length ; i++)
			{
				const dapai = tehaiCode[i];
				
				if(this._note.xiangtingProgress.includes(dapai))
				{
					continue;
				}
				const tempTehai = [...tehaiCode];
				tempTehai.splice(i,1);
				
				const xiangting = PF.countXiangting(tempTehai,xiangtingOption);
				
				if(this._currentXiangting < xiangting)
				{
					this._note.xiangtingProgress.push(dapai);
				}
			}
			
		}
		
		xiangtingProgress()
		{
			return this._note.xiangtingProgress || [];
		}
		
		refreshData(tehai,fulu)
		{
			this._note = {};
			this._note.decision = [];
			this._note.attention = [];
			this.setTehaiFulu(tehai,fulu);
			this.setEvPaizi();
			this.setJian();
			this.setZimoExpect();
			this.setMenqian();
			this.setXiangtingProgress();
			//this.measurementField();
			this.setYitongZimoEx();
			this.setHeAmpai();
			this.startEvaluate();
		}
		
		setTehaiFulu(tehai,fulu)
		{
			this._note.tehai = [...tehai];
			this._note.tehai.sort((a , b) => a.paiId() - b.paiId());
			this._note.fulu = fulu;
			this._note.allPaizi = this._note.tehai.concat(...fulu.map(p => p.paizi()));
		}
		
		setEvPaizi()
		{
			
			this._note.evTehai = this.tehai().map(p => new EvPai(p,this._position));
			this._note.evAll = [...this._note.evTehai];
			this._note.evFulu = [];
			for(const fulu of this.fulu())
			{
				const fuluData = {};
				const fuluType = fulu.fuluType();
				fuluData.fuluType = fuluType;
				
				fuluData.statusCode = fulu.statusCode();
				fuluData.evPaizi = [];
				for(let i = 0; i < 3 ; i++)
				{
					const pai = fulu.paizi()[i];
					const evPai = new EvPai(pai,this._position);
					evPai.setFuluType(fuluType);
					fuluData.evPaizi.push(evPai);
					this._note.evAll.push(evPai);
				}
				this._note.evFulu.push(fuluData);
			}
		}
		
		tehai()
		{
			return this._note.tehai;
		}
		
		evTehai()
		{
			return this._note.evTehai;
		}
		fulu()
		{
			return this._note.fulu;
		}
		evFulu()
		{
			return this._note.evFulu;
		}
		allPaizi()
		{
			return this._note.allPaizi;
		}
		evAll()
		{
			return this._note.evAll;
		}
		
		setZimoExpect()
		{
			const ret1 = {};
			const ret2 = {};
			for(const code of $dataMajiangSystem.allPaicode)
			{
				const jianNum = this.jian()[code];
				const maxPaiNum = $gameMajiangPaishan.maxPaiNum(code);
				ret1[code] = maxPaiNum - jianNum;
				const jianAllNum = this.jianAll()[code];
				ret2[code] = maxPaiNum - jianAllNum;
			}
			this._note.zimoExpect1 = ret1;
			this._note.zimoExpect2 = ret2;
		}
		
		zimoExpect(code,exTehai)
		{
			if(exTehai)
			{
				return this._note.zimoExpect2?.[code] || 0;
			}
			else
			{
				return this._note.zimoExpect1?.[code] || 0;
			}
		}
		
		setMenqian()
		{
			this._note.menqian = this.battler().isMenqian();
		}
		
		isMenqian()
		{
			return this._note.menqian;
		}
		
		yakuData()
		{
			return this._yakuData;
		}
		checkAdoptYaku(yakuName)
		{
			return $gameMajiangCPU[PP_unitName].checkAdoptYaku(yakuName);
			
		}
		
		startEvaluate()
		{
			const evTehai = this.evTehai();
			const evFulu = this.evFulu();
			const evAll = this.evAll();
			
			this.setDaziStatus(evTehai);
			this.setYakuStatus(evTehai,evFulu);
			this.setAmpaiStatus(evTehai);
			
			this.setCountYaku();
			
			this.setPoint();
		}
		
		findDapai(ex)
		{
			const evPaizi = this.evTehai().filter(p => p.isDapaiEnabled());
			
			
			//const minValue = Math.min(...evPaizi.map(p => p.point4()));
			
			let decision = null;
			this._bValue = 0;
			if(ex)
			{
				this.setBValue(evPaizi);
			}
			
			
			const bValue = this.bValue();
			if(bValue === 0)
			{
				const minValue = Math.min(...evPaizi.map(p => p.point4()));
				const nominate = evPaizi.filter(p => p.point4() === minValue);
				decision = nominate[Math.randomInt(nominate.length)];
			}
			else 
			{
				const ranking = [...evPaizi].sort((a,b) => a.point4() - b.point4());
				decision = ranking[bValue];
			}
			
			return decision;
		}
		bValue()
		{
			return this._bValue;
		}
		
		setBValue(evPaizi)
		{
			const bValue = this.clase().general.badmove;
			if(!bValue || this.flipBValue(bValue))
			{
				this._bValue = 0;
				return ;
			}
			
			const length = evPaizi.length;
			const maxValue = Math.floor(length * (bValue / 100));
			let value = Math.randomInt(maxValue);
			value -= Math.floor(Math.randomInt(maxValue) / 2);
			value = value.clamp(0,length - 1);
			this._bValue = value;
		}
		flipBValue(value)
		{
			return (Math.randomInt(100) >= value);
		}
		
		getMinMax(kind,evPaizi)
		{
			
			const value = evPaizi.map(ev => ev.point1(kind));
			
			return [Math.min(...value),Math.max(...value)];
		}
		weight()
		{
			const ret = {};
			const data = this.clase().general;
			const total = data.daziWeight + data.yakuWeight + data.ampaiWeight;
			ret.dazi = (data.daziWeight / total);
			ret.yaku = (data.yakuWeight / total);
			ret.ampai = (data.ampaiWeight / total);
			if(this.cpuBattler().betaori())
			{
				ret.dazi = 0;
				ret.yaku = 0;
			}
			else if(this.cpuBattler().zentsu())
			{
				ret.ampai = 0;
			}
			return ret;
		}
		total()
		{
			for(const evPai of this.evAll())
			{
				evPai.total();
			}
		}
		
		setPoint()
		{
			this.total();
			const weightData = this.weight();
			
			for(const kind of ["dazi","yaku","ampai"])
			{
				const evPaizi = (kind === "yaku") ? this.evAll() : this.evTehai();
				const minmax = this.getMinMax(kind,evPaizi);
				
				const weight = weightData[kind];
				for(const evPai of evPaizi)
				{
					evPai.setPoint(kind,minmax[0],minmax[1],weight);
				}
			}
		}
		
		
		
		
		
		
		setHeAmpai()
		{
			const ret = [];
			const allBattlers = $gameMajiangBattlers.allBattlers()
			const heAll = allBattlers.map(b => b.he()).flat();
			for(const battler of allBattlers)
			{
				if(battler !== this.battler())
				{
					ret.push(...this.battlerAmpaiData(battler,heAll));
				}
			}
			
			this._note.ampai = ret;
		}
		
		
		battlerAmpaiData(battler)
		{
			
			const position = battler.position();
			const he = battler.he();
			
			
			
			
			
			const lizhi = battler.isLizhi();
			const dealer = battler.isDealer();
			const ret = [];
			for(const code of battler.actual())
			{
				const type = Math.floor(code / 10);
				const value = code % 10;
				if(!ret.find(d => d.code === code))
				{
					ret.push({"code" : code ,"kind" : "actual"});
				}
				
			}
			for (const code of battler.suji())
			{
				if(!ret.find(d => d.code === code))
				{
					ret.push({"code" : code, "kind" : "suji"});
				}
				
			}
			
			ret.forEach(d =>
				{
					d.position = position;
					d.lizhi = lizhi;
					d.dealer = dealer;
				});
				
			return ret;
		}
		
		
		
		setCountYaku()
		{
			const evAll = this.evAll();
			evAll.forEach(evp => evp.filterYakuStatus());
			for(const evp of evAll)
			{
				for(const s of evp.yakuStatus())
				{
					const count = this.countStatus(evAll,s.name,s.value);
					const decision = this.decisionYaku(s.name,count);
					const attention = this.attentionYaku(s.name,count);
					
					if(s.zimoEx !== 0 && !this.cpuBattler().betaori())
					{
						if(decision)
						{
							this.setDecision(s,count);
						}
						else if(attention)
						{
							this.setAttention(s,count);
						}
					}
					s.count = count;
				}
			}
			this.filterYakuDecition();
			this.setDrop();
		}
		setDrop()
		{
			const occupyDec = this.allDecision().filter(function(s)
			{
				return $gameMajiangCPU[PP_unitName].yakuData()[s.name].occupy;
			});
			
			if(occupyDec.length === 0 ) return ;
			
			for(const evp of this.evAll())
			{
				if(!evp.yakuStatus().find(s => 
					{
						for(const o of occupyDec)
						{
							if(o.name === s.name && o.value === s.value)
							{
								return true;
							}
						}
						return false;
					})
				)
				{
					evp.setDrop();
				}
			}
		}
		
		decision(...args)
		{
			return this.findAttention("decision", ...args);
		}
		
		attention(...args)
		{
			return this.findAttention("attention",...args);
		}
		findAttention(kind, ...args)
		{
			if(!this._note[kind])
			{
				return false;
			}
			for (const status of args)
			{
				if(typeof status === "object")
				{
					if(this._note[kind].some(d => 
						{
							return (d.name === status.name && d.value === status.value);
						})
					)
					{
						return true;
					}
				}
				else
				{
					if(this._note[kind].some(d => 
						{
							return (d.name === status);
						})
					)
					{
						return true;
					}
				}
				
				
			}
			return false;
		}
		
		allDecision()
		{
			return this._note.decision;
		}
		allAttention()
		{
			return this._note.attention;
		}
		
		decisionYaku(name,count)
		{
			return (this.clase().yaku[name].decision <= count);
		}
		attentionYaku(name,count)
		{
			return (this.clase().yaku[name].threshold <= count);
		}
			
		setAttention(status,count)
		{
			this._note.attention.push({"name" : status.name , "value" : status.value});
		}
		
		setDecision(status,count)
		{
			this._note.decision.push({"name" : status.name , "value" : status.value});
		}
			
		filterYakuDecition()
		{
			const yakuData = $gameMajiangCPU[PP_unitName].yakuData();
			const exclude = [];
			const excludeAttention = [];
			
			for(const decision of this._note.decision)
			{
				const decisionYakuData = yakuData[decision.name];
				excludeAttention.push(decision.name);
				exclude.push(...decisionYakuData.exclude);
			}
			this._note.decision = this._note.decision.filter(y => !exclude.includes(y.name));
			
			excludeAttention.push(...exclude);
			for(const attention of this._note.attention)
			{
				const attentionYakuData = yakuData[attention.name];
				excludeAttention.push(...attentionYakuData.exclude);
			}
			this._note.attention = this._note.attention.filter(y => !excludeAttention.includes(y.name));
		}
		
		
		countStatus(evAll,name,value)
		{
			const avoid = [];
			let ret = 0;
			
			for(const evp of evAll)
			{
				const status = evp.yakuStatus();
				
				for(const s of status)
				{
					if (s.name === name
						 && this.checkAdoptYaku(s.name)
						 && (s.value === value)
						 && (!avoid.includes(s.avoid))
						 && s.zimoEx !== 0
					)
					{
						if(s.reject) return -1;
						if(!s.noCount)
						{
							ret += 1;
						}
						
						if(s.avoid)
						{
							avoid.push(s.avoid);
						}
						
					};
				}
			}
			return ret;
		}
		
		
		
		
		setDaziStatus(evTehai)
		{
			for(const evPai of evTehai)
			{
				this.checkDanqiStatus(evPai,evTehai);
				this.checkShunziDaziStatus(evPai,evTehai);
				this.checkKeziDaziStatus(evPai,evTehai);
			}
		}
		
		
		
		checkDanqiStatus(evPai,evTehai)
		{
			const pai = evPai.pai();
			const code = pai.code();
			
			
			if(evTehai.filter(p => p.code() === code).length === 1)
			{
				const zimoEx = (this.zimoExpect(code) / 4);
				evPai.pushDaziStatus({"name" : "danqi" , "zimoEx" : zimoEx });
			}
			
			if(this.xiangtingProgress().includes(pai.code()))
			{
				evPai.pushDaziStatus({"name" : "xiangting" , "zimoEx" : 1 });
			}
			
		}
		
		checkShunziDaziStatus(evPai,evTehai)
		{
			const pai = evPai.pai();
			
			if(pai.isZipai() || pai.isSammaWanzi()) return [];
			
			const tehai = this.tehai();
			const paiId = pai.paiId();
			const code = pai.code();
			const type = pai.type();
			const value = pai.value();
			
			const ret = [];
			
			const r = {};
			const e = {};
			r[-2] = this.findShunziPai(code,type,value - 2,evTehai); //paizi.find(p => p.code() === code - 2);
			r[-1] = this.findShunziPai(code,type,value - 1,evTehai);
			r[0] = evPai;
			r[1] = this.findShunziPai(code,type,value + 1,evTehai);
			r[2] = this.findShunziPai(code,type,value + 2,evTehai);
			
			for(const i in r)
			{
				if(r[i])
				{
					r[i].pushCheckedShunzi(code);
				}
				
				const num = value + Number(i);
				
				
				if(!r[i] && (num > 0 && num < 10))
				{
					e[i] = (type * 10) + num;
				}
			}
			let zimoEx;
			let daziName = "";
			
			if(r[-2] && r[-1])
			{
				evPai.pushDaziStatus({"name" : "shunzi" , "statusCode" : r[-2].code() , "zimoEx" : 1 , "checkedPai" : [r[0].paiId() , r[-2].paiId() , r[-1].paiId()]});
			}
			if(r[-1] && r[1])
			{
				evPai.pushDaziStatus({"name" : "shunzi" , "statusCode" : r[-1].code() , "zimoEx" : 1, "checkedPai" : [r[0].paiId() , r[-1].paiId() , r[1].paiId()]});
			}
			if(r[1] && r[2])
			{
				evPai.pushDaziStatus({"name" : "shunzi" , "statusCode" : r[0].code() , "zimoEx" : 1, "checkedPai" : [r[0].paiId() , r[1].paiId() , r[2].paiId()]});
			}
			
			if(e[-2] && r[-1])
			{
				zimoEx = this.zimoExpect(e[-2]) / 4;
				daziName = (e[-2] === 7) ? "bianDazi" : "liangmianDazi";
				evPai.pushDaziStatus({"name" : daziName , "statusCode" : e[-2] , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[-1].paiId()]});
			}
			if(e[-1] && r[1])
			{
				zimoEx = this.zimoExpect(e[-1]) / 4;
				daziName = (e[-1] === 7) ? "bianDazi" : "liangmianDazi";
				evPai.pushDaziStatus({"name" : daziName , "statusCode" : e[-1] , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[1].paiId()]});
			}
			if(r[-1] && e[1])
			{
				zimoEx = this.zimoExpect(e[1]) / 4;
				daziName = (r[-1].code() === 1) ? "bianDazi" : "liangmianDazi";
				evPai.pushDaziStatus({"name" : daziName , "statusCode" : r[-1].code() , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[-1].paiId()]});
			}
			if(r[1] && e[2])
			{
				zimoEx = this.zimoExpect(e[2]) / 4;
				daziName = (r[0].code() === 1) ? "bianDazi" : "liangmianDazi";
				evPai.pushDaziStatus({"name" : daziName , "statusCode" : r[0].code() , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[1].paiId()]});
			}
			if(r[-2] && e[-1])
			{
				zimoEx = this.zimoExpect(e[-1]) / 4;
				evPai.pushDaziStatus({"name" : "qianDazi" , "statusCode" : r[-2].code() , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[-2].paiId()]});
			}
			if(e[1] && r[2])
			{
				zimoEx = this.zimoExpect(e[1]) / 4;
				evPai.pushDaziStatus({"name" : "qianDazi" , "statusCode" : r[0].code() , "zimoEx" : zimoEx , "checkedPai" : [r[0].paiId() , r[2].paiId()]});
			}
			
		};
		findShunziPai(code,type,value,evTehai)
		{
			
			const ret = evTehai.find(evp => 
			{
				const p = evp.pai();
				if((p.value() !== value ) || p.type() !== type)
				{
					return false;
				}
				
				
				if(evp.checkedShunzi(code))
				{
					return false;
				}
				return true;
				
			});
			
			return ret;
		}
		
		
		
		checkKeziDaziStatus(evPai,evTehai)
		{
			const pai = evPai.pai();
			const code = pai.code();
			
			const kezi = this.findKeziPaizi("kezi",code,evTehai,evPai)
			if(kezi)
			{
				evPai.pushDaziStatus({"name" : "kezi", "zimoEx" : 1, "statusCode" : code ,"checkedPai" : kezi.map(p => p.paiId())});
			}
			const duizi = this.findKeziPaizi("duizi",code,evTehai,evPai)
			if(duizi)
			{
				const zimoEx = this.zimoExpect(code) / 4;
				evPai.pushDaziStatus({"name" : "duizi", "zimoEx": zimoEx , "statusCode" : code , "checkedPai" : duizi.map(p => p.paiId())});
			}
			
		};
		
		findKeziPaizi(name,code,evTehai,evPai)
		{
			const ret = [evPai];
			const threshold = (name === "kezi") ? 2 : 1;
			
			const paizi = evTehai.filter(p => (p.code() === code && p !== evPai));
			const length = paizi.length;
			if(length < threshold) return null;
			if(length == threshold) return [evPai,...paizi];
			
			const checkPaizi = paizi.filter( evp =>
			{
				return evp.daziStatus().some(s => {
					return (s.name === name)
				});
				
			});
			
			if(checkPaizi.length > threshold) return null;
			return [evPai,...paizi] ;
		}
		setYakuStatus(evTehai,evFulu)
		{
			for(const evPai of evTehai)
			{
				this.setYakuStatusTehai(evPai);
			}
			this.setYakuStatusFulu(evFulu);
		}
		
		
		
		setYakuStatusFulu(evFulu)
		{
			for(const fulu of evFulu)
			{
				for(const evPai of fulu.evPaizi)
				{
					this.setYakuStatusFuluPai(evPai,fulu.fuluType,fulu.statusCode);
				}
			}
		}
		
		setYakuStatusFuluPai(evPai,fuluType,statusCode)
		{
			this.yakuStatusPai(evPai,false);
			switch(fuluType)
			{
				case "chi" : 
					this.yakuStatusShunzi(statusCode,1,false,"shunzi",evPai);
				break;
				case "peng" : 
				case "daminggang" : 
				case "angang" : 
				case "jiagang" : 
					this.yakuStatusKezi(statusCode,1,false,"kezi",evPai,fuluType);
				break;
			}
		}
		
		setYakuStatusTehai(evPai)
		{
			this.yakuStatusPai(evPai,true);
			
			for(const s of evPai.daziStatus())
			{
				const code = s.statusCode;
				const zimoEx = s.zimoEx;
				switch(s.name)
				{
					case "shunzi" : 
						this.yakuStatusShunzi(code,1,true,s.name,evPai);
					break;
					case "lianDazi" : 
					case "liangmianDazi" : 
					case "bianDazi" : 
					case "qianDazi" : 
						this.yakuStatusShunzi(code,zimoEx,true,s.name,evPai);
					break;
					case "kezi" : 
						this.yakuStatusKezi(code,1,true,s.name,evPai,"",);
					break;
					case "duizi" : 
						this.yakuStatusKezi(code,zimoEx,true,s.name,evPai,"",);
					break;
					case "danqi" : 
						this.yakuStatusDanqi(code,zimoEx,evPai);
					break;
				}
			}
		}
		
		yakuStatusShunzi(statusCode,zimoEx,isTehai,daziName,evPai)
		{
			const isDazi = (daziName !== "shunzi");
			const value = statusCode % 10;
			
			const type = Math.floor(statusCode / 10);
			
			if(($gameMajiangSystem.gamerule.kuiduan || isTehai) && (![1,7].includes(value)))
			{
				evPai.pushYakuStatus({"name" : "duanyaojiu", "zimoEx" : zimoEx ,"isDazi" : isDazi});
			}
			
			if(isTehai)
			{
				if(["shunzi","liangmianDazi"].includes(daziName))
				{
					evPai.pushYakuStatus({"name" : "pinghe", "zimoEx" : zimoEx ,"isDazi" : isDazi });
				}
			}
			else 
			{
				evPai.pushYakuStatus({"name" : "pinghe","reject" : true});
				if(value !== 1 && value !== 7)
				{
					evPai.pushYakuStatus({"name" : "chunquandai", "reject" : true});
					evPai.pushYakuStatus({"name" : "hunquandai", "reject" : true});
				}
				
			}
			if(!MajiangManager.samma())
			{
				evPai.pushYakuStatus({"name" : "sanseTongshun" ,"isDazi" : isDazi , "value" : value ,"zimoEx" : this.tongshunZimoEx(value),"avoid" : evPai.code()});
			}
			if(value === 1 || value === 7)
			{
				evPai.pushYakuStatus({"name" : "chunquandai","isDazi" : isDazi  ,"zimoEx": zimoEx });
				evPai.pushYakuStatus({"name" : "hunquandai","isDazi" : isDazi  ,"zimoEx": zimoEx});
			}
			if([1,4,7].includes(value))
			{
				evPai.pushYakuStatus({"name" : "yiqiTongguan" ,"isDazi" : isDazi , "value" : type, "zimoEx" : this.yitongZimoEx(type),"avoid" : evPai.code()});
			}
			
			
			
		}
		
		
		
		yakuStatusKezi(statusCode,zimoEx,isTehai,daziName,evPai,fuluType)
		{
			const isDazi = (daziName === "duizi");
			
			const value = statusCode % 10;
			const type = Math.floor(statusCode / 10);
			
			
			
			if(daziName === "duizi")
			{
				evPai.pushYakuStatus({"name" : "qiduizi"});
			}
			
			
			
			evPai.pushYakuStatus({"name" : "duiduihe" ,"zimoEx": zimoEx, "isDazi" : isDazi  ,"noCount" : ((isDazi) ? true : false)});
			if(isTehai)
			{
				evPai.pushYakuStatus({"name" : "anke","zimoEx": zimoEx, "isDazi" : isDazi ,"noCount" : ((isDazi) ? true : false)});
				if(!isDazi)
				{
					evPai.pushYakuStatus({"name" : "gangzi","noCount" : true});
				}
			}
			
			if(($gameMajiangSystem.gamerule.kuiduan || isTehai) && (![1,9].includes(value)) && type !== 3)
			{
				evPai.pushYakuStatus({"name" : "duanyaojiu",  "zimoEx" : zimoEx, "isDazi" : isDazi});
			}
			
			if(!isTehai)
			{
				evPai.pushYakuStatus({"name" : "pinghe","reject" : true});
				
				if(type !== 3)
				{
					if(value !== 1 && value !== 9)
					{
						evPai.pushYakuStatus({"name" : "chunquandai", "reject" : true});
						evPai.pushYakuStatus({"name" : "hunquandai","reject" : true});
						evPai.pushYakuStatus({"name" : "hunlaotou", "reject" : true});
						evPai.pushYakuStatus({"name" : "qinglaotou", "reject" : true});
						
					}
				}
				
				if(fuluType !== "angang")
				{
					evPai.pushYakuStatus({"name" : "anke", "reject" : true});
				}
			}
				
			if(type !== 3 && (value === 1 || value === 9))
			{
				evPai.pushYakuStatus({"name" : "chunquandai", "isDazi" : isDazi  ,"zimoEx": zimoEx});
				evPai.pushYakuStatus({"name" : "hunquandai","isDazi" : isDazi  ,"zimoEx": zimoEx});
			}
			else if(type === 3)
			{
				evPai.pushYakuStatus({"name" : "hunquandai","isDazi" : isDazi  ,"zimoEx": zimoEx});
				evPai.pushYakuStatus({"name" : "hunlaotou","isDazi" : isDazi  ,"zimoEx": zimoEx});
				for(const v of [0,1,2])
				{
					evPai.pushYakuStatus({"name" : "hunyise" , "value" : v , "zimoEx" : zimoEx});
				}
			}
			if(["angang","jiagang","daminggang"].includes(fuluType))
			{
				evPai.pushYakuStatus({"name" : "gangzi"});
			}
			
			
			
			
			if(type !== 3)
			{
				evPai.pushYakuStatus({"name" : "sanseTongke" , "value" : value  , "isDazi" : isDazi ,"zimoEx" : this.tongkeZimoEx(value)});
			}
			if(this.isYipai(statusCode))
			{
				evPai.pushYakuStatus({"name" : "yipai", "value" : statusCode , "zimoEx": (zimoEx / 4) });
			}
		}
		yakuStatusDanqi(statusCode,zimoEx,evPai)
		{
			evPai.pushYakuStatus({"name" : "qiduizi" , "amount" : 0 , "noCount" : true});
		}
		
		isYipai(code)
		{
			if(code < 30) return false;
			const pai = $gameMajiangPaishan.makeDummyPai(code);
			return pai.isYipai(this.battler());
		}
		
		
		yakuStatusPai(evPai,isTehai)
		{
			
			const pai = evPai.pai();
			
			const type = pai.type();
			const value = pai.value();
			const code = pai.code();
			const otherColor = [0,1,2].filter(v => v !== type) ;
			const zimoEx = this.zimoExpect(code);
			
			
			
			if(!isTehai && (!$gameMajiangSystem.gamerule.kuiduan || !pai.isZhongzhang()))
			{
				evPai.pushYakuStatus({"name" : "duanyaojiu" , "reject" : true});
			}
			
			if(!isTehai)
			{
				
				evPai.pushYakuStatus({"name" : "guoshiwushuang", "reject" : true});
				evPai.pushYakuStatus({"name" : "qiduizi", "reject" : true});
				
				
				if(pai.isZipai())
				{
					for(const v of [0,1,2])
					{
						evPai.pushYakuStatus({"name" : "qingyise", "value" : v ,"reject" : true});
					}
				}
				if(pai.isShupai())
				{
					evPai.pushYakuStatus({"name" : "ziyise", "reject" : true});
					for(const other of otherColor)
					{
						evPai.pushYakuStatus({"name" : "qingyise", "value" : other ,"reject" : true});
						evPai.pushYakuStatus({"name" : "hunyise" , "value" : other,"reject" : true});
					}
				}
				if(!pai.isLaotou())
				{
					evPai.pushYakuStatus({"name" : "qinglaotou","reject" : true});
				}
				if(!pai.isYaojiu())
				{
					evPai.pushYakuStatus({"name" : "hunlaotou","reject" : true});
				}
				if(!pai.constituteLuyise())
				{
					evPai.pushYakuStatus({"name" : "luyise" ,"reject" : true});
				}
				
			}
			
			if(pai.isYaojiu())
			{
				if(pai.isLaotou())
				{
					evPai.pushYakuStatus({"name" : "chunquandai"});
					evPai.pushYakuStatus({"name" : "hunquandai"});
					evPai.pushYakuStatus({"name" : "qinglaotou"});
					evPai.pushYakuStatus({"name" : "hunlaotou"});
				}
				if(pai.isZipai())
				{
					evPai.pushYakuStatus({"name" : "ziyise"});
				}
			}
			if(pai.isShupai())
			{
				evPai.pushYakuStatus({"name" : "hunyise" , "value" : type});
				evPai.pushYakuStatus({"name" : "qingyise" , "value" : type});
			}
			
			if(pai.isYaojiu())
			{
				if(isTehai)
				{
					evPai.pushYakuStatus({"name" : "guoshiwushuang", "zimoEx": this.guoshiZimoEx() , "avoid" : code});
				}
			}
			if(pai.dora() > 0)
			{
				evPai.pushYakuStatus({"name" : "dora", "value" : pai.paiId() });
			}
			if(pai.isSanyuan())
			{
				evPai.pushYakuStatus({"name" : "sanyuan", "zimoEx": this.sanyuanZimoEx() });
			}
			if(pai.isFeng())
			{
				evPai.pushYakuStatus({"name" : "sixi", "zimoEx": this.sixiZimoEx() });
			}
			if(pai.constituteLuyise())
			{
				evPai.pushYakuStatus({"name" : "luyise" });
			}
			
		}
		
		setAmpaiStatus(evTehai)
		{
			for(const evPai of evTehai)
			{
				const code = evPai.code();
				const status = this._note.ampai.filter(s => s.code === code);
				
				evPai.setAmpaiStatus(status);
			}
		}
		
		
		guoshiZimoEx()
		{
			const tehaiCode = this.tehai().map(p => p.code());
			const yaojiuCode = [1,9,11,19,21,29,31,32,33,34,35,36,37].filter(v => !tehaiCode.includes(v));
			if(yaojiuCode.length === 0) return 1;
			let count = 0;
			for(const code of yaojiuCode)
			{
				const ex = this.zimoExpect(code);
				if(ex === 0) return 0;
				count += (ex / 4);
			}
			
			return count / yaojiuCode.length;
		}
		sanyuanZimoEx()
		{
			
			let ret = 0;
			let count = 0;
			for(const code of [35,36,37])
			{
				const zimoEx = this.keziZimoEx(code);
				
				if(zimoEx < (1/2))
				{
					return 0;
				}
				if(zimoEx <= (1/2))
				{
					count++;
					if(count >= 2)
					{
						return 0;
					}
				}
				ret += zimoEx;
			}
			
			return ret / 3;
		}
		sixiZimoEx()
		{
			
			let ret = 0;
			let count = 0;
			for(const code of [31,32,33,34])
			{
				const zimoEx = this.keziZimoEx(code);
				if(zimoEx < (1/2))
				{
					return 0;
				}
				if(zimoEx <= (1/2))
				{
					count++;
					if(count >= 2)
					{
						return 0;
					}
				}
				ret += zimoEx;
			}
			
			return ret / 4;
		}
		
		
		tongshunZimoEx(value)
		{
			if(value > 7) return 0;
			let ret = 0;
			for(const color of [0,1,2])
			{
				const code = (color * 10) + value ;
				
				const zimoEx = this.shunziZimoEx(code);
				if(zimoEx === 0 )return 0;
				ret += zimoEx;
				
			}
			return ret / 3;
		}
		tongkeZimoEx(value)
		{
			let ret = 0;
			for(const color of [0,1,2])
			{
				const code = (color * 10) + value ;
				
				const zimoEx = this.keziZimoEx(code);
				if(zimoEx === 0 )return 0;
				ret += zimoEx;
			}
			return ret / 3;
		}
		
		shunziZimoEx(code)
		{
			if((code % 10) > 7) return 0;
			
			if(this.fulu().some(m => (m.isShunzi() && m.paizi()[0].code() === code)))
			{
				return 1;
			}
			else
			{
				let ret = 0;
				for(const addVal of [0 , 1 , 2])
				{
					const sCode = code + addVal;
					if(this.tehai().some(p => p.code() === sCode))
					{
						ret += 1;
					}
					else
					{
						const zimoExpect = this.zimoExpect(sCode);
						if(zimoExpect === 0) return 0;
						ret += (zimoExpect / 4);
					}
				}
				return ret / 3;
			}
			
		}
		keziZimoEx(code)
		{
			
			if(this.fulu().some(m => (m.isKezi() && m.keziCode() === code)))
			{
				return 1;
			}
			else
			{
				const zimoEx = this.zimoExpect(code);
				const tehaiNum = this.tehai().filter(p => p.code() === code).length;
				
				return Math.min(1 , ((tehaiNum + zimoEx)  / 4)) ;
			}
			
		}
		setYitongZimoEx()
		{
			this._note.yitongZimoEx = [];
			for(const color of [0,1,2])
			{
				let ret = 0;
				for(const shunzi of [1,4,7])
				{
					const code = (color * 10) + shunzi;
					const zimoEx = this.shunziZimoEx(code);
					if(zimoEx === 0 )
					{
						ret = 0;
						break;
					}
					else
					{
						ret += zimoEx;
					}
				}
				this._note.yitongZimoEx[color] = (ret / 3);
			}
		}
		yitongZimoEx(color)
		{
			return this._note.yitongZimoEx[color];
		}
		
		statusText()
		{
			const text = [];
			for(const evPai of this.evTehai())
			{
				text.push(...evPai.statusText());
			}
			return text;
		}
		
		
		
		
	}
	
	
	class MajiangCPUBattler
	{
		constructor(position)
		{
			const battler = $gameMajiangBattlers.battler(position);
			this._position = position;
			this._battler = battler;
			
			
			this._heleMoment = 0;
			this.clearText();
		}
		
		battler()
		{
			return this._battler;
		}
		
		
		cpuPaizi()
		{
			return $gameMajiangCPU[PP_unitName].paizi(this._position);
		}
		updatePeipai()
		{
			this.updateZimo();
			const battler = this._battler;
			this.refreshData(battler.tehai(),battler.fulu());
			
			
		}
		updateZimo()
		{
			this.refreshXiangting();
			this.refreshCPUClass();
			this.refreshHeleMoment();
		}
		
		updateAfterFulu()
		{
			this.updateZimo();
		}
		refreshXiangting()
		{
			this.cpuPaizi().refreshXiangting(this._battler.tehai());
		}
		
		currentXiangting()
		{
			return this.cpuPaizi().currentXiangting();
		}
		
		countXiangting(tehai)
		{
			return PF.countXiangting(tehai.map(p => p.code()));
		}
		
		refreshData(tehai,fulu)
		{
			this.cpuPaizi().refreshData(tehai,fulu);
		}
		
		evAll()
		{
			return this.cpuPaizi().evAll();
		}
		evTehai()
		{
			return this.cpuPaizi().evTehai();
		}
		evFulu()
		{
			return this.cpuPaizi().evFulu();
		}
		
		position()
		{
			return this._position;
		}
		basicCPUClass()
		{
			return !this._battler.cpuClass();
		}				
		
		refreshCPUClass()
		{
			const basic = this._battler.cpuClass();
			
			if(!basic)
			{
				this._currentCpuClass = this.position();
			}
			else
			{
				this._currentCpuClass = this.findFlexClass(basic);
			}
		}
		
		findFlexClass(basicName)
		{
			const basic = $gameMajiangCPU[PP_unitName].cpuClass(basicName);
			const currentClassName = this._currentCpuClass?.name || basicName
			for(const flex of basic.flex)
			{
				this.pushText("打ち筋変動 %1 の条件の確認を行います 現在の打ち筋 %2",flex.cpuClass,currentClassName);
				if(this.checkFlex(flex.conditions,flex.cpuClass))
				{
					this.echo();
					return flex.cpuClass;
				}
			}
			this.echo();
			return basicName;
			
		}
		checkFlex(conditions,subject)
		{
			const c = PF.checkCondition();
			c.set(this);
			const check = c.checkCondition(conditions);
			for(const text of c.text())
			{
				this.pushText(text);
			}
			
			if(check)
			{
				this.pushText("条件を満たしました %1を読み込みます".format(subject) );
			}
			return check;
		}
		
		clase()
		{
			return $gameMajiangCPU[PP_unitName].cpuClass(this._currentCpuClass);
		}
		
		
		
		decision(...args)
		{
			return this.cpuPaizi().decision(...args);
		}
		allDecision()
		{
			return this.cpuPaizi().allDecision();
		}
		allAttention()
		{
			return this.cpuPaizi().allAttention();
		}
		
		
		
		
		
		
		
		
		betaori()
		{
			return (this.heleMoment() <= this.clase().perspective.defThreshold) ; 
		}
		
		zentsu()
		{
			return (this.heleMoment() >= this.clase().perspective.atkThreshold) ; 
		}
		
		refreshHeleMoment()
		{
			const clase = this.clase();
			const zijia = this.battler();
			if(zijia.isLizhi())
			{
				this._heleMoment = clase.perspective.atkThreshold;
				return;
			}
			let point = 0;
			const tajia = $gameMajiangBattlers.allBattlers().filter(b => b !== zijia);
			for(const battler of tajia)
			{
				if(battler.isLizhi())
				{
					if(battler.isDealer())
					{
						point += clase.perspective.lizhi_dealer;
					}
					else
					{
						point += clase.perspective.lizhi;
					}
				}
				else
				{
					for(const fulu of battler.fulu())
					{
						if(battler.isDealer())
						{
							point += clase.perspective.fulu_dealer;
						}
						else
						{
							point += clase.perspective.fulu;
						}
					}
				}
			}
			const xiangting = this.currentXiangting();
			if(xiangting === 0)
			{
				point += clase.perspective.tingpai;
			}
			else if(xiangting === 1)
			{
				point += clase.perspective.yixiangting;
			}
			else if(xiangting === 2)
			{
				point += clase.perspective.liangxiangting;
			}
			if(zijia.isDealer())
			{
				point += clase.perspective.dealer;
			}
			this._heleMoment = point;
		}
		
		heleMoment()
		{
			return this._heleMoment;
		}
		
		
		
		
		
		
		
		
		
		
		
		
		zimogiri()
		{
			return !!(this.clase().traits.zimogiri);
		}
		
		dapai()
		{
			
			if(this.zimogiri())
			{
				const tehai = this._battler.tehai();
				let decision = tehai.find(p => p.isZimoPai());
				if(!decision)
				{
					decision = tehai[tehai.length - 1];
				}
				if(this.echoConsole())
				{
					this.pushText("設定により自摸切りします");
					this.echo();
				}
				return decision.paiId();
			}
			
			this.refreshData(this._battler.tehai(),this._battler.fulu());
			const dapai = this.cpuPaizi().findDapai(true);
			if(!dapai)
			{
				return null;
			}
			if(this.echoConsole())
			{
				this.echoDapaiStatus(dapai);
			}
			
			return dapai.paiId();
		}
		
		
		echo()
		{
			
			if(this.echoConsole())
			{
				$gameMajiangSystem.echo(...this._text);
			}
			this.clearText();
		}
		
		echoConsole()
		{
			return this._battler.echoConsole();
		}
		clearText()
		{
			this._text = [];
		}
		pushText(text , ...args)
		{
			if(this.echoConsole())
			{
				this._text.push(text.format(...args));
			}
		}
		flv(value)
		{
			const digit = 10000;
			return Math.floor(value * digit) / digit;
		}
		
		atkStatusText()
		{
			if(this.betaori())
			{
				return "ベタオリ";
			}
			else if(this.zentsu())
			{
				return "全ツ";
			}
			else
			{
				return "標準";
			}
		}
		statusText()
		{
			const text = [];
			const name = this._battler.name();
			text.push(PF.horzLine());
			text.push("%1のステータス".format(name));
			
			
			text.push("現在の状態 %1".format(this.atkStatusText()));
			let cpuText = this._currentCpuClass;
			if(this.basicCPUClass())
			{
				cpuText = "basic";
			}
			
			text.push("現在の打ち筋 %1".format(cpuText));
			text.push("現在の和了気運値 %1".format(this.heleMoment()));
			text.push("現在の向聴数 %1".format(this.currentXiangting()));
			text.push("現在意識中の役");
			const yakuText = [];
			for(const s of this.allDecision())
			{
				const yakuName = PF.textYaku(s.name,s.value);
				if(!yakuText.includes(yakuName))
				{
					yakuText.push(yakuName);
					text.push("・%1 状態 : 決心".format(yakuName));
				}
				
			}
			for(const s of this.allAttention())
			{
				const yakuName = PF.textYaku(s.name,s.value);
				if(!yakuText.includes(yakuName))
				{
					yakuText.push(yakuName);
					text.push("・%1 状態 : 意識".format(yakuName));
				}
				
			}
			
			text.push("手牌コード %1".format(JSON.stringify(this._battler.tehai().map(p => p.code()))));
			
			
			return text;
		}
		echoDapaiStatus(dapai)
		{
			
			let text = [];
			text.push("%1 による打牌".format(this._battler.name()));
			text.push("打牌評価内訳を表示します");
			text.push(...this.cpuPaizi().statusText());
			text.forEach(t => this.pushText(t) , this);
			
			this.echo();
			text = [];
			text.push(...this.statusText());
			const bValue = this.cpuPaizi().bValue();
			if(bValue)
			{
				text.push("弱さ補正により最善から %1番目の牌を選択しました".format(bValue + 1));
			}
			
			text.push("以上の評価により %1の打牌を選択しました".format(PF.textPai(dapai.pai())));
			
			text.forEach(t => this.pushText(t) , this);
			
			this.echo();
			return text;
		}
		
		zimoExpect(code,exTehai)
		{
			return this.cpuPaizi().zimoExpect(code,exTehai);
		}
		
		
		
		//------------------------------
		
		
		
		fuluAction(actions,dapaiPosition)
		{
			this.refreshXiangting();
			this.refreshCPUClass();
			this.pushText("%1による副露アクション評価を開始します",this._battler.name());
			const ret = this.queryAction(actions,dapaiPosition);
			this.echo();
			return ret;
		}
		
		zimoAction(actions)
		{
			this.pushText("%1による自摸アクション評価を開始します",this._battler.name());
			const ret = this.queryAction(actions,null);
			this.echo();
			return ret;
		}
			
		textAction(a)
		{
			let text = "";
			const paizi = [];
			switch(a.kind)
			{
				case "chi" : 
				case "peng" : 
				case "daminggang" : 
					paizi.push(a.dapai.code() , ...a.paizi.map(p => p.code()));
				break ; 
				case "angang" : 
				case "jiagang" : 
					paizi.push(a.paizi[0].code());
				break;
			}
			for(const code of paizi)
			{
				text += PF.textPai(code) + " ";
			}
			if(paizi.length)
			{
				text += "の";
			}
			
			text += PF.textAction(a.kind);
			return text;
		}
		
		
		queryAction(actions,dapaiPosition)
		{
			
			const dapaijia = (dapaiPosition !== null) ? $gameMajiangBattlers.battler(dapaiPosition) : null;
			this.refreshData(this._battler.tehai(),this._battler.fulu());
			for(const action of actions)
			{
				let textAction = "";
				if(this.echoConsole ())
				{
					textAction += this.textAction(action)
				}
				
				this.pushText("%1が可能です",textAction);
				
				
				if(this.checkActionCondition(action))
				{
					this.pushText("%1の基本条件評価を開始します",textAction);
					
					if(this["queryAction_" + action.kind](action,dapaijia))
					{
						this.pushText("%1は全ての条件を通過したため実行します",textAction);
						
						return action;
					}
				}
				this.pushText("%1は条件を満たさなかったため実行しません",textAction);
			}
			
			return null;
		}
		
		checkActionCondition(action)
		{
			this.pushText("アクションの追加条件評価を開始します");
			
			const actionData = this.clase().action.filter(a => a.subject === action.kind);
			const length = actionData.length;
			if(length === 0)
			{
				this.pushText("追加条件が設定されていません 通過します");
				return true;
			}
			const c = PF.checkCondition();
			const evTehai = this.evTehai();
			c.set(this,action,evTehai);
			
			for(let i = 0; i < length ; i++)
			{
				this.pushText("追加条件%1番の評価を開始します",(i + 1));
				const data = actionData[i];
				
				const check = c.checkCondition(data.conditions);
				for(const text of c.text())
				{
					this.pushText(text);
				}
				
				if(check)
				{
					this.pushText("追加条件%1番を満たしました",(i + 1));
					return true;
				}
			}
			return false;
		}
		
		
		
		queryAction_rong(action,dapaijia)
		{
			const rongpai = action.dapai;
			this.pushText("栄和了評価を開始します");
			
			const ret = this.thoughtHele(this._battler.tehai(),null,rongpai,dapaijia)
			this.pushText("栄和了評価を終了します 結果は [ %1 ] でした",((ret) ? "実行する" : "実行しない"));
			
			return ret;
		}
		queryAction_zimo(action)
		{
			this.pushText("自摸和了評価を開始します");
			const ret = this.thoughtHele(this._battler.tehai(),null);
			this.pushText("自摸和了評価を終了します 結果は [ %1 ] でした" ,((ret) ? "実行する" : "実行しない"));
			
			return ret;
		}
		
		thoughtHele(tehai,lizhiAction,rongpai,fangchongjia)
		{
			const isLizhi = !!lizhiAction;
			const consoleText = (isLizhi) ? "立直" : "和了";
			const copyAllBattlers = this.cleateCopyAllBattlers();
			
			
			const copyBattler = copyAllBattlers.find(b => b.position() === this._battler.position());
			let copyFangchongjia;
			if(fangchongjia)
			{
				copyFangchongjia = copyAllBattlers.find(b => b.position() === fangchongjia.position());
			}
			let notLizhiHeleResult;
			if(isLizhi)
			{
				notLizhiHeleResult = copyBattler.helePoint(tehai,rongpai);
				copyBattler.applyLizhi(lizhiAction);
				copyBattler.setYifa(false);
			}
			
			
			const heleResult = copyBattler.helePoint(tehai,rongpai);
			copyBattler.setHeleResult(heleResult);
			
			const estimate = this.estimateHele(copyBattler,copyFangchongjia,copyAllBattlers);
			
			
			const lowerPoint = (isLizhi) ? this.clase().hele.lizhiPointLower : this.clase().hele.helePoint;
			
			if(lowerPoint >= estimate.gainPoint)
			{
				this.pushText("和了予想点が閾値以下のため %1を行いませんでした 予想点 : %2 , 閾値 , %3",consoleText,estimate.gainPoint , lowerPoint);
				return false;
			}
			const upperPoint = this.clase().hele.lizhiPointUpper;
			if(isLizhi && upperPoint !== -1)
			{
				if(upperPoint <= notLizhiHeleResult.point.gainPoint)
				{
					this.pushText("和了予想点がダマ閾値以上のため %1を行いませんでした 予想点 : %2 , 閾値 , %3",consoleText,notLizhiHeleResult.point.gainPoint , upperPoint);
					return false;
				}
			}
			if(MajiangManager.currentAllLast() || estimate.tobi)
			{
				switch(this.clase().hele.allLast)
				{
					case 1 :
						//ラス確での和了を避けるの場合
						if(estimate.las)
						{
							this.pushText("和了予想がラス確定になるため %1を行いませんでした",consoleText);
							return false;
						}
					break;
					case 2 : 
						//トップ確定でないと和了しないの場合
						if(!estimate.top)
						{
							this.pushText("和了予想がトップにならないと判断したため %1を行いませんでした",consoleText);
							return false;
						}
					break;
				}
			}
			return true;
		}
		
		cleateCopyAllBattlers()
		{
			const copyAllBattlers = $gameMajiangBattlers.allBattlers().map(b => 
			{
				const battler = JsonEx.makeDeepCopy(b);
				battler.setTehai(b.tehai());
				battler.setFulu(b.fulu());
				return battler;
			});
			return copyAllBattlers;
		}
		
		estimateHele(helejia,fangchongjia,allBattlers)
		{
			const currentPoint = helejia.point();
			if(fangchongjia)
			{
				MajiangManager.resultPointRong([helejia],fangchongjia,allBattlers);
			}
			else
			{
				MajiangManager.resultPointZimo([helejia],allBattlers);
			}
			
			
			const ranking = MajiangManager.ranking(allBattlers);
			const topPosition = ranking[0].position();
			const lastBattler = ranking[ranking.length - 1]
			const lastPosition = lastBattler.position();
			const zijiaPosition = this.position();
			
			const zijiaPoint = helejia.point();
			const lasPoint = lastBattler.point();
			
			return  {
				"top" : (zijiaPosition === topPosition),
				"las" : (zijiaPosition === lastPosition),
				"gainPoint" : zijiaPoint - currentPoint,
				"tobi" : ($gameMajiangSystem.gamerule.tobi && lasPoint < 0)
			};
		}
		
		
		queryAction_lizhi(action)
		{
			this.pushText("立直評価を開始します");
			const lizhiTrait = this.clase().traits.lizhi;
			if(lizhiTrait === 1)
			{
				this.pushText("立直特徴が 「必ずする」のため 行います");
				return true;
			}
			else if(lizhiTrait === -1)
			{
				this.pushText("立直特徴が 「しない」のため 行いません");
				return false;
			}
			
			const lizhiThreshold = this.clase().perspective.lizhiThreshold;
			if(this.heleMoment() <= lizhiThreshold)
			{
				this.pushText("和了気運が閾値以下のため 立直を行いませんでした 和了気運 %1 , 閾値 : %2",this.heleMoment() , lizhiThreshold);
				return false;
			}
			this.pushText("次の打牌予定牌を検索します");
			
			const nextDapaiId = this.cpuPaizi().findDapai(false).paiId();
			
			
			const select = this._battler.canTingpaiResult().find(r => r.qiepai.paiId() === nextDapaiId);
			if(!select)
			{
				this.pushText("次の打牌予定牌が 立直宣言可能な牌ではなかったため 立直を行いませんでした");
				return false;
			}
			
			const zimoEx = select.machi.reduce( (r,c) => r + this.zimoExpect(c,true), 0);
			
			if(zimoEx <= this.clase().hele.lizhiShengyu)
			{
				this.pushText("待ち牌の残数が閾値以下のため 立直を行いませんでした 残余 : %1 , 閾値 : %2",zimoEx , this.clase().hele.lizhiShengyu);
				return false;
			}
			
			const tempTehai = this._battler.tehai().filter(p => p.paiId() !== nextDapaiId);
			
			let dapaiText = "";
			let machihaiText = "";
			
			const heleDec = select.machi.every(machi =>
			{
				const machihai = $gameMajiangPaishan.makeDummyPai(machi);
				
				machihai.setZimoPai(true);
				
				if(this.echoConsole())
				{
					dapaiText = PF.textPai($gameMajiangPaishan.allData().find(p => p.paiId() === nextDapaiId));
					machihaiText = PF.textPai(machihai);
				}
				
				
				const tempTehai2 = [...tempTehai , machihai];
				if(this.echoConsole())
				{
					this.pushText("%1を打牌し %2を自摸和了した場合の和了シミュレーションを開始します",dapaiText , machihaiText);
				}
				const ret = this.thoughtHele(tempTehai2,action);
				if(this.echoConsole())
				{
					this.pushText("%1を打牌し %2を自摸和了した場合の和了シミュレーションを終了します 結果は [ %3 ] でした",dapaiText , machihaiText , ((ret) ? "実行する" : "実行しない"));
				}
				return ret;
			},this);
			if(this.echoConsole())
			{
				this.pushText("立直評価を終了します 結果は [ %1 ] でした",((heleDec) ? "%1を打牌して実行する".format(dapaiText) : "実行しない"));
			}
			
			return heleDec;
		}
		
		makeTempFulu(paizi,dapai,kind,dapaijia)
		{
			
			const relativePosition = this._battler.relativePosition(dapaijia);
			
			dapai.setFulu(relativePosition , kind);
			dapai.setEnabled(false);
			
			const fulu = $gameMajiangPaishan.makeMianzi([...paizi , dapai]);
			return fulu;
		}
		
		queryAction_chi(action,dapaijia)
		{
			return  this.evaluateFulu(action,dapaijia,true);
		}
			
		queryAction_peng(action,dapaijia)
		{
			return  this.evaluateFulu(action,dapaijia);
		}
		checkXiangtingEffect(tempTehai,maintain)
		{
			const current = this.currentXiangting();
			const after = this.countXiangting(tempTehai);
			const check = ((maintain) ? (current >= after) : (current > after));
			if(!check)
			{
				this.pushText("向聴数に有効ではなかったのでアクションを行いませんでした 現在向聴 : %1 副露後向聴 : %2",current , after);
				return false;
			}
			return true;
		}
		checkFuluTrait()
		{
			const fuluTrait = this.clase().traits.fulu;
			if(fuluTrait === 1)
			{
				this.pushText("副露特徴が 「必ずする」のため 行います");
				return true;
			}
			else if(fuluTrait === -1)
			{
					
				this.pushText("副露特徴が 「しない」のため 行いません");
				return false;
			}
			return null;
		}
		
		
		evaluateFulu(action,dapaijia,isChi)
		{
			const fuluTrait = this.checkFuluTrait();
			if(fuluTrait !== null)
			{
				return fuluTrait;
			}
			const allPaizi = this._battler.allPaizi();
			//const minYakuPoint = Math.min(...allPaizi.map(p => p.evalCPU().point.yaku));
			
			const dapai = JsonEx.makeDeepCopy(action.dapai);
			const tempTehai = this._battler.tehai().filter(p => !action.paizi.includes(p));
			const tempFulu = this._battler.fulu().concat(this.makeTempFulu(action.paizi , dapai, action.kind , dapaijia));
			
			const checkXiangting = this.checkXiangtingEffect(tempTehai)
			if(!checkXiangting)
			{
				return false;
			}
			
			this.refreshData(tempTehai , tempFulu);
			
			const evDapai = this.evAll().find(p => p.paiId() === dapai.paiId());
			
			const yakuStatus = evDapai.yakuStatus().filter(y => !y.reject);
			
			const invalid = yakuStatus.every(s => 
			{
				if(this.decision(s) && this.clase().yaku[s.name].fulu)
				{
					this.pushText("この牌は次の役に有効です %1",PF.textYaku(s.name,s.value));
					return false;
				}
				return true;
				
			},this);
			if(invalid)
			{
				this.pushText("有効な役が見つからないため副露を行いません");
				return false;
			}
			
			
			this.pushText("基本条件を満たしました");
			
			return true;
			
		}
		
		
		
		
		
		queryAction_daminggang(action)
		{
			const fuluTrait = this.checkFuluTrait();
			if(fuluTrait !== null)
			{
				return fuluTrait;
			}
			const tempTehai = this._battler.tehai().filter(p => !action.paizi.includes(p));
			const checkXiangting = this.checkXiangtingEffect(tempTehai,true);
			if(!checkXiangting)
			{
				return false;
			}
			
			this.pushText("基本条件を満たしました");
			return true;
		}
		
		
		
		
		
		
		
		queryAction_jiuzhongJiupai(action)
		{
			return true;
		}
		
		queryAction_angang(action)
		{
			const tempTehai = this._battler.tehai().filter(p => !action.paizi.includes(p));
			const checkXiangting = this.checkXiangtingEffect(tempTehai,true);
			if(!checkXiangting)
			{
				return false;
			}
			
			this.pushText("基本条件を満たしました");
			return true;
		}
		
		queryAction_jiagang(action)
		{
			const tempTehai = this._battler.tehai().filter(p => !action.paizi.includes(p));
			const checkXiangting = this.checkXiangtingEffect(tempTehai,true);
			if(!checkXiangting)
			{
				return false;
			}
			this.pushText("基本条件を満たしました");
			return true;
			
		}
		
		
		
	}
	
	
	class Condition
	{
		constructor()
		{
			this.clear();
		}
		clear()
		{
			this._n = false;
			this._tx = "";
			this._text = [];
			this._evTehai = [];
			this._action = null;
			this._evDapai = null;
			this._targetEvTehai = [];
		}
		set(cpuBattler,action,evTehai)
		{
			this.clear();
			this.setStatus(cpuBattler,action,evTehai);
		}
		setStatus(cpuBattler,action,evTehai)
		{
			this._cpuBattler = cpuBattler;
			this._battler = cpuBattler.battler();
			this._evTehai = evTehai;
			this._action = action;
			
			if(action)
			{
				this.setActionPaizi(action);
			}
			
		}
		setActionPaizi(action)
		{
			if(action.dapai)
			{
				const dapaiId = action.dapai.paiId();
				this._dapai = action.dapai;
				this._evDapai = this._evTehai.find(p => p.paiId() === dapaiId);
			}
			if(action.paizi)
			{
				const paiziId = action.paizi.map(p => p.paiId());
				this._targetTehai =  action.paizi;
				this._targetEvTehai = this._evTehai.filter(p => paiziId.includes(p.paiId()));
			}
		}
		text()
		{
			return this._text;
		}
		
		
		checkCondition(keys)
		{
			for(const key of keys)
			{
				const method = key.split("_");
				const methodName = method[0];
				const anti = (method[1] === "n");
				let antiText = "";
				if(!this[methodName])
				{
					throw new Error("次のメソッドが見つかりません %1".format(methodName));
				}
				const args = method[2];
				let ret = this[methodName](args);
				
				if(anti)
				{
					antiText += "ではない";
					ret = !ret;
				}
				const conditionText = "条件 [ %1 ]%2 ".format(this._tx , antiText);
				
				if(!ret)
				{
					this._text.push(conditionText + "は通過しませんでした");
					return false;
				}
				this._text.push(conditionText + "は通過しました");
			}
			return true;
		}
		
		choiceTrue()
		{
			this._tx = "無条件にTrue";
			return true;
		}
		
		zentsu()
		{
			this._tx = "全ツ中";
			return this._cpuBattler.zentsu();
		}
		betaori()
		{
			this._tx = "ベタオリ中";
			return this._cpuBattler.betaori();
		}
		top()
		{
			this._tx = "トップ目";
			return (MajiangManager.topBattler() === this._battler);
		}
		
		last()
		{
			this._tx = "ラス目";
			return (MajiangManager.lastBattler() === this._battler);
		}
		
		dealer()
		{
			this._tx = "自身が親";
			return this._battler.isDealer();
		}
		
		shengyu(value)
		{
			this._tx = "残り" + value + "枚以下";
			return ($gameMajiangPaishan.shengyu() >= Number(value));
		}
			
		xiangting(value)
		{
			this._tx = "現在" + value + "向聴以下";
			return (this._cpuBattler.currentXiangting() >= Number(value));
		}
		
		lastHele()
		{
			this._tx = "前局で和了している";
			return (this._battler.lianHele() >= 1);
		}
		menqian()
		{
			this._tx = "門前";
			return this._battler.isMenqian();
		}
		allLast()
		{
			this._tx = "オーラス";
			return MajiangManager.currentAllLast();
		}
		yakitori()
		{
			this._tx = "焼き鳥";
			return this._battler.yakitori();
		}
		
		avoidFulu()
		{
			this._tx = "門前役を決心している";
			return this._cpuBattler.decision("guoshi","qiduizi","pinghe","anke");
		}
		avoidAngang() 
		{
			this._tx = "暗槓を避ける役を決心している";
			return this._cpuBattler.decision("guoshi","qiduizi","pinghe");
		}
		checkDaziStatus(...status)
		{
			return this._targetEvTehai.some( p => 
			{
				return p.daziStatus().some( s =>
				{
					return (status.includes(s.name));
				});
			});
		}
		
		decision(yaku)
		{
			this._tx = PF.textYaku(yaku) + "を決心している";
			return this._cpuBattler.decision(yaku);
		}
		
		
		quanFeng(feng)
		{
			const nFeng = Number(feng);
			this._tx = PF.textFeng(feng) + "場"; 
			return (MajiangManager.currentQuanfeng() === nFeng);
		}
		yomma()
		{
			this._tx = "四麻"; 
			return (!MajiangManager.samma());
		}
		samma()
		{
			this._tx = "三麻"; 
			return (MajiangManager.samma());
		}
		dapaiDora()
		{
			this._tx = "対象の打牌がドラ";
			return this._dapai.dora();
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
 })();