////////////////////////////////
// Class of CCOINS
// for use of Nim
// Copyright (c) by Ysama, 2022
////////////////////////////////
/*
【ルール】
（１）5列の5個、4個、3個、2個、1個のコイン群をテーブルに置き、
（２）先手、後手が一回に、一つの列から、1個以上のコインを取ってゆき、
（３）最後のコインを取ることになった方が負け
nimの双対ゲーム（https://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A0）
*/

//定数定義
#define		NOTYET	0		//勝負中
#define		FIRST	1		//先手勝利
#define		SECOND	2		//後手勝利

////////////////////////
//CCOINSクラスヘッダー
////////////////////////
class CCOINS {

protected:
	//メンバー変数
	int m_Coins[5];								//5列のコイン数の配列
	int m_Player;								//先手-1、後手-2
public:
	//メンバー関数
	CCOINS();									//コンストラクター（初期化のみ）
	void Init();								//初期化（再ゲーム時に必要）
	int* GetCoins();							//コインデータのコピーのポインターを渡す
	bool TakeCoin(int, int);					//指定列の指定数コインを取る（取れた−TRUE、取れない−FALSE）
	bool RandomTake();							//乱数でコインを取る（取れた−TRUE、取れない−FALSE）
	int IsOver();								//最後のコインが一つ残ったか否かの判定（NOTYETまたは敗者を返す）
	int GetNextPlayer() {return m_Player;}		//次に手を打つプレーヤーを返す（勝敗決定後は敗者）
};

//コンストラクター（初期化のみ）
CCOINS::CCOINS() {

	srand(time(NULL));							//乱数の初期化
	Init();
}

//初期化（再ゲームに必要）
void CCOINS::CCOINS::Init() {

	for(int i = 0; i < 5; i++)
		m_Coins[i] = 5 - i;						//m_Coins配列を5から1に初期化
	m_Player = FIRST;							//先手から始まる
}

//コインデータのコピーのポインターを渡す（一手先のシミュレーション用）
int* CCOINS::GetCoins() {

	static int copy[5];
	for(int i = 0; i < 5; i++)
		copy[i] = m_Coins[i];
	return copy;
}

//指定列の指定数コインを取る（取れた−TRUE、取れない−FALSE）
bool CCOINS::TakeCoin(int l, int num) {

	if(m_Coins[l] < num)			//コイン数よりも多くとろうとしたら
		return FALSE;				//FALSEを返す
	else {
		m_Coins[l] -= num;
		m_Player = 3 - m_Player;	//先手、後手の交代
	}
	return TRUE;
}

//乱数でコインを取る（取れた−TRUE、取れない−FALSE）
bool CCOINS::RandomTake() {

	int col[5], i, j;
	for(i = 0, j = 0; i < 5; i++) {	//列配列に0以外の列を入れる
		if(m_Coins[i])
			col[j++] = i;
	}
	//以下のcase 1とcase 2の明らかな勝利を乱数に任せない為
	switch(j) {						//列数による分岐
	case 0:							//すべて0（本来発生しない）
		return FALSE;
	case 1:							//1列だけの場合
		if(m_Coins[col[0]] == 1)		//1なら既に負け
			return FALSE;
		else
			m_Coins[col[0]] = 1;		//1を残してすべて取る
		break;
	case 2:							//2列の場合
		if(m_Coins[col[0]] == 1) {	//一方が1ならば、
			m_Coins[col[1]] = 0;		//他方のすべてのコインを取る
			break;
		}
		else if(m_Coins[col[1]] == 1) {	//他方が1ならば、
			m_Coins[col[0]] = 0;			//一方のすベてのコインを取る
			break;
		}							//それ以外はdefault処理へ
	default:						//それ以外の場合
		i = rand() % j;				//値を持つ列を乱数で抽出
		//m_Coins[col[i]]のコイン数から、乱数で定めた値（1〜そのコイン数）を差し引く
		m_Coins[col[i]] -= ((rand() % m_Coins[col[i]]) + 1);
		break;
	}
	m_Player = 3 - m_Player;		//先手、後手の交代
	return TRUE;
}

//最後のコインが一つ残ったか否かの判定
int CCOINS::IsOver() {

	int col[5], i, j;
	for(i = 0, j = 0; i < 5; i++) {
		if(m_Coins[i])
			col[j++] = i;
	}
	if(j == 1 && m_Coins[col[0]] == 1)	//一列に一つ残っている場合
		return m_Player;				//敗者（最後の一つを取る者）を返す
	return NOTYET;						//勝敗がまだついていない（0）
}
