#include <clip.h>
#include <clipmisc.c>
#include <cliptlz.c>
#include <clipwell.c>
#include <clipslot.c>
//#include <clipsl5n.c>
#include "SlotData.h"

//==============================================================================
//■絵柄
//==============================================================================
//絵柄名称を取得する。
//[in]
//	iOrder		絵柄番号	0~(Pict_Size-1)
//[out]
//	戻り値		絵柄名称
const char* GetPict_Name(int iPict) {
	if((unsigned)iPict >= Pict_Size) { DIE(); }
	return TBL_Pict_Name[iPict];
}

//==============================================================================
//■条件装置
//==============================================================================
//条件装置名称を取得する。
//[in]
//	iOrder		条件装置番号	0~(HitCd_Size-1)
//[out]
//	戻り値		条件装置名称
const char* GetHitCd_Name(int iHitCd) {
	if((unsigned)iHitCd >= HitCd_Size) { DIE(); }
	return TBL_HitCd_Name[iHitCd];
}
//------------------------------------------------------------------------------
//条件装置を抽選する。
//[in]
//	iRank		設定値	0~5
//	iRndVal		乱数値	0~65535
//[out]
//	戻り値		条件装置番号	0~(HitCd_Size-1)
int LotHitCd(int iRank, int iRndVal) {
	int iHitCd;
	if((unsigned)iRank > 5) { DIE(); }
	if((unsigned)iRndVal > 65535) { DIE(); }
	for(iHitCd = 0; iHitCd < HitCd_Size; iHitCd++) {
		iRndVal -= (TBL_HitCd[iHitCd][iRank] + 1);
		if(iRndVal < 0) { return iHitCd; }
	}
	DIE();
}
//------------------------------------------------------------------------------
//条件装置iHitCdが、絵柄組合せiHitFlgを含むか調べる。
//[in]
//	iHitCd		条件装置番号	0~(HitCd_Size-1)
//	iHitFlg		絵柄組合せ番号	0~(HitFlg_Size-1)
//[out]
//	戻り値		含むならば、0以外の値を返す。
int GetHitCd_HitFlg(int iHitCd, int iHitFlg) {
	if((unsigned)iHitCd >= HitCd_Size) { DIE(); }
	if((unsigned)iHitFlg >= HitFlg_Size) { DIE(); }
	return (TBL_HitCd_HitFlg[iHitCd][iHitFlg/8] >> (iHitFlg%8)) & 1;
}

//==============================================================================
//■絵柄組合せ
//==============================================================================
//絵柄組合せ名称を取得する。
//[in]
//	iOrder		絵柄組合せ番号	0~(HitFlg_Size-1)
//[out]
//	戻り値		絵柄組合せ名称
const char* GetHitFlg_Name(int iHitFlg) {
	if((unsigned)iHitFlg >= HitFlg_Size) { DIE(); }
	return TBL_HitFlg_Name[iHitFlg];
}
//------------------------------------------------------------------------------
//絵柄組合せiHitFlgの、払出枚数を取得する。
//[in]
//	iHitFlg		絵柄組合せ番号	0~(HitFlg_Size-1)
//[out]
//	戻り値		払出枚数	0~15
int GetHitFlg_Pay(int iHitFlg) {
	if((unsigned)iHitFlg >= HitFlg_Size) { DIE(); }
	return (TBL_HitFlg[iHitFlg] >> 12) & 15;
}

//==============================================================================
//■テスト
//==============================================================================
//停止テスト
void StpTst() {
	int aPushPos[3], aStopPos[3], iOrder, iReel1, iReel2, iReel3, iHitCd, iHitFlg, iLine, nPay, fBonus, fReplay;
	unsigned char aHitFlg[HitFlg_Size], aStopPict[3][3], aResult[7/*iLine*/];
	//各条件装置について…
	for(iHitCd = 0; iHitCd < HitCd_Size; iHitCd++) {
		memset(aHitFlg, 0, sizeof aHitFlg);	//表示された絵柄組合せをクリアしておく
		//各押順について…
		for(iOrder = 0; iOrder < 6; iOrder++) {
			iReel1 = Slot_GetOrderReel(iOrder, 0);	//第1停止リール
			iReel2 = Slot_GetOrderReel(iOrder, 1);	//第2停止リール
			iReel3 = Slot_GetOrderReel(iOrder, 2);	//第3停止リール
			//第1停止リールの各押下位置について…
			for(aPushPos[iReel1] = 0; aPushPos[iReel1] < 21; aPushPos[iReel1]++) {
				//第1停止リールの停止位置を取得する。
				aStopPos[iReel1] = aPushPos[iReel1] - Slot_GetStpCtl1(&stStpCtl, iHitCd, iReel1, aPushPos[iReel1]);
				if(aStopPos[iReel1] < 0) { aStopPos[iReel1] += 21; }
				//第1停止リールの停止絵柄を取得する。
				aStopPict[0][iReel1] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel1] + 0, iReel1);
				aStopPict[1][iReel1] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel1] + 1, iReel1);
				aStopPict[2][iReel1] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel1] + 2, iReel1);
				//第2停止リールの各押下位置について…
				for(aPushPos[iReel2] = 0; aPushPos[iReel2] < 21; aPushPos[iReel2]++) {
					//第2停止リールの停止位置を取得する。
					aStopPos[iReel2] = aPushPos[iReel2] - Slot_GetStpCtl2(&stStpCtl, iHitCd, iReel1, iReel2, aStopPos[iReel1], aPushPos[iReel2]);
					if(aStopPos[iReel2] < 0) { aStopPos[iReel2] += 21; }
					//第2停止リールの停止絵柄を取得する。
					aStopPict[0][iReel2] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel2] + 0, iReel2);
					aStopPict[1][iReel2] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel2] + 1, iReel2);
					aStopPict[2][iReel2] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel2] + 2, iReel2);
					//第3停止リールの各押下位置について…
					for(aPushPos[iReel3] = 0; aPushPos[iReel3] < 21; aPushPos[iReel3]++) {
						//第3停止リールの停止位置を取得する。
						aStopPos[iReel3] = aPushPos[iReel3] - Slot_GetStpCtl3(&stStpCtl, iHitCd, iReel1, iReel2, iReel3, aStopPos[iReel1], aStopPos[iReel2], aPushPos[iReel3]);
						if(aStopPos[iReel3] < 0) { aStopPos[iReel3] += 21; }
						//第3停止リールの停止絵柄を取得する。
						aStopPict[0][iReel3] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel3] + 0, iReel3);
						aStopPict[1][iReel3] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel3] + 1, iReel3);
						aStopPict[2][iReel3] = Slot_GetReelPict(TBL_Reel, aStopPos[iReel3] + 2, iReel3);
						//各ラインの絵柄組合せを検索する。
						Slot_CheckPrize(aStopPict, TBL_HitFlg, HitFlg_Size, aResult);
						//各ラインについて…
						nPay = fBonus = fReplay = 0;
						for(iLine = 0; iLine < 5; iLine++) {
							iHitFlg = aResult[iLine];
							if(!GetHitCd_HitFlg(iHitCd, iHitFlg)) { DIE(); }		//条件装置に含まれない絵柄組合せが表示されたらエラー
							nPay += GetHitFlg_Pay(iHitFlg);					//払出枚数を加算する
							aHitFlg[iHitFlg] = 1;						//表示された絵柄組合せを記憶する
							if(strstr(GetHitFlg_Name(iHitFlg), "ﾎﾞｰﾅｽ")) { fBonus  = 1; }	//ボーナスが作動したことを記憶する
							if(strstr(GetHitFlg_Name(iHitFlg), "ﾘﾌﾟﾚｲ")) { fReplay = 1; }	//リプレイが作動したことを記憶する
						}
						//条件装置がハズレ,ボーナス,リプレイならば、払出が無いことを確認する。
						if(!iHitCd ||
						   strstr(GetHitCd_Name(iHitCd), "ﾎﾞｰﾅｽ") ||
						   strstr(GetHitCd_Name(iHitCd), "ﾘﾌﾟﾚｲ")) {
							if(nPay != 0) { DIE(); }
						//条件装置がハズレ,ボーナス,リプレイ以外ならば、払出が有ることを確認する。
						} else {
							if(nPay == 0) { DIE(); }
						}
						//条件装置がボーナスならば、ボーナスが作動したことを確認する。
						if(strstr(GetHitCd_Name(iHitCd), "ﾎﾞｰﾅｽ")) { if(!fBonus ) { DIE(); } }
						//条件装置がリプレイならば、リプレイが作動したことを確認する。
						if(strstr(GetHitCd_Name(iHitCd), "ﾘﾌﾟﾚｲ")) { if(!fReplay) { DIE(); } }
					}
				}
			}
		}
		//各絵柄組合せについて…
		for(iHitFlg = 0; iHitFlg < HitFlg_Size; iHitFlg++) {
			//この条件装置に含まれる絵柄組合せならば…
			if(GetHitCd_HitFlg(iHitCd, iHitFlg)) {
				//この絵柄組合せが表示されたら正しい。
				if(aHitFlg[iHitFlg]) {
					/** no job **/
				//この絵柄組合せが表示されなければエラー。ただし条件装置がCT中全小役である場合のみ許容する。
				} else {
					if(strstr(GetHitCd_Name(iHitCd), "全小役")) {
						/** no job **/
					} else {
						DIE();
					}
				}
			} else {
				//この絵柄組合せが表示されたらエラー。
				if(aHitFlg[iHitFlg]) {
					DIE();
				//この絵柄組合せが表示されなければ正しい。
				} else {
					/** no job **/
				}
			}
		}
	}
}
//------------------------------------------------------------------------------
//シミュレーションテスト
void SimTst(int iRank, int nYuugiKaisuu, int nSikouDaisuu) {
	WELL well;	//乱数生成器
	FILE* fp;	//ログ出力ストリーム
	//--- 試験通算の変数 ---
	unsigned aTousenKaisuu[HitCd_Size];				//各条件装置の当選回数
	int Sum_In_RPincl, Sum_Ot_RPincl;				//RP込投入枚数,RP込払出枚数
	int Sum_In_RPexcl, Sum_Ot_RPexcl;				//RP抜投入枚数,RP抜払出枚数
	int                Sum_Ot_YKonly;				//役物払出枚数
	int ShortOver, MidOver, LongOver, YakuhiOver, TotalOver;	//短時間不適合台数,中時間不適合台数,長時間不適合台数,役物比率不適合台数,不適合台数
	double ShortBest, MidBest, LongBest, YakuhiBest;		//短時間最大出玉率,中時間最大出玉率,長時間最大出玉率,最大役物比率
	double LongWorst;						//長時間最小出玉率
	int iWin, iLose;						//勝ち台数,負け台数
	int iSikouDaisuu;						//試行台数
	//--- 試験ごとの変数 ---
	ST_SlotDeritu stSlotDeritu_RPincl;				//RP込出玉率管理
	ST_SlotDeritu stSlotDeritu_RPexcl;				//RP抜出玉率管理
	ST_SlotYakuhi stSlotYakuhi;					//役物比率管理
	double ShortMax, MidMax, LongMax, YakuhiMax;			//短時間最大出玉率,中時間最大出玉率,長時間最大出玉率,最大役物比率
	double LongMin;							//長時間最小出玉率
	int iYuugiKaisuu;						//遊技回数
	int fReplay, iBonusNokoriGameSuu, iBonusKanGameSuu;		//リプレイ作動,ボーナス残りG数,ボーナス間G数
	//--- 遊技ごとの変数 ---
	int iHitCd;							//条件装置
	int In_RPincl, Ot_RPincl;					//RP込投入枚数,RP込払出枚数
	int In_RPexcl, Ot_RPexcl;					//RP抜投入枚数,RP抜払出枚数
	//乱数生成器を初期化する。
	WELL_init(&well, time(NULL));
	//ログ出力ストリームを開く。
	fp = fopen("log.txt", "wt");
	if(!fp) { DIE(); }
	//試験通算の変数をクリアする。
	memset(aTousenKaisuu, 0, sizeof aTousenKaisuu);
	Sum_In_RPincl = Sum_Ot_RPincl = 0;
	Sum_In_RPexcl = Sum_Ot_RPexcl = 0;
	                Sum_Ot_YKonly = 0;
	ShortOver = MidOver = LongOver = YakuhiOver = TotalOver = 0;
	ShortBest = MidBest = LongBest = YakuhiBest = DBL_MIN;
	LongWorst = DBL_MAX;
	iWin = 0, iLose = 0;
	//各試験について…
	for(iSikouDaisuu = 1; iSikouDaisuu <= nSikouDaisuu; iSikouDaisuu++) {
		//試験ごとの変数をクリアする。
		SlotDeritu_Init(&stSlotDeritu_RPincl);
		SlotDeritu_Init(&stSlotDeritu_RPexcl);
		SlotYakuhi_Init(&stSlotYakuhi);
		ShortMax = MidMax = LongMax = YakuhiMax = DBL_MIN;
		LongMin = DBL_MAX;
		fReplay = iBonusNokoriGameSuu = iBonusKanGameSuu = 0;
		//各遊技について…
		for(iYuugiKaisuu = 1; iYuugiKaisuu <= nYuugiKaisuu; iYuugiKaisuu++) {
			In_RPincl = 3;				//RP込投入枚数
			In_RPexcl = fReplay ? 0 : In_RPincl;	//RP抜投入枚数
			iHitCd = iBonusNokoriGameSuu ? HitCd_Zk : LotHitCd(iRank, (unsigned short)WELL_next(&well));	//条件装置
			aTousenKaisuu[iHitCd]++;		//各条件装置の当選回数
			switch(iHitCd) {
			case 0:					//ハズレ
			case HitCd_Bo: Ot_RPincl =  0; break;	//ボーナス	//RP込払出枚数
			case HitCd_Rp: Ot_RPincl =  3; break;	//リプレイ	//RP込払出枚数
			default:       Ot_RPincl = 11; break;	//小役		//RP込払出枚数
			}
			Ot_RPexcl = (iHitCd == HitCd_Rp) ? 0 : Ot_RPincl;	//RP抜払出枚数
			SlotDeritu_Update(&stSlotDeritu_RPincl, In_RPincl, Ot_RPincl);		//RP込出玉率管理
			SlotDeritu_Update(&stSlotDeritu_RPexcl, In_RPexcl, Ot_RPexcl);		//RP抜出玉率管理
			SlotYakuhi_Update(&stSlotYakuhi, iBonusNokoriGameSuu, Ot_RPexcl);	//役物比率管理
			Sum_In_RPincl += In_RPincl; Sum_Ot_RPincl += Ot_RPincl;			//RP込払出枚数(合計)
			Sum_In_RPexcl += In_RPexcl; Sum_Ot_RPexcl += Ot_RPexcl;			//RP抜払出枚数(合計)
			if(iBonusNokoriGameSuu) { Sum_Ot_YKonly += Ot_RPexcl; }			//役物払出枚数(合計)
			if((SlotDeritu_ShortTerm(&stSlotDeritu_RPexcl) != 0.0) && (SlotDeritu_ShortTerm(&stSlotDeritu_RPexcl) > ShortMax )) ShortMax  = SlotDeritu_ShortTerm(&stSlotDeritu_RPexcl);	//短時間最大出玉率
			if((SlotDeritu_MidTerm(  &stSlotDeritu_RPexcl) != 0.0) && (SlotDeritu_MidTerm(  &stSlotDeritu_RPexcl) > MidMax   )) MidMax    = SlotDeritu_MidTerm(  &stSlotDeritu_RPexcl);	//中時間最大出玉率
			if((SlotDeritu_LongTerm( &stSlotDeritu_RPexcl) != 0.0) && (SlotDeritu_LongTerm( &stSlotDeritu_RPexcl) > LongMax  )) LongMax   = SlotDeritu_LongTerm( &stSlotDeritu_RPexcl);	//長時間最大出玉率
			if((SlotYakuhi_MidTerm(  &stSlotYakuhi       ) != 0.0) && (SlotYakuhi_MidTerm(  &stSlotYakuhi       ) > YakuhiMax)) YakuhiMax = SlotYakuhi_MidTerm(  &stSlotYakuhi       );	//最大役物比率
			if((SlotDeritu_LongTerm( &stSlotDeritu_RPexcl) != 0.0) && (SlotDeritu_LongTerm( &stSlotDeritu_RPexcl) < LongMin  )) LongMin   = SlotDeritu_LongTerm( &stSlotDeritu_RPexcl);	//長時間最小出玉率
			if(!iBonusNokoriGameSuu) {				//ボーナス中でなければ…
				iBonusKanGameSuu++;				//ボーナス間G数を増やす(ボーナス当選Gも含む)
			}
			if(iSikouDaisuu == 1) { //1台目のみ、1Gログを出力する
				fprintf(fp, "%d\t", iYuugiKaisuu);			//遊技回数
				fprintf(fp, "%d\t", iBonusKanGameSuu);			//ボーナス間G数
				fprintf(fp, "%s\t", GetHitCd_Name(iHitCd));		//条件装置
				fprintf(fp, "%d\t", In_RPincl);				//投入枚数(RP込)
				fprintf(fp, "%d\t", Ot_RPincl);				//払出枚数(RP込)
				fprintf(fp, "%d\t", Sum_In_RPincl);			//累計投入枚数(RP込)
				fprintf(fp, "%d\t", Sum_Ot_RPincl);			//累計払出枚数(RP込)
				fprintf(fp, "%d\n", Sum_Ot_RPincl - Sum_In_RPincl);	//累計差枚数(RP込)
			}
			if(iBonusNokoriGameSuu) {				//ボーナス中ならば…
				iBonusNokoriGameSuu--;				//ボーナス残りG数を減らす
				if(!iBonusNokoriGameSuu) {			//ボーナス終了ならば…
					iBonusKanGameSuu = 0;			//ボーナス間G数をクリアする
				}
			} else {						//ボーナス中でなければ…
				if(iHitCd == HitCd_Rp) {			//リプレイならば…
					fReplay = 1;				//リプレイ作動フラグをセットする
				} else {					//リプレイでなければ…
					fReplay = 0;				//リプレイ作動フラグをクリアする
					if(iHitCd == HitCd_Bo) {		//ボーナスならば…
						iBonusNokoriGameSuu = 24;	//ボーナス残りG数をセットする
					}
				}
			}
		}
		if(ShortMax  >= 300.0) { ShortOver++;  }	//短時間不適合台数
		if(MidMax    >= 150.0) { MidOver++;    }	//中時間不適合台数
		if(LongMax   >= 120.0) { LongOver++;   }	//長時間不適合台数
		if(YakuhiMax >   70.0) { YakuhiOver++; }	//役物比率不適合台数
		if((ShortMax >= 300.0) || (MidMax >= 150.0) || (LongMax >= 120.0) || (YakuhiMax > 70.0)) { TotalOver++; }	//不適合台数
		if(ShortMax  > ShortBest ) { ShortBest  = ShortMax;  }	//短時間最大出玉率
		if(MidMax    > MidBest   ) { MidBest    = MidMax;    }	//中時間最大出玉率
		if(LongMax   > LongBest  ) { LongBest   = LongMax;   }	//長時間最大出玉率
		if(YakuhiMax > YakuhiBest) { YakuhiBest = YakuhiMax; }	//最大役物比率
		if(LongMin   < LongWorst ) { LongWorst  = LongMin;   }	//長時間最小出玉率
		if(stSlotDeritu_RPincl.TotalOut[2] >= stSlotDeritu_RPincl.TotalIn[2]) {
			iWin++;		//勝ち台数
		} else {
			iLose++;	//負け台数
		}
		if((iSikouDaisuu % 100) == 0) {
			printf(".");	//進捗表示
			fflush(stdout);
		}
	}
	printf("\n");
	printf("出率(RP込) 出率(RP抜) 役比\n");
	printf("%10.5f %10.5f %10.5f\n",
		((double)Sum_Ot_RPincl / (double)Sum_In_RPincl) * 100.0,	//RP込出玉率(合計)
		((double)Sum_Ot_RPexcl / (double)Sum_In_RPexcl) * 100.0,	//RP抜出玉率(合計)
		((double)Sum_Ot_YKonly / (double)Sum_Ot_RPexcl) * 100.0);	//役物比率(合計)
	if(nYuugiKaisuu <   400) { ShortBest            = 0; }	//短時間結果無効
	if(nYuugiKaisuu <  6000) { MidBest              = 0; }	//中時間結果無効
	if(nYuugiKaisuu < 17500) { LongBest = LongWorst = 0; }	//長時間結果無効
	printf("短時間最大 中時間最大 長時間最大 役比最大   長時間最小\n");
	printf("%10.5f %10.5f %10.5f %10.5f %10.5f\n", ShortBest, MidBest, LongBest, YakuhiBest, LongWorst);
	printf("短時間不適 中時間不適 長時間不適 役比不適     総合不適\n");
	printf("%10d %10d %10d %10d %10d\n", ShortOver, MidOver, LongOver, YakuhiOver, TotalOver);
	printf("勝ち台数 %d 負け台数 %d\n", iWin, iLose);
	printf("\n");
	for(iHitCd = 0; iHitCd < HitCd_Size; iHitCd++) {
		printf("%d%10d\n", iHitCd, aTousenKaisuu[iHitCd]);	//各条件装置の当選回数
	}
	fclose(fp);
}
//------------------------------------------------------------------------------
int main(int argc, char* argv[]) {
	int iRank = 5, nYuugiKaisuu = 17500, nSikouDaisuu = 10000;
	StpTst();	//停止テスト
	if(argc >= (1+1)) { iRank        = atoi(argv[1]); if((unsigned)iRank        >     5) { iRank        =     5; } }	//設定値
	if(argc >= (1+2)) { nYuugiKaisuu = atoi(argv[2]); if((unsigned)nYuugiKaisuu > 17500) { nYuugiKaisuu = 17500; } }	//遊技回数
	if(argc >= (1+3)) { nSikouDaisuu = atoi(argv[3]); if((unsigned)nSikouDaisuu > 10000) { nSikouDaisuu = 10000; } }	//試行台数
	SimTst(iRank, nYuugiKaisuu, nSikouDaisuu);	//シミュレーションテスト
	return 0;
}
