///////////////////////////////
//CLIGHTCYCLEクラス定義ファイル
///////////////////////////////
///////////////////////////////【仕様】///////////////////////////////
//【識別色】（m_Color）
//LightCycle車両は識別色を持つ。識別色はユーザーが決定する。
//【方向】（m_Dir）
//LightCycleでは方向を0-7で表す。
//    0				//y--
//  7 ↑ 1			//y--, x++(x--)
//6←    →2		//x++(x--)
//　5 ↓ 3			//y++, x++(x--)
//    4				//y++
//【位置関係】
//LightCycleの現在位置は2次元(平面)上のx（m_x）y（m_y）座標で特定する。
//走行範囲は競技場限界（m_MinX/−x座標最小値、m_MinY−y座標最小値、
//m_MaxX−x座標最大値、m_MaxY−y座標最大値）内とする。
//また、現在の方向と位置に基づく次の移動位置をm_nx、m_nyとする。
//競技の勝敗決定因である移動回数（プログラム的にはコールされた回数）
//はm_Timesとする。（要すれば一番長く生き残った者が勝つ。）
//【方向転換】
//方向転換のルールとして、進行方向の左右二つずつまでを許容
//    0				//m_Dir == 0であれば
//  7 ↑ 1			//左右斜め上（線をまたぐのは禁止）と
//6←    →2		//左右の転回を許す。
//また、競技車に一定間隔で方向を自ら変えることを許す。プ
//ログラム上は一定の動作回数で方向を変換させる。
//（m_Caprice：0-しない、1-1/100、2-1/200、3-1/400 ）
//【速度】（m_Speed）
//速度はタイマー、可変とする。（0-1/1000、1-10/1000、2-100/1000ミリ秒）
//////////////////////////////////////////////////////////////////////
class CLIGHTCYCLE
{
public:		//メンバー変数
	//クラス共通静的変数（クラス外宣言が必要）
	static HDC m_hDC;		//表示デバイスのハンドル
	static int m_MinX;		//x座標最小値
	static int m_MinY;		//y座標最小値
	static int m_MaxX;		//x座標最大値
	static int m_MaxY;		//y座標最大値
	//競技車の属性変数
	int m_Color;			//競走車の色（CANVASクラスのカラー番号）
	int m_StartDir;			//競走車の設定進行方向（0-7）
	int m_StartX;			//競走車の最初のx座標
	int m_StartY;			//競走車の最初のy座標
	int m_Speed;			//競走車のスピード（0-1/1000、1-10/1000、2-100/1000ミリ秒）
	int m_Caprice;			//何遍に1回方向転換を行うか（0-しない、1-1/100、2-1/200、3-1/400）
	//内部管理変数
	UINT m_Times;			//競走車の移動回数
	bool m_Alive;			//現在走行できるか否か
	int m_Dir;				//競走車の進行方向（0-7）
	int m_x;				//競走車の現在のx座標
	int m_y;				//競走車の現在のy座標
	int m_nx;				//競走車の方向の次のx座標
	int m_ny;				//競走車の方向の次のy座標
	char m_msg[32];			//死亡時のメッセージ

public:		//メンバー関数
	CLIGHTCYCLE();			//コンストラクター
	void SetDC(HDC);		//デバイスコンテキストの初期化
	void SetLimit(int, int, int, int);	//座標限界の設定
	void Init();			//競技車データ初期化
	void GetReady();		//競技車スタート位置設定
	void SetData(int, int, int, int, int, int);	//競走車の初期化
	bool CheckNext(int);	//現在方向と位置から次に進めるかを確認
	bool CanGo();			//可能なら進みTRUE、不可なら進まずFALSE
	bool ChangeDir();		//現在の方向以外で進める方向があれ方向転換
	char* Dead();			//死亡処理と通知
};

///////////////////
//コンストラクター
///////////////////
CLIGHTCYCLE::CLIGHTCYCLE() {

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

///////////////////////////////
//デバイスコンテキストの初期化
//全車共通で一回だけ実行する
///////////////////////////////
void CLIGHTCYCLE::SetDC(HDC hDC) {	

	m_hDC = hDC;
}

/////////////////
//座標限界の設定
/////////////////
void CLIGHTCYCLE::SetLimit(int minx, int miny, int maxx, int maxy) {

	m_MinX = minx;		//x座標最小値
	m_MinY = miny;		//y座標最小値
	m_MaxX = maxx;		//x座標最大値
	m_MaxY = maxy;		//y座標最大値
}

/////////////////////
//競技車データ初期化
/////////////////////
void CLIGHTCYCLE::Init() {

	//個車属性情報
	m_Color = 0;
	m_StartDir = 0;
	m_StartX = 0;
	m_StartY = 0;
	m_Speed = 0;
	m_Caprice = 0;
	m_Alive = FALSE;
}

/////////////////////////
//競技車スタート位置設定
/////////////////////////
void CLIGHTCYCLE::GetReady() {

	//内部管理変数の設定
	m_Times = 0;
	m_Alive = TRUE;
	m_Dir = m_StartDir;
	m_x = m_StartX;
	m_y = m_StartY;
}

///////////////////////
//競走車の初期化
//各車の属性を設定する
///////////////////////
void CLIGHTCYCLE::SetData(int col, int dir, int x, int y, int sp = 0, int cap = 0) {

	m_Color = col;
	m_StartDir = dir;
	m_StartX = x;
	m_StartY = y;
	m_Speed = sp;
	m_Caprice = cap;
}

/////////////////////////////////////////
//現在位置（m_x、m_y）と引数の方向（dir）
//から次の位置（m_nx、m_nyに代入される）
//を求め、競技場内か否か、未走破（黒）か
//否か、斜線跨ぎが無いかで進める（TRUE）
//か否（FALSE）かを返す
/////////////////////////////////////////
bool CLIGHTCYCLE::CheckNext(int dir) {

	bool OK = TRUE;		//戻り値用ブール変数
	//現在方向から次の位置をm_nx、m_nyに代入
	switch(dir) {
	case 0:
		m_nx = m_x;
		m_ny = m_y - 1;
		if(m_ny <= m_MinY)	OK = FALSE;
		break;
	case 1:
		m_nx = m_x + 1;
		if(m_nx >= m_MaxX)	OK = FALSE;
		m_ny = m_y - 1;
		if(m_ny <= m_MinY)	OK = FALSE;
		break;
	case 2:
		m_nx = m_x + 1;
		if(m_nx >= m_MaxX)	OK = FALSE;
		m_ny = m_y;
		break;
	case 3:
		m_nx = m_x + 1;
		if(m_nx >= m_MaxX)	OK = FALSE;
		m_ny = m_y + 1;
		if(m_ny >= m_MaxY)	OK = FALSE;
		break;
	case 4:
		m_nx = m_x;
		m_ny = m_y + 1;
		if(m_ny >= m_MaxY)	OK = FALSE;
		break;
	case 5:
		m_nx = m_x - 1;
		if(m_nx <= m_MinX)	OK = FALSE;
		m_ny = m_y + 1;
		if(m_ny >= m_MaxY)	OK = FALSE;
		break;
	case 6:
		m_nx = m_x - 1;
		if(m_nx <= m_MinX)	OK = FALSE;
		m_ny = m_y;
		break;
	case 7:
		m_nx = m_x - 1;
		if(m_nx <= m_MinX)	OK = FALSE;
		m_ny = m_y - 1;
		if(m_ny <= m_MinY)	OK = FALSE;
		break;
	}	
	//次の位置が黒(COLORREF 0)以外であれば走行済
	if(GetPixel(m_hDC, m_nx, m_ny))
		OK = FALSE;
	else {		//黒であっても
		if(dir % 2)	{	//進行方向斜めで
			COLORREF c = GetPixel(m_hDC, m_x, m_ny);
			//m_(x, y)とm_n(x, y)の線が他の線と交差ていなら不可る
			if(c && c == GetPixel(m_hDC, m_nx, m_y))
				OK = FALSE;
		}
	}
	return OK;
}

////////////////////////////////////////
//可能なら進みTRUE、不可なら進まずFALSE
////////////////////////////////////////
bool CLIGHTCYCLE::CanGo() {

	//走行不能の場合は何もしない
	if(!m_Alive)
		return FALSE;
	//方向転換（不能の場合、m_Dirは変更されない）
	if(m_Caprice && !(rand() % ((1 << m_Caprice) * 50)))
		ChangeDir();
	//現在位置に基づいて次の位置を取得
	if(CheckNext(m_Dir)) {			//走行継続可能
		//現在位置に次の位置を代入
		m_x = m_nx;
		m_y = m_ny;
		//競走車の動作回数を増やす
		m_Times++;
		return TRUE;
	}
	else {
		if(ChangeDir()) {			//方向転換で走行継続可能
			//現在位置に次の位置を代入
			m_x = m_nx;
			m_y = m_ny;
			//競走車の動作回数を増やす
			m_Times++;
			return TRUE;
		}
		else {						//走行継続不能
			m_Alive = FALSE;
			return FALSE;
		}
	}
}

///////////////////////////////////////////
//現在の方向以外で進める方向があれ方向転換
// 7 0 1	現在の方向から時計回りに２つ、
// 6 * 2	反時計回りに２つ方向を設定し、
// 5 4 3	CanGo()を実行する
//進めればTRUE、不可であればFALSEを返す
//FALSEの場合、進行ができないので終了する
///////////////////////////////////////////
bool CLIGHTCYCLE::ChangeDir() {

	//方向転換候補の特定
	int newdir[4];						//転換方向候補
	newdir[0] = (m_Dir + 6) % 8;		//反時計回り2つ目
	newdir[1] = (m_Dir + 7) % 8;		//反時計回り1つ目
	newdir[2] = (m_Dir + 1) % 8;		//時計回り1つ目
	newdir[3] = (m_Dir + 2) % 8;		//時計回り2つ目
	//4候補について進行可能か否か確認（不可は-1を代入）
	int result[4];						//進行可能方向とその数
	int count = 0;
	for(int i = 0; i < 4; i++) {
		if(CheckNext(newdir[i])) {
			result[count] = newdir[i];
			count++;
		}
	}
	//結果処理−進行可能方向（複数）があれば（乱数で選択し）m_Dirを変更する
	if(count) {
		m_Dir = result[rand() % count];	//進行可能方向候補からランダムに選ぶ
		CheckNext(m_Dir);				//再度m_nxとm_nyを設定する為
		return TRUE;
	}
	else
		return FALSE;
}

/////////////////
//死亡処理と通知
/////////////////
char* CLIGHTCYCLE::Dead() {

	m_Alive = FALSE;
	wsprintf(m_msg, "は走行距離%dで死亡。", m_Times);
	return m_msg;
}
