/*
 *	game.c
 *
 *	覇沙夢もどき
 *
 *	* Sun Jan 29 11:46:37 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"
/****************************************************************************
 *	
 ****************************************************************************/
extern char START_JINGLE[];
extern char TEZUMARI_JINGLE[];
extern char CLEAR_JINGLE[];
extern char MAIN_BGM[];
/****************************************************************************
 *	フィールド定数
 ****************************************************************************/
#define MAX_COLS	12		//最大列数
#define MAX_ROWS	9		//最大行数
/*--------------------------------------------------------------------------*/
#define Pai_Max		((9*3)+4+3)	//牌の種類	1〜Pai_Max
/****************************************************************************
 *	フィールド構造体
 ****************************************************************************/
//方向テーブル
typedef struct _ST_Dir {
	char		vCol, vRow;
} ST_Dir;
const ST_Dir TBL_Dir[4]={{1,0},{-1,0},{0,1},{0,-1}};	//0=右,1=左,2=下,3=上				PAD_*やTRG_*の並び順に合わせました。
/*--------------------------------------------------------------------------*/
//セル
typedef struct _ST_Cell {
	uint8_t		iPai;				//牌の種類					0=空き,1〜Pai_Max=牌
	uint8_t		iPutOrd;			//何回目に配置した牌か?				0=空き,1=一回目,2=二回目,3=三回目,...
	uint8_t		iSide;				//配置タイプ					0=空き,1=動かす(動かさない事も有る)端,2=内側,3=動かさない端
	uint8_t		iMovCol, iMovRow;		//動かす元の位置				(iSide==1)の場合に移動先の位置を格納する。(iSide<>1)の場合はこのフィールドは使用しない。
	uint8_t		fBlank2;			//空き数が2以上である方向のビットマスク		bit0=右,bit1=左,bit2=下,bit3=上
	uint8_t		TBL_Blank[4];			//各方向への空き数				0〜max(MAX_COLS,MAX_ROWS)
} ST_Cell;
/*--------------------------------------------------------------------------*/
//フィールド
typedef struct _ST_Field {
	uint8_t		iPutOrd;			//解答は何手で解くか?
	uint8_t		iColMin, iColMax;		//列番号の最小値と最大値
	uint8_t		iRowMin, iRowMax;		//行番号の最小値と最大値
	ST_Cell		TBL_Cell[MAX_ROWS][MAX_COLS];	//セル配列
} ST_Field;
/****************************************************************************
 *	フィールド初期化処理
 ****************************************************************************/
//セルへのポインタを取得する。
ST_Cell* Field_GetCell(ST_Field* pField, int iCol, int iRow) {
	if((iCol < pField->iColMin) || (iCol > pField->iColMax) ||
	   (iRow < pField->iRowMin) || (iRow > pField->iRowMax)) { DIE(); }	//バグ
	return &pField->TBL_Cell[iRow][iCol];
}
/*--------------------------------------------------------------------------*/
//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
void Field_UpdateBlank(ST_Field* pField) {
	int iDir;
	for(iDir = 0; iDir < 4; iDir++) {
		int iCol1 = pField->iColMin, iRow1 = pField->iRowMin;	//数えない方向の起点位置
		for(;;) {
			//空きが連続する方向の逆方向に数えて行く。
			int iCol2 = (TBL_Dir[iDir].vCol < 0) ? pField->iColMin : (TBL_Dir[iDir].vCol > 0) ? pField->iColMax : iCol1;
			int iRow2 = (TBL_Dir[iDir].vRow < 0) ? pField->iRowMin : (TBL_Dir[iDir].vRow > 0) ? pField->iRowMax : iRow1;
			int nBlank = 0;	//空き数の初期値を0としておく。
			for(;;) {
				ST_Cell* pCell = Field_GetCell(pField, iCol2, iRow2);
				nBlank = pCell->iPutOrd ? 0 : (nBlank + 1);	//空きでなければ空き数を0に戻す。空きならば空き数を増やす。	Field_Gen()の途中から呼び出された時点では、iPaiはまだ格納されていないので、iPaiではなくiPutOrdで判断する事に注意しろ。
				pCell->TBL_Blank[iDir] = nBlank;
				if(nBlank >= 2) {
					pCell->fBlank2 |=  (1<<iDir);	//空き数が2以上ならば、この方向のビットをセットする。
				} else {
					pCell->fBlank2 &= ~(1<<iDir);	//空き数が2未満ならば、この方向のビットをクリアする。
				}
				//数える方向の位置を次へ進めて、フィールド外になったらループを抜ける。
				iCol2 -= TBL_Dir[iDir].vCol;
				iRow2 -= TBL_Dir[iDir].vRow;
				if((iCol2 < pField->iColMin) || (iCol2 > pField->iColMax)) { break; }
				if((iRow2 < pField->iRowMin) || (iRow2 > pField->iRowMax)) { break; }
			}
			//数えない方向の位置を次へ進めて、フィールド外になったらループを抜ける。
			iCol1 += !TBL_Dir[iDir].vCol;
			iRow1 += !TBL_Dir[iDir].vRow;
			if((iCol1 < pField->iColMin) || (iCol1 > pField->iColMax)) { break; }
			if((iRow1 < pField->iRowMin) || (iRow1 > pField->iRowMax)) { break; }
		}
	}
}
/*--------------------------------------------------------------------------*/
//フィールドの初期配置を生成する。
void Field_Gen(ST_Field* pField) {
	//二方向に二連以上の空きが有る位置への配置を試みるループ。
	for(;;) {
		//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
		Field_UpdateBlank(pField);
		{
			uint8_t TBL_DirOrd[][2/*0=動かす方向,1=挟む方向*/]={{0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2}};
			int iDirOrd, iDir1, iDir2, fBlank2, iCol = -1/*警告抑制*/, iRow, nCount, nLen;
			ST_Cell* pCell;
			//方向の検索順序をシャッフルする。
			shuffle(TBL_DirOrd, ARRAY_SIZE(TBL_DirOrd), sizeof TBL_DirOrd[0], &seed);
			//条件に合う位置と方向を数える。
			nCount = 0;
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir1 = TBL_DirOrd[iDirOrd][0];	//動かす方向
				iDir2 = TBL_DirOrd[iDirOrd][1];	//挟む方向
				fBlank2 = (1 << iDir1) | (1 << iDir2);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount++; }
					}
				}
			}
			if(!nCount) { break; }	//条件に合う位置と方向が無ければ、一方向に二連の空きが有る位置への配置を試みるループへ移行する。	条件に合う位置と方向が無買った場合、一方向に三連以上の空きが有る位置は無いはずです。もし一方向に三連以上の空きが有れば、中間の位置を起点として二方向に二連以上の空きが有る位置として検出されるはずだからです。
			//条件に合う位置と方向を抽選する。
			nCount = RND32_RANGE(seed, 0, nCount);
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir1 = TBL_DirOrd[iDirOrd][0];	//動かす方向
				iDir2 = TBL_DirOrd[iDirOrd][1];	//挟む方向
				fBlank2 = (1 << iDir1) | (1 << iDir2);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount--; }
						if(nCount < 0) { break; }
					}
					if(nCount < 0) { break; }
				}
				if(nCount < 0) { break; }
			}
			if(nCount >= 0) { DIE(); }	//バグ
			//配置番号を進めておく。
			pField->iPutOrd++;
			//動かす距離を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
#if 0
			nLen = RND32_RANGE(seed, 0, pCell->TBL_Blank[iDir1]);	//0≦nLen≦(動かす方向への空き数-1)	動かさない事も有るバージョン
#else			//↑↓どちらでも構いません。とりあえず、せっかく動かせるのだから、必ず動かす事にしました。
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir1]);	//1≦nLen≦(動かす方向への空き数-1)	必ず動かすバージョン
#endif
			//動かす(動かさない事も有る)牌を配置する。
			pCell = Field_GetCell(pField,
				iCol + (TBL_Dir[iDir1].vCol * nLen),
				iRow + (TBL_Dir[iDir1].vRow * nLen));
			if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
			pCell->iPutOrd = pField->iPutOrd;
			pCell->iSide = 1/*動かす(動かさない事も有る)端*/;
			pCell->iMovCol = iCol;
			pCell->iMovRow = iRow;
			//挟む長さ(終端も含む)を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir2]);	//1≦nLen≦(挟む方向への空き数-1)
			//内側の牌(挟む長さが2ならば存在しない),及び,動かさない牌を配置する。
			do {
				iCol += TBL_Dir[iDir2].vCol;
				iRow += TBL_Dir[iDir2].vRow;
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
				pCell->iPutOrd = pField->iPutOrd;
				pCell->iSide = (nLen > 1) ? 2/*内側*/ : 3/*動かさない端*/;
			} while(--nLen);
		}
	}
	//一方向に二連の空きが有る位置への配置を試みるループ
	for(;;) {
		//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
		Field_UpdateBlank(pField);
		{
			uint8_t TBL_DirOrd[]={0,1,2,3};
			int iDirOrd, iDir, fBlank2, iCol = -1/*警告抑制*/, iRow, nCount, nLen;
			ST_Cell* pCell;
			//方向の検索順序をシャッフルする。
			shuffle(TBL_DirOrd, ARRAY_SIZE(TBL_DirOrd), sizeof TBL_DirOrd[0], &seed);
			//条件に合う位置と方向を数える。
			nCount = 0;
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir = TBL_DirOrd[iDirOrd];	//挟む方向
				fBlank2 = (1 << iDir);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if(pCell->fBlank2 & fBlank2) { nCount++; }
					}
				}
			}
			if(!nCount) { break; }	//条件に合う位置と方向が無ければ、配置を終了する。
			//条件に合う位置と方向を抽選する。
			nCount = RND32_RANGE(seed, 0, nCount);
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir = TBL_DirOrd[iDirOrd];	//挟む方向
				fBlank2 = (1 << iDir);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount--; }
						if(nCount < 0) { break; }
					}
					if(nCount < 0) { break; }
				}
				if(nCount < 0) { break; }
			}
			if(nCount >= 0) { DIE(); }	//バグ
			//配置番号を進めておく。
			pField->iPutOrd++;
			//始点の牌を配置する。
			pCell = Field_GetCell(pField, iCol, iRow);
			if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
			pCell->iPutOrd = pField->iPutOrd;
			pCell->iSide = 1/*動かす(動かさない事も有る)端*/;
			pCell->iMovCol = iCol;
			pCell->iMovRow = iRow;
			//挟む長さ(終端も含む)を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir]);	//1≦nLen≦(挟む方向への空き数-1)
			if(nLen != 1) { DIE(); }	//一応抽選しているものの、実際には空き数は2であるはずなので、挟む長さ(終端も含む)は常に1となるはずだ。
			//内側の牌(挟む長さが2ならば存在しない),及び,動かさない牌を配置する。
			do {
				iCol += TBL_Dir[iDir].vCol;
				iRow += TBL_Dir[iDir].vRow;
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
				pCell->iPutOrd = pField->iPutOrd;
				pCell->iSide = (nLen > 1) ? 2/*内側*/ : 3/*動かさない端*/;
			} while(--nLen);
		}
	}
	//牌の種類を決定する処理。
	{
		uint8_t TBL_PaiOrd[Pai_Max * 4/*4枚づつ*/];
		int i, iCol, iRow;
		ST_Cell* pCell;
		//牌の種類の配列を初期化する。
		for(i = 0; i < ARRAY_SIZE(TBL_PaiOrd); i++) {
			TBL_PaiOrd[i] = (i / 4) + 1;	//{1,1,1,1,2,2,2,2,3,3,3,3,...,Pai_Max,Pai_Max,Pai_Max,Pai_Max}
		}
		//まず、二個組みでシャフルする。(両端の牌)
		shuffle(TBL_PaiOrd, ARRAY_SIZE(TBL_PaiOrd) / 2, (sizeof TBL_PaiOrd[0]) * 2, &seed);
		//配置した回数の分は二個組みを維持して、それ以降はばらばらにシャフルする。(内側の牌)
		shuffle(&TBL_PaiOrd[pField->iPutOrd * 2], ARRAY_SIZE(TBL_PaiOrd) - (pField->iPutOrd * 2), sizeof TBL_PaiOrd[0], &seed);
		//配置する。
		i = pField->iPutOrd * 2;	//ばらばらにシャフルした先頭位置
		for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
			for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPutOrd) {
					if(pCell->iPai) { DIE(); }	//バグ
					switch(pCell->iSide) {
					default:DIE();
					case 1/*動かす(動かさない事も有る)端*/:
					case 3/*動かさない端*/:
						pCell->iPai = TBL_PaiOrd[(pCell->iPutOrd - 1) * 2];
						break;
					case 2/*内側*/:
						pCell->iPai = TBL_PaiOrd[i++];
						break;
					}
					if((pCell->iPai < 1) || (pCell->iPai > Pai_Max)) { DIE(); }	//バグ
				}
			}
		}
	}
}
/*--------------------------------------------------------------------------*/
//フィールドを初期化する。
void Field_Init(ST_Field* pField, int iColMin, int iColMax, int iRowMin, int iRowMax) {
	//構造体をクリアする。
	memset(pField, 0, sizeof(ST_Field));
	//列番号と行番号の最小値と最大値を格納する。
	if((((unsigned)iColMin >= MAX_COLS) || ((unsigned)iColMax >= MAX_COLS) || (iColMin > iColMax)) ||
	   (((unsigned)iRowMin >= MAX_ROWS) || ((unsigned)iRowMax >= MAX_ROWS) || (iRowMin > iRowMax))) { DIE(); }
	pField->iColMin = iColMin;
	pField->iColMax = iColMax;
	pField->iRowMin = iRowMin;
	pField->iRowMax = iRowMax;
	//フィールドの初期配置を生成する。
	Field_Gen(pField);
}
/****************************************************************************
 *	ゲーム構造体
 ****************************************************************************/
//ゲーム状態
#define GAME_STAT_SEL1		0	//一個目選択中
#define GAME_STAT_MOV1		1	//一個目移動中
#define GAME_STAT_ERR1		2	//操作ロック中1
#define GAME_STAT_SEL2		3	//二個目選択中
#define GAME_STAT_MOV2		4	//二個目移動中
#define GAME_STAT_ERR2		5	//操作ロック中2
#define GAME_STAT_GET		6	//牌消し処理中
/*--------------------------------------------------------------------------*/
//メニュー状態
#define MENU_STAT_GAME		1	//ゲームを続ける
#define MENU_STAT_HELP		2	//解き方を見る
#define MENU_STAT_TITLE		3	//タイトルに戻る
/*--------------------------------------------------------------------------*/
//ゲーム
typedef struct _ST_Game {
	uint8_t		iGameStat;				//ゲーム状態
	uint8_t		iMenuStat;				//メニュー状態
	uint8_t		bBgmPlaying;				//BGM再生中フラグ			手詰まりジングルを鳴らしたら0にする。アンドゥした時に0だったらBGMを再生して1にする。
	//
	uint16_t	nTime;					//残り時間				クリア時の残り時間をスコアに加算します。時間が無くなってもゲームオーバーにはしません。
	uint16_t	nScore;					//スコア
	//
	const char*	pErrMsg;				//エラーメッセージ
	uint8_t		iErrCnt;				//操作ロックカウンタ
	//
	uint16_t	nScoEff;				//スコアエフェクト			0以外ならば有効
	uint8_t		iScoCnt;				//スコアエフェクトカウンタ
	uint8_t		iScoX;					//スコアエフェクト表示座標X
	uint8_t		iScoY;					//スコアエフェクト表示座標Y
	//
	uint8_t		iCurCol, iCurRow;			//カーソル位置
	uint8_t		iSelPai;				//選択牌
	uint8_t		iSelCol1, iSelRow1;			//一個目選択位置
	uint8_t		iMovCol1, iMovRow1;			//一個目移動位置
	uint8_t		iSelCol2, iSelRow2;			//二個目選択位置
	uint8_t		iMovCol2, iMovRow2;			//二個目移動位置
	//
	uint8_t		iGetOrd;				//最後に消した順番(=消した回数)		0=一回も消していない,1=一回消した,2=二回消した,...
	uint8_t		TBL_GetOrd[MAX_ROWS][MAX_COLS];		//各位置の牌を消した順番		0=消していない,1〜=消した順番		現在のフィールドの描画用,及び,アンドゥ用です。
	//
	uint8_t		nStartOffset;				//開始演出用牌描画オフセット
	uint8_t		TBL_StartMask[MAX_ROWS][MAX_COLS];	//開始演出用牌マスク
	//
	ST_Field	Field;					//フィールドの初期配置
} ST_Game;
ST_Game		stGame;
/****************************************************************************
 *	ゲーム処理
 ****************************************************************************/
//ゲームを初期化する。
void Game_Init() {
	int iColMin, iColMax, iRowMin, iRowMax;
	//構造体をクリアする。
	memset(&stGame, 0, sizeof stGame);
	//フィールドの範囲を計算する。
	iColMin = (MAX_COLS - cols_sel) / 2;
	iColMax = iColMin + cols_sel - 1;
	iRowMin = (MAX_ROWS - rows_sel) / 2;
	iRowMax = iRowMin + rows_sel - 1;
	//フィールドを初期化する。
	Field_Init(&stGame.Field, iColMin, iColMax, iRowMin, iRowMax);
	//残り時間を初期化する。
	stGame.nTime = (cols_sel * rows_sel) * 10;	//最大で(12*9)*10=1080
	stGame.nTime -= stGame.nTime % 100;		//100未満の位を切り捨てる。
	if(stGame.nTime > 900) { stGame.nTime = 900; }	//900超過ならば900にする。
	if(stGame.nTime < 100) { stGame.nTime = 100; }	//100未満ならば100にする。
	stGame.nTime *= FPS;				//フレーム単位にする。
	//カーソル位置を初期化する。
	stGame.iCurCol = iColMin;
	stGame.iCurRow = iRowMin;
}
/*--------------------------------------------------------------------------*/
//フィールドを描画する。
void Field_Draw() {
	int iCol, iRow, iPri, x, y, iSprNo;
	ST_Cell* pCell;
	//背景を描画する。
	CbkQue_sprite_draw(pCbkQue,
		PRI__BG,
		X__BG,
		Y__BG,
		SPR_BG,
		DRW_NOMAL);
	//セルクリッピングを設定する。
	CbkQue_render_set_draw_area(pCbkQue,		//設定
		PRI__CELL,
		&render,
		X__CELL,
		Y__CELL,
		X__CELL + (W__CELL * MAX_COLS) + 1,
		Y__CELL + (H__CELL * MAX_ROWS) + 1);
	CbkQue_render_set_draw_area_full(pCbkQue,	//解除
		PRI__CELL + ((MAX_ROWS + 1) * 10/*調整可*/),
		&render);
	//通常セルを描画する。
	for(iRow = 0; iRow < MAX_ROWS; iRow++) {
		for(iCol = 0; iCol < MAX_COLS; iCol++) {
			iPri = PRI__CELL + (iRow * 10/*調整可*/);
			iSprNo = -1;
			//フィールド内ならば…
			if(((iCol >= stGame.Field.iColMin) && (iCol <= stGame.Field.iColMax)) &&
			   ((iRow >= stGame.Field.iRowMin) && (iRow <= stGame.Field.iRowMax))) {
				//まだ消されていなければ…
				if(!stGame.TBL_GetOrd[iRow][iCol]) {
					pCell = Field_GetCell(&stGame.Field, iCol, iRow);
					//初期配置の牌が有れば…
					if(pCell->iPai) {
						iSprNo = SPR_PAI1_G1 + (pCell->iPai - 1);	//スプライト番号のベースはM1ではなくG1である事に注意しろ。
						if(stGame.iGameStat >= GAME_STAT_MOV1) {	//一個目移動中以降ならば…
							if((iCol == stGame.iSelCol1) &&
							   (iRow == stGame.iSelRow1)) {		//一個目選択位置ならば…
								iSprNo = -1;
							}
						}
						if(stGame.iGameStat >= GAME_STAT_MOV2) {	//二個目移動中以降ならば…
							if((iCol == stGame.iSelCol2) &&
							   (iRow == stGame.iSelRow2)) {		//二個目選択位置ならば…
								iSprNo = -1;
							}
						}
					}
				}
			//フィールド外ならば…
			} else {
				iSprNo = SPR_PAI_OUTER;
			}
		    //{{開始演出用
			if(stGame.TBL_StartMask[iRow][iCol]) { iSprNo = SPR_PAI_OUTER; }
		    //}}開始演出用
			//スプライトが有れば…
			if(iSprNo != -1) {
				x = X__CELL + (W__CELL * iCol);
				y = Y__CELL + (H__CELL * iRow);
			    //{{開始演出用
				x -= stGame.nStartOffset;
			    //}}開始演出用
				CbkQue_sprite_draw(pCbkQue,
					iPri,
					x,
					y,
					iSprNo,
					DRW_NOMAL);
			}
		}
	}
	//操作中のセルを描画する。
	if(stGame.iGameStat < GAME_STAT_GET) {				//操作中ならば…
		if(stGame.iGameStat == GAME_STAT_MOV1) {		//一個目移動中ならば…
			if((stGame.iSelCol1 != stGame.iMovCol1) ||
			   (stGame.iSelRow1 != stGame.iMovRow1)) {	//一個目の牌を移動していたら…
				if((now % SEC(0.5)) < SEC(0.25)) {
					iCol = stGame.iSelCol1;
					iRow = stGame.iSelRow1;
					iPri = PRI__CELL + (iRow * 10/*調整可*/) + 1/*調整可*/;
					x = X__CELL + (W__CELL * iCol);
					y = Y__CELL + (H__CELL * iRow);
					iSprNo = SPR_PAI_FRAME;
					CbkQue_sprite_draw(pCbkQue,
						iPri,
						x,
						y,
						iSprNo,
						DRW_NOMAL);
				}
			}
		}
		if(stGame.iGameStat >= GAME_STAT_MOV1) {		//一個目移動中以降ならば…
			iCol = stGame.iMovCol1;
			iRow = stGame.iMovRow1;
			iPri = PRI__CELL + (iRow * 10/*調整可*/);
			x = X__CELL + (W__CELL * iCol);
			y = Y__CELL + (H__CELL * iRow);
			pCell = Field_GetCell(&stGame.Field, stGame.iSelCol1, stGame.iSelRow1);
			iSprNo = SPR_PAI2_G1 + (pCell->iPai - 1);	//スプライト番号のベースはM1ではなくG1である事に注意しろ。
			CbkQue_sprite_draw(pCbkQue,
				iPri,
				x,
				y,
				iSprNo,
				DRW_NOMAL);
		}
		if(stGame.iGameStat == GAME_STAT_MOV2) {		//二個目移動中ならば…
			if((stGame.iSelCol2 != stGame.iMovCol2) ||
			   (stGame.iSelRow2 != stGame.iMovRow2)) {	//二個目の牌を移動していたら…
				if((now % SEC(0.5)) < SEC(0.25)) {
					iCol = stGame.iSelCol2;
					iRow = stGame.iSelRow2;
					iPri = PRI__CELL + (iRow * 10/*調整可*/) + 1/*調整可*/;
					x = X__CELL + (W__CELL * iCol);
					y = Y__CELL + (H__CELL * iRow);
					iSprNo = SPR_PAI_FRAME;
					CbkQue_sprite_draw(pCbkQue,
						iPri,
						x,
						y,
						iSprNo,
						DRW_NOMAL);
				}
			}
		}
		if(stGame.iGameStat >= GAME_STAT_MOV2) {		//二個目移動中以降ならば…
			iCol = stGame.iMovCol2;
			iRow = stGame.iMovRow2;
			iPri = PRI__CELL + (iRow * 10/*調整可*/);
			x = X__CELL + (W__CELL * iCol);
			y = Y__CELL + (H__CELL * iRow);
			pCell = Field_GetCell(&stGame.Field, stGame.iSelCol2, stGame.iSelRow2);
			iSprNo = SPR_PAI2_G1 + (pCell->iPai - 1);	//スプライト番号のベースはM1ではなくG1である事に注意しろ。
			CbkQue_sprite_draw(pCbkQue,
				iPri,
				x,
				y,
				iSprNo,
				DRW_NOMAL);
		}
	}
	//スコアを描画する。
	CbkQue_render_printf(pCbkQue,
		PRI__1P,
		&render,
		X__1P,
		Y__1P,
		3,
		0,
		"%07d",
		stGame.nScore * 10);
	//ハイスコアを描画する。
	CbkQue_render_printf(pCbkQue,
		PRI__HI,
		&render,
		X__HI,
		Y__HI,
		3,
		0,
		"%07d",
		hiscore * 10);
	//タイムを描画する。
	CbkQue_render_printf(pCbkQue,
		PRI__TIME,
		&render,
		X__TIME - 1,
		Y__TIME - 1,
		1,
		0,
		"%03d",
		(stGame.nTime + (FPS - 1)) / FPS);
	//メッセージを描画する。
	if(stGame.pErrMsg) {
		char** v = textwrap(stGame.pErrMsg, 6);
		int i = 0;
		iPri = PRI__ERR;
		x = X__ERR;
		y = Y__ERR;
		while(v[i]) {
			CbkQue_render_printf(pCbkQue,
				iPri,
				&render,
				x,
				y,
				3,
				0,
				"%s",
				v[i]);
			y += 5;
			i++;
		}
		strfreev(v);
	}
	//スコアエフェクトを表示する。
	if(stGame.nScoEff) {
		if(!(stGame.iScoCnt & 1)) {
			iPri = PRI__CURSOR + 1;
			x = stGame.iScoX;
			y = stGame.iScoY;
			if(stGame.nScoEff <= 9) {
				iSprNo = SPR_POINT2;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, DRW_NOMAL);
				x += 1;
				y += 3;
				CbkQue_render_printf(pCbkQue, iPri, &render, x, y, 3, 3, "%2d", stGame.nScoEff * 10);
			} else if(stGame.nScoEff <= 99) {
				iSprNo = SPR_POINT3;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, DRW_NOMAL);
				x -= 1;
				y += 3;
				CbkQue_render_printf(pCbkQue, iPri, &render, x, y, 3, 3, "%2d", stGame.nScoEff * 10);
			} else if(stGame.nScoEff <= 999) {
				iSprNo = SPR_POINT4;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, DRW_NOMAL);
				x -= 3;
				y += 3;
				CbkQue_render_printf(pCbkQue, iPri, &render, x, y, 3, 3, "%2d", stGame.nScoEff * 10);
			} else {
				iSprNo = SPR_POINT5;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, DRW_NOMAL);
				x -= 5;
				y += 3;
				CbkQue_render_printf(pCbkQue, iPri, &render, x, y, 3, 3, "%2d", stGame.nScoEff * 10);
			}
		}
		if(++stGame.iScoCnt >= SEC(0.5)) { stGame.nScoEff = 0; }	//ここだけ表示処理の中でカウンタの進行を行っています。
	}
	//メニュー表示中ならば…
	if(stGame.iMenuStat) {
		x = 14;
		y = 27;
		CbkQue_render_rectangle(       pCbkQue, SHRT_MAX, &render, x - 2, y - 3, 76, 39,    0);
		CbkQue_render_rectangle_shadow(pCbkQue, SHRT_MAX, &render, x - 1, y - 2, 74, 37, 2, 3);
		CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 0, (stGame.iMenuStat == MENU_STAT_GAME ) ? 0x220 : 0x331, "%s", "ゲームを続ける");
		y += 11;
		CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 0, (stGame.iMenuStat == MENU_STAT_HELP ) ? 0x220 : 0x331, "%s", "解き方を見る");
		y += 11;
		CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 0, (stGame.iMenuStat == MENU_STAT_TITLE) ? 0x220 : 0x331, "%s", "タイトルに戻る");
	}
}
/*--------------------------------------------------------------------------*/
//一個目移動後に、この位置で挟めるかを検査する。
int MoveCheck1() {
	int iDir1, iCol1, iRow1;
	ST_Cell* pCell;
	//一個目の牌の移動後の位置から、四方向のどこかに、牌が連続していて、選択牌と同じ種類の牌が有るかを調べる。	一個目の牌自身は含めないように注意しろ。
	for(iDir1 = 0; iDir1 < 4; iDir1++) {
		iCol1 = stGame.iMovCol1;
		iRow1 = stGame.iMovRow1;
		for(;;) {
			iCol1 += TBL_Dir[iDir1].vCol;
			iRow1 += TBL_Dir[iDir1].vRow;
			if(((iCol1 < stGame.Field.iColMin) || (iCol1 > stGame.Field.iColMax)) ||
			   ((iRow1 < stGame.Field.iRowMin) || (iRow1 > stGame.Field.iRowMax))) {	//フィールド外ならば…
				break;	//この方向には挟めない。
			}
			if((iCol1 == stGame.iSelCol1) &&
			   (iRow1 == stGame.iSelRow1)) {	//一個目選択位置ならば…	プレインクリメントで位置を進めていて、ここで一個目の選択位置を見つけたという事は、一個目の牌を移動しているという事である。従って、二個目の牌を移動して挟めるかを、検査する必要は無い。
				break;	//この方向には挟めない。
			}
			pCell = Field_GetCell(&stGame.Field, iCol1, iRow1);
			if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
			   !stGame.TBL_GetOrd[iRow1][iCol1]) {	//この位置の牌がまだ消されていなければ…
				if(pCell->iPai == stGame.iSelPai) {	//選択牌と同じ種類ならば…
					return 1;	//挟める。
				}
			} else {				//この位置に初期配置の牌が無いか、又は、この位置の牌がもう消されていたら…
				//一個目の牌を移動していなければ…
				if((stGame.iSelCol1 == stGame.iMovCol1) &&
				   (stGame.iSelRow1 == stGame.iMovRow1)) {
					int iDir2, iCol2, iRow2;
					for(iDir2 = 0; iDir2 < 4; iDir2++) {
						if((iDir1 ^ iDir2) != 1) {	//いま辿ってきた方向に戻っての走査はしない。右に対する左,及び,下に対する上は除外すると言う事です。こうすれば、一個目の牌自身を含めてしまう事は発生しません。
							iCol2 = iCol1;
							iRow2 = iRow1;
							for(;;) {
								iCol2 += TBL_Dir[iDir2].vCol;
								iRow2 += TBL_Dir[iDir2].vRow;
								if(((iCol2 < stGame.Field.iColMin) || (iCol2 > stGame.Field.iColMax)) ||
								   ((iRow2 < stGame.Field.iRowMin) || (iRow2 > stGame.Field.iRowMax))) {	//フィールド外ならば…
									break;	//この方向から移動してきて挟む事は出来ない。
								}
								pCell = Field_GetCell(&stGame.Field, iCol2, iRow2);
								if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
								   !stGame.TBL_GetOrd[iRow2][iCol2]) {	//この位置の牌がまだ消されていなければ…
									if(pCell->iPai == stGame.iSelPai) {	//選択牌と同じ種類ならば…
										return 1;	//挟める。
									} else {				//選択牌と違う種類ならば…
										break;	//この方向から移動してきて挟む事は出来ない。
									}
								}
							}
						}
					}
				}
				break;	//この方向には挟めない。
			}
		}
	}
	return 0;
}
/*--------------------------------------------------------------------------*/
//二個目移動後に、この位置で挟めるかを検査する。
int MoveCheck2() {
	int iDir, iCol, iRow;
	ST_Cell* pCell;
	//一個目の牌の移動後の位置から、四方向のどこかに、牌が連続していて、二個目の牌の移動後の位置へ到達出来るかを調べる。	一個目の牌,及び,二個目の牌の、移動前の位置は含めないように注意しろ。ただし、それがもう片方の牌の移動後の位置ならば、到達可能と判断する処理を優先する。
	for(iDir = 0; iDir < 4; iDir++) {
		iCol = stGame.iMovCol1;
		iRow = stGame.iMovRow1;
		for(;;) {
			iCol += TBL_Dir[iDir].vCol;
			iRow += TBL_Dir[iDir].vRow;
			if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
			   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax))) {	//フィールド外ならば…
				break;	//この方向には挟めない。
			}
		    //{{この処理順序必須です。
			if((iCol == stGame.iMovCol2) &&
			   (iRow == stGame.iMovRow2)) {		//二個目移動位置ならば…	この下の判定式では、二個目移動位置を空セルと判断してしまう可能性が有るので、こちらを先に判断する必要が有ります。
				return 1;	//挟める。
			}
			pCell = Field_GetCell(&stGame.Field, iCol, iRow);
			if((!pCell->iPai || stGame.TBL_GetOrd[iRow][iCol]) ||			//この位置に初期配置の牌が無いか,又は,この位置の牌がもう消されているか、又は、	//┐
			   ((iCol == stGame.iSelCol1) && (iRow == stGame.iSelRow1)) ||		//一個目選択位置か、又は、							//├空セルの判断です。
			   ((iCol == stGame.iSelCol2) && (iRow == stGame.iSelRow2))) {		//二個目選択位置ならば…							//┘
				break;	//この方向には挟めない。
			}
		    //}}この処理順序必須です。
		}
	}
	return 0;
}
/*--------------------------------------------------------------------------*/
//クリアチェックを行う。
int IsCleared() {
	int iCol, iRow;
	ST_Cell* pCell;
	for(iRow = stGame.Field.iRowMin; iRow <= stGame.Field.iRowMax; iRow++) {
		for(iCol = stGame.Field.iColMin; iCol <= stGame.Field.iColMax; iCol++) {
			pCell = Field_GetCell(&stGame.Field, iCol, iRow);
			if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
			   !stGame.TBL_GetOrd[iRow][iCol]) {	//この位置の牌がまだ消されていなければ…
				return 0;	//クリアではない。
			}
		}
	}
	return 1;	//クリアです。
}
/*--------------------------------------------------------------------------*/
//手詰まりチェックを行う。
int IsDeadLock() {
	int iCol, iRow;
	ST_Cell* pCell;
	for(iRow = stGame.Field.iRowMin; iRow <= stGame.Field.iRowMax; iRow++) {
		for(iCol = stGame.Field.iColMin; iCol <= stGame.Field.iColMax; iCol++) {
			pCell = Field_GetCell(&stGame.Field, iCol, iRow);
			if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
			   !stGame.TBL_GetOrd[iRow][iCol]) {	//この位置の牌がまだ消されていなければ…
				//一個目を選択して移動せずに、二個目を移動可能という体で、移動チェックを行う。
				stGame.iSelPai = pCell->iPai;
				stGame.iSelCol1 = stGame.iMovCol1 = iCol;
				stGame.iSelRow1 = stGame.iMovRow1 = iRow;
				if(MoveCheck1()) { return 0; }	//手詰まりではない。
			}
		}
	}
	return 1;	//手詰まりです。
}
/*--------------------------------------------------------------------------*/
//カーソルの周期処理を行う。
void Cursor_Exec() {
	int iCol, iRow, iDir;
	ST_Cell* pCell;
//{{メニュー処理
	//メニュー表示中でなければ…
	if(!stGame.iMenuStat) {
		if((stGame.iGameStat == GAME_STAT_SEL1) ||	//一個目選択中
		   (stGame.iGameStat == GAME_STAT_SEL2)) {	//二個目選択中
			if(joy & TRG_SELECT) {
				sound_play(0, sound_table[SND_MENU_HYOUJI], 1);	//メニュー表示音
				stGame.iMenuStat = MENU_STAT_GAME;
				stGame.pErrMsg = NULL;
				return;	//ここまで
			}
		}
	//メニュー表示中ならば…
	} else {
		if((joy & TRG_UP) && (stGame.iMenuStat > MENU_STAT_GAME)) {
			sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
			stGame.iMenuStat--;
			return;	//ここまで
		}
		if((joy & TRG_DN) && (stGame.iMenuStat < MENU_STAT_TITLE)) {
			sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
			stGame.iMenuStat++;
			return;	//ここまで
		}
		if(joy & (TRG_B|TRG_SELECT)) {
			sound_play(0, sound_table[SND_CURSOR_SENTAKU], 1);	//カーソル選択音
			stGame.iMenuStat = 0;
			return;	//ここまで
		}
		return;	//ここまで
	}
//}}メニュー処理
	switch(stGame.iGameStat) {
	default:DIE();
	case GAME_STAT_SEL1:	//一個目選択中
		if(IsDeadLock()) {
			stGame.pErrMsg = "ﾃﾂﾞﾏﾘ ﾃﾞｽ";
		} else {
			stGame.pErrMsg = NULL;
			//選択
			if(joy & TRG_A) {
				pCell = Field_GetCell(&stGame.Field, stGame.iCurCol, stGame.iCurRow);
				if(!pCell->iPai ||						//この位置に初期配置の牌が無いか、又は、
				   stGame.TBL_GetOrd[stGame.iCurRow][stGame.iCurCol]) {		//この位置の牌がもう消されていたら…
					sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
					return;	//ここまで
				}
				sound_play(0, sound_table[SND_CURSOR_SENTAKU], 1);	//カーソル選択音
				stGame.iGameStat = GAME_STAT_MOV1;
				stGame.iSelPai = pCell->iPai;
				stGame.iSelCol1 = stGame.iMovCol1 = stGame.iCurCol;
				stGame.iSelRow1 = stGame.iMovRow1 = stGame.iCurRow;
				return;	//ここまで
			}
		}
		//移動
		iDir = ffs(joy >> 8) - 1;	//TRG_RIがセットされていたら0、TRG_LFがセットされていたら1、TRG_DNがセットされていたら2、TRG_UPがセットされていたら3、そうでなければ0〜3以外の値になる。
		if((unsigned)iDir <= 3) {
			iCol = stGame.iCurCol + TBL_Dir[iDir].vCol;
			iRow = stGame.iCurRow + TBL_Dir[iDir].vRow;
			if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
			   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax))) {	//フィールド外ならば…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
			stGame.iCurCol = iCol;
			stGame.iCurRow = iRow;
			return;	//ここまで
		}
		//アンドゥ
		if(joy & TRG_B) {
			if(!stGame.iGetOrd) {	//一回も消していなければ…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			sound_play(0, sound_table[SND_UNDO], 1);	//アンドゥ音
			if(!stGame.bBgmPlaying) {	//手詰まりジングル後ならば、BGM再開
				PlayMusic(MAIN_BGM);
				stGame.bBgmPlaying = 1;
			}
			{
				int nPoint = 0;
				for(iRow = stGame.Field.iRowMin; iRow <= stGame.Field.iRowMax; iRow++) {
					for(iCol = stGame.Field.iColMin; iCol <= stGame.Field.iColMax; iCol++) {
						if(stGame.TBL_GetOrd[iRow][iCol] == stGame.iGetOrd) {
							stGame.TBL_GetOrd[iRow][iCol] = 0;
							nPoint++;
						}
					}
				}
				if(!nPoint) { DIE(); }
				nPoint = 1 << (nPoint - 2);
				stGame.nScore -= nPoint;
			}
			stGame.iGetOrd--;
			return;	//ここまで
		}
		break;
	case GAME_STAT_MOV1:	//一個目移動中
		//移動
		iDir = ffs(joy >> 8) - 1;	//TRG_RIがセットされていたら0、TRG_LFがセットされていたら1、TRG_DNがセットされていたら2、TRG_UPがセットされていたら3、そうでなければ0〜3以外の値になる。
		if((unsigned)iDir <= 3) {
			iCol = stGame.iCurCol + TBL_Dir[iDir].vCol;
			iRow = stGame.iCurRow + TBL_Dir[iDir].vRow;
			if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
			   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax))) {	//フィールド外ならば…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			if((iCol != stGame.iSelCol1) ||
			   (iRow != stGame.iSelRow1)) {			//元の位置でなければ…
				if((iCol != stGame.iSelCol1) &&
				   (iRow != stGame.iSelRow1)) {		//一方向だけの移動でなければ…
					sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
					return;	//ここまで
				}
				pCell = Field_GetCell(&stGame.Field, iCol, iRow);
				if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
				   !stGame.TBL_GetOrd[iRow][iCol]) {	//この位置の牌がまだ消されていなければ…
					sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
					return;	//ここまで
				}
			}
			sound_play(0, sound_table[SND_PAI_IDOU], 1);	//牌移動音
			stGame.iMovCol1 = stGame.iCurCol = iCol;
			stGame.iMovRow1 = stGame.iCurRow = iRow;
			return;	//ここまで
		}
		//移動完了
		if(!(joy & PAD_A)) {
			if(!MoveCheck1()) {	//この位置で挟めなければ…
				sound_play(1, sound_table[SND_PIPIPIPI_ERROR], 1);	//ピピピピエラー音
				stGame.iGameStat = GAME_STAT_ERR1;
				stGame.pErrMsg = "ｿﾉｲﾁﾃﾞﾊ ﾊｻﾒﾏｾﾝ";
				stGame.iErrCnt = 0;
				return;	//ここまで
			}
			stGame.iGameStat = GAME_STAT_SEL2;
			return;	//ここまで
		}
		break;
	case GAME_STAT_ERR1:	//操作ロック中1
		stGame.iErrCnt++;
		if((stGame.iErrCnt >= SEC(1.0/*調整可*/)) ||
		  ((stGame.iErrCnt >= SEC(0.2/*調整可*/)) && (joy & (TRG_LRUD|TRG_AB)))) {
			sound_play(0, sound_table[SND_UNDO], 1);	//アンドゥ音
			stGame.iGameStat = GAME_STAT_SEL1;
			stGame.iCurCol = stGame.iSelCol1;
			stGame.iCurRow = stGame.iSelRow1;
			stGame.pErrMsg = NULL;
			return;	//ここまで
		}
		break;
	case GAME_STAT_SEL2:	//二個目選択中
	    //{{ロック無し
		if(stGame.pErrMsg) {
			stGame.iErrCnt++;
			if(stGame.iErrCnt >= SEC(1.0)) {
				stGame.pErrMsg = NULL;
			}
		}
	    //}}ロック無し
		//移動
		iDir = ffs(joy >> 8) - 1;	//TRG_RIがセットされていたら0、TRG_LFがセットされていたら1、TRG_DNがセットされていたら2、TRG_UPがセットされていたら3、そうでなければ0〜3以外の値になる。
		if((unsigned)iDir <= 3) {
	    //{{ロック無し
			stGame.pErrMsg = NULL;
	    //}}ロック無し
			iCol = stGame.iCurCol + TBL_Dir[iDir].vCol;
			iRow = stGame.iCurRow + TBL_Dir[iDir].vRow;
			if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
			   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax))) {	//フィールド外ならば…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
			stGame.iCurCol = iCol;
			stGame.iCurRow = iRow;
			return;	//ここまで
		}
		//選択
		if(joy & TRG_A) {
			pCell = Field_GetCell(&stGame.Field, stGame.iCurCol, stGame.iCurRow);
			if(!pCell->iPai ||						//この位置に初期配置の牌が無いか、又は、
			   stGame.TBL_GetOrd[stGame.iCurRow][stGame.iCurCol] ||		//この位置の牌がもう消されているか、又は、
			   ((stGame.iCurCol == stGame.iSelCol1) &&
			    (stGame.iCurRow == stGame.iSelRow1))) {			//一個目選択位置と同じならば…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			if(pCell->iPai != stGame.iSelPai) {	//一個目の牌と違う種類ならば…
				sound_play(1, sound_table[SND_PIPIPIPI_ERROR], 1);	//ピピピピエラー音
	    //{{ロック無し
				stGame.pErrMsg = "ﾊﾟｲﾉｽﾞｶﾞﾗｶﾞ ﾁｶﾞｲﾏｽ";
				stGame.iErrCnt = 0;
	    //}}ロック無し
				return;	//ここまで
			}
			sound_play(0, sound_table[SND_CURSOR_SENTAKU], 1);	//カーソル選択音
			stGame.iGameStat = GAME_STAT_MOV2;
			stGame.iSelCol2 = stGame.iMovCol2 = stGame.iCurCol;
			stGame.iSelRow2 = stGame.iMovRow2 = stGame.iCurRow;
			return;	//ここまで
		}
		//取り消し
		if(joy & TRG_B) {
			sound_play(0, sound_table[SND_UNDO], 1);	//アンドゥ音
			stGame.iGameStat = GAME_STAT_SEL1;
			stGame.iCurCol = stGame.iSelCol1;
			stGame.iCurRow = stGame.iSelRow1;
			return;	//ここまで
		}
		break;
	case GAME_STAT_MOV2:	//二個目移動中
	    //{{ロック無し
		if(stGame.pErrMsg) {
			stGame.iErrCnt++;
			if(stGame.iErrCnt >= SEC(1.0)) {
				stGame.pErrMsg = NULL;
			}
		}
	    //}}ロック無し
		//移動
		iDir = ffs(joy >> 8) - 1;	//TRG_RIがセットされていたら0、TRG_LFがセットされていたら1、TRG_DNがセットされていたら2、TRG_UPがセットされていたら3、そうでなければ0〜3以外の値になる。
		if((unsigned)iDir <= 3) {
	    //{{ロック無し
			stGame.pErrMsg = NULL;
	    //}}ロック無し
			iCol = stGame.iCurCol + TBL_Dir[iDir].vCol;
			iRow = stGame.iCurRow + TBL_Dir[iDir].vRow;
			if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
			   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax))) {	//フィールド外ならば…
				sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
				return;	//ここまで
			}
			if((iCol != stGame.iSelCol2) ||
			   (iRow != stGame.iSelRow2)) {			//元の位置でなければ…
				if((iCol != stGame.iSelCol2) &&
				   (iRow != stGame.iSelRow2)) {		//一方向だけの移動でなければ…
					sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
					return;	//ここまで
				}
				pCell = Field_GetCell(&stGame.Field, iCol, iRow);
				if(pCell->iPai &&			//この位置に初期配置の牌が有り、かつ、
				   !stGame.TBL_GetOrd[iRow][iCol]) {	//この位置の牌がまだ消されていなければ…
					sound_play(0, sound_table[SND_TANJYUN_ERROR], 1);	//単純エラー音
					return;	//ここまで
				}
			}
			if((stGame.iSelCol1 != stGame.iMovCol1) ||
			   (stGame.iSelRow1 != stGame.iMovRow1)) {	//一個目の牌を移動していたら…
				sound_play(1, sound_table[SND_PIPIPIPI_ERROR], 1);	//ピピピピエラー音
	    //{{ロック無し
				stGame.pErrMsg = "ｶﾀﾎｳﾀﾞｹｦ ｳｺﾞｶｾﾏｽ";
				stGame.iErrCnt = 0;
	    //}}ロック無し
				return;	//ここまで
			}
			sound_play(0, sound_table[SND_PAI_IDOU], 1);	//牌移動音
			stGame.iMovCol2 = stGame.iCurCol = iCol;
			stGame.iMovRow2 = stGame.iCurRow = iRow;
			return;	//ここまで
		}
		//移動完了
		if(!(joy & PAD_A)) {
			if(!MoveCheck2()) {	//この位置で挟めなければ…
				sound_play(1, sound_table[SND_PIPIPIPI_ERROR], 1);	//ピピピピエラー音
				stGame.iGameStat = GAME_STAT_ERR2;
				stGame.pErrMsg = "ｿﾉｲﾁﾃﾞﾊ ﾊｻﾒﾏｾﾝ";
				stGame.iErrCnt = 0;
				return;	//ここまで
			}
			stGame.iGameStat = GAME_STAT_GET;
			return;	//ここまで
		}
		break;
	case GAME_STAT_ERR2:	//操作ロック中2
		stGame.iErrCnt++;
		if((stGame.iErrCnt >= SEC(1.0/*調整可*/)) ||
		  ((stGame.iErrCnt >= SEC(0.2/*調整可*/)) && (joy & (TRG_LRUD|TRG_AB)))) {
			sound_play(0, sound_table[SND_UNDO], 1);	//アンドゥ音
			stGame.iGameStat = GAME_STAT_SEL2;
			stGame.iCurCol = stGame.iSelCol2;
			stGame.iCurRow = stGame.iSelRow2;
			stGame.pErrMsg = NULL;
			return;	//ここまで
		}
		break;
	case GAME_STAT_GET:	//牌消し処理中
		/** no job **/
		break;
	}
}
/*--------------------------------------------------------------------------*/
//カーソルを描画する。
void Cursor_Draw() {
	int iPri, x, y, iSprNo;
	switch(stGame.iGameStat) {
	default:DIE();
	case GAME_STAT_SEL1:	//一個目選択中
	case GAME_STAT_MOV1:	//一個目移動中
	case GAME_STAT_ERR1:	//操作ロック中1
	case GAME_STAT_SEL2:	//二個目選択中
	case GAME_STAT_MOV2:	//二個目移動中
	case GAME_STAT_ERR2:	//操作ロック中2
	case GAME_STAT_GET:	//牌消し処理中
		iPri = PRI__CURSOR;
		x = X__CELL + (W__CELL * stGame.iCurCol);
		y = Y__CELL + (H__CELL * stGame.iCurRow);
		switch(stGame.iGameStat) {
		default:DIE();
		case GAME_STAT_SEL1:	//一個目選択中
		case GAME_STAT_SEL2:	//二個目選択中
		case GAME_STAT_ERR1:	//操作ロック中1
		case GAME_STAT_ERR2:	//操作ロック中2
		case GAME_STAT_GET:	//牌消し処理中
			iSprNo = SPR_CURSOR1;
			break;
		case GAME_STAT_MOV1:	//一個目移動中
		case GAME_STAT_MOV2:	//二個目移動中
			iSprNo = SPR_CURSOR2;
			break;
		}
		CbkQue_sprite_draw(pCbkQue,
			iPri,
			x,
			y,
			iSprNo,
			DRW_NOMAL);
		break;
	}
}
/*--------------------------------------------------------------------------*/
//牌を取る処理を行う。
int DoGet(int bHelp) {
	int nPoint;
	//消した回数を進める。
	stGame.iGetOrd++;
	//一個目選択位置に取ったマークを格納する。
	if(!Field_GetCell(&stGame.Field, stGame.iSelCol1, stGame.iSelRow1)->iPai) { DIE(); }	//バグ
	if(stGame.TBL_GetOrd[stGame.iSelRow1][stGame.iSelCol1]) { DIE(); }	//バグ
	stGame.TBL_GetOrd[stGame.iSelRow1][stGame.iSelCol1] = stGame.iGetOrd;
	sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
	//二個目選択位置に取ったマークを格納する。
	if(!Field_GetCell(&stGame.Field, stGame.iSelCol2, stGame.iSelRow2)->iPai) { DIE(); }	//バグ
	if(stGame.TBL_GetOrd[stGame.iSelRow2][stGame.iSelCol2]) { DIE(); }	//バグ
	stGame.TBL_GetOrd[stGame.iSelRow2][stGame.iSelCol2] = stGame.iGetOrd;
	sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
	//横方向に挟むならば…
	if(stGame.iMovRow1 == stGame.iMovRow2) {
		int iCol, iRow, iCol1, iCol2, iOfs, iDiff;
		//移動後の位置を取得する。
		iRow = stGame.iMovRow1;
		iCol1 = stGame.iMovCol1;
		iCol2 = stGame.iMovCol2;
		if(iCol1 > iCol2) { XCHG(iCol1, iCol2); }	//iCol1=左,iCol2=右
		nPoint = 1 << ((iCol2 - iCol1) - 1);
		//取るアニメーションを行う。
		iOfs = 0;
		for(;;) {
			schedule();
			if(bHelp && (joy & (TRG_B|TRG_SELECT))) { return 1; }
			Field_Draw();
			Cursor_Draw();
			iCol = iCol1;
			if(Field_GetCell(&stGame.Field, iCol, iRow)->iPai) {	//移動後の位置でなければ…
				if(!stGame.TBL_GetOrd[iRow][iCol]) {		//前回のループでまだマークしていなければ…
					stGame.TBL_GetOrd[iRow][iCol] = stGame.iGetOrd;	//取ったマークを格納する。
					sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
				}
			}
			iCol = iCol2;
			if(Field_GetCell(&stGame.Field, iCol, iRow)->iPai) {	//移動後の位置でなければ…
				if(!stGame.TBL_GetOrd[iRow][iCol]) {		//前回のループでまだマークしていなければ…
					stGame.TBL_GetOrd[iRow][iCol] = stGame.iGetOrd;	//取ったマークを格納する。
					sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
				}
			}
			{
				int iPri, x, y, iSprNo;
				iSprNo = SPR_PAI2_G1 + (stGame.iSelPai - 1);	//スプライト番号のベースはM1ではなくG1である事に注意しろ。
				iCol = iCol1;
				iPri = PRI__CELL + (iRow * 10/*調整可*/) + 1/*調整可*/;
				x = X__CELL + (W__CELL * iCol);
				y = Y__CELL + (H__CELL * iRow);
				x += iOfs;
				CbkQue_sprite_draw(pCbkQue,
					iPri,
					x,
					y,
					iSprNo,
					DRW_NOMAL);
				iCol = iCol2;
				iPri = PRI__CELL + (iRow * 10/*調整可*/) + 1/*調整可*/;
				x = X__CELL + (W__CELL * iCol);
				y = Y__CELL + (H__CELL * iRow);
				x -= iOfs;
				CbkQue_sprite_draw(pCbkQue,
					iPri,
					x,
					y,
					iSprNo,
					DRW_NOMAL);
			}
			iDiff = iCol2 - iCol1;
			if(iDiff <  0) { DIE(); }
			if(iDiff == 0) { break; }
			iOfs += 3/*調整可*/;
			if(iDiff == 1) { if(iOfs >  (W__CELL / 2)) { break;                      } }
			else           { if(iOfs >=  W__CELL     ) { iOfs = 0; iCol1++; iCol2--; } }
		}
	//縦方向に挟むならば…
	} else if(stGame.iMovCol1 == stGame.iMovCol2) {
		int iCol, iRow, iRow1, iRow2, iOfs, iDiff;
		//移動後の位置を取得する。
		iCol = stGame.iMovCol1;
		iRow1 = stGame.iMovRow1;
		iRow2 = stGame.iMovRow2;
		if(iRow1 > iRow2) { XCHG(iRow1, iRow2); }	//iRow1=上,iRow2=下
		nPoint = 1 << ((iRow2 - iRow1) - 1);
		//取るアニメーションを行う。
		iOfs = 0;
		for(;;) {
			schedule();
			if(bHelp && (joy & (TRG_B|TRG_SELECT))) { return 1; }
			Field_Draw();
			Cursor_Draw();
			iRow = iRow1;
			if(Field_GetCell(&stGame.Field, iCol, iRow)->iPai) {	//移動後の位置でなければ…
				if(!stGame.TBL_GetOrd[iRow][iCol]) {		//前回のループでまだマークしていなければ…
					stGame.TBL_GetOrd[iRow][iCol] = stGame.iGetOrd;	//取ったマークを格納する。
					sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
				}
			}
			iRow = iRow2;
			if(Field_GetCell(&stGame.Field, iCol, iRow)->iPai) {	//移動後の位置でなければ…
				if(!stGame.TBL_GetOrd[iRow][iCol]) {		//前回のループでまだマークしていなければ…
					stGame.TBL_GetOrd[iRow][iCol] = stGame.iGetOrd;	//取ったマークを格納する。
					sound_play(1, sound_table[SND_GET_IDOU], 1);	//GET移動音
				}
			}
			{
				int iPri, x, y, iSprNo;
				iSprNo = SPR_PAI2_G1 + (stGame.iSelPai - 1);	//スプライト番号のベースはM1ではなくG1である事に注意しろ。
				iRow = iRow1;
				iPri = PRI__CELL + (iRow * 10/*調整可*/) + 1/*調整可*/;		if(iOfs) { iPri += 10/*調整可*/; }
				x = X__CELL + (W__CELL * iCol);
				y = Y__CELL + (H__CELL * iRow);
				y += iOfs;
				CbkQue_sprite_draw(pCbkQue,
					iPri,
					x,
					y,
					iSprNo,
					DRW_NOMAL);
				iRow = iRow2;
				iPri = PRI__CELL + (iRow * 10/*調整可*/) + 2/*調整可*/;
				x = X__CELL + (W__CELL * iCol);
				y = Y__CELL + (H__CELL * iRow);
				y -= iOfs;
				CbkQue_sprite_draw(pCbkQue,
					iPri,
					x,
					y,
					iSprNo,
					DRW_NOMAL);
			}
			iDiff = iRow2 - iRow1;
			if(iDiff <  0) { DIE(); }
			if(iDiff == 0) { break; }
			iOfs += 3/*調整可*/;
			if(iDiff == 1) { if(iOfs >  (H__CELL / 2)) { break;                      } }
			else           { if(iOfs >=  H__CELL     ) { iOfs = 0; iRow1++; iRow2--; } }
		}
	} else {
		DIE();	//バグ
	}
	if(!bHelp) {
		//スコアを加算する。
		stGame.nScore += nPoint;
	}
	//スコアエフェクトを生成する。		ここでロックして表示すると操作性が悪かったので、次の操作と同時進行する事にした。
	stGame.nScoEff = nPoint;
	stGame.iScoCnt = 0;
	stGame.iScoX = X__CELL + ((stGame.iMovCol1 + stGame.iMovCol2) * W__CELL) / 2;
	stGame.iScoY = Y__CELL + ((stGame.iMovRow1 + stGame.iMovRow2) * H__CELL) / 2;
	sound_play(1, sound_table[SND_SCORE_EFFECT], 1);	//スコアエフェクト音
	return 0;
}
/****************************************************************************
 *	Game_Run
 ****************************************************************************/
void Game_Run() {
	Game_Init();
	//開始演出
	{
		int i, iCol, iRow, iDir;
		sound_play(1, sound_table[SND_DORA], 1);	//ドラ音
		for(iRow = stGame.Field.iRowMin; iRow <= stGame.Field.iRowMax; iRow++) {
			for(iCol = stGame.Field.iColMin; iCol <= stGame.Field.iColMax; iCol++) {
				stGame.TBL_StartMask[iRow][iCol] = 1;
			}
		}
		for(i = (W__CELL * MAX_COLS) + (SEC(1.5) * 4); i > 0; i -= 4) {
			stGame.nStartOffset = i;
			schedule();
			Field_Draw();
			if(i == (W__CELL * MAX_COLS)) {
				PlayMusic(START_JINGLE);
			}
			if(joy & (TRG_LRUD|TRG_AB)) { goto L_START_10; }
		}
		stGame.nStartOffset = 0;
		for(i = 0; i < SEC(0.34); i++) {
			schedule();
			Field_Draw();
			if(joy & (TRG_LRUD|TRG_AB)) { goto L_START_10; }
		}
		iDir = 0;
		iCol = stGame.Field.iColMin;
		iRow = stGame.Field.iRowMin;
		for(;;) {
			schedule();
			Field_Draw();
			if(joy & (TRG_LRUD|TRG_AB)) { goto L_START_10; }
			for(i = 0; i < 2/*調整可*/; i++) {
				sound_play(0, sound_table[SND_PAI_IDOU], 1);	//牌移動音
				stGame.TBL_StartMask[iRow][iCol] = 0;
				if(!memchr(stGame.TBL_StartMask, 1, sizeof stGame.TBL_StartMask)) { break; }
				for(;;) {
					iCol += TBL_Dir[iDir].vCol;
					iRow += TBL_Dir[iDir].vRow;
					if(((iCol < stGame.Field.iColMin) || (iCol > stGame.Field.iColMax)) ||
					   ((iRow < stGame.Field.iRowMin) || (iRow > stGame.Field.iRowMax)) ||
					   !stGame.TBL_StartMask[iRow][iCol]) {
						iCol -= TBL_Dir[iDir].vCol;
						iRow -= TBL_Dir[iDir].vRow;
						iDir = (iDir + 1) & 3;
					} else {
						break;
					}
				}
			}
			if(!memchr(stGame.TBL_StartMask, 1, sizeof stGame.TBL_StartMask)) { break; }
		}
		for(i = 0; i < SEC(0.34); i++) {
			schedule();
			Field_Draw();
			if(joy & (TRG_LRUD|TRG_AB)) { goto L_START_10; }
		}
	L_START_10:
		stGame.nStartOffset = 0;
		memset(stGame.TBL_StartMask, 0, sizeof stGame.TBL_StartMask);
	}
	//ゲーム
L_GAME:
	{
		PlayMusic(MAIN_BGM);
		stGame.bBgmPlaying = 1;
	}
	stGame.nScore = 0;
	stGame.iGetOrd = 0;
	memset(stGame.TBL_GetOrd, 0, sizeof stGame.TBL_GetOrd);
	stGame.iGameStat = GAME_STAT_SEL1;
	stGame.iCurCol = stGame.Field.iColMin;
	stGame.iCurRow = stGame.Field.iRowMin;
	for(;;) {
		stGame.iGameStat = GAME_STAT_SEL1;
		for(;;) {
			schedule();
			Field_Draw();
			Cursor_Draw();
			Cursor_Exec();
			if(stGame.iGameStat == GAME_STAT_GET) { break; }
			if(stGame.iMenuStat && (joy & TRG_A)) {
				sound_play(0, sound_table[SND_CURSOR_SENTAKU], 1);	//カーソル選択音
				switch(stGame.iMenuStat) {
				default:DIE();
				case MENU_STAT_GAME:
					stGame.iMenuStat = 0;
					break;
				case MENU_STAT_HELP:
					stGame.iMenuStat = 0;
					goto L_HELP;
				case MENU_STAT_TITLE:
				//不要	stGame.iMenuStat = 0;
					return;	//戻る
				}
			}
			if(!stGame.iMenuStat && stGame.nTime) { stGame.nTime--; }
		}
		DoGet(0);
		if(IsCleared()) { break; }
		if(IsDeadLock()) {
			stGame.iGameStat = GAME_STAT_SEL1;	//必要。stGame.iSelCol1,stGame.iMovCol1,stGame.iSelRow1,stGame.iMovRow1を書き換えているので、GAME_STAT_GETのままだと待ち中に牌が表示されないから。
			while(stGame.nScoEff) {	//スコエエフェクトが消えるまで"ﾃﾂﾞﾏﾘ ﾃﾞｽ"の表示を待つ。
				schedule();
				Field_Draw();
				Cursor_Draw();
			}
			{
				PlayMusic(TEZUMARI_JINGLE);
				stGame.bBgmPlaying = 0;
			}
		}
	}
	//クリア演出
	{
		const int nClearPrize = 100;
		const int nTime = ((stGame.nTime + (FPS - 1)) / FPS);
		const int nTotal = nClearPrize + nTime;
		int i, x, y, bDora;
		for(i = 0; i < SEC(1.5)/*調整可*/; i++) {
			schedule();
			Field_Draw();
			Cursor_Draw();
			if(i == SEC(0.5)) {
				StopMusic();
			}
		}
		PlayMusic(CLEAR_JINGLE);
		bDora = 1;
		for(i = 0; i < SEC(10.0); i++) {
			schedule();
			Field_Draw();
			if(bDora && MusicCheck()) {
				sound_play(1, sound_table[SND_DORA], 1);	//ドラ音
				bDora = 0;
			}
			x = 15;
			y = 32;
			CbkQue_render_rectangle(       pCbkQue, SHRT_MAX, &render, x - 3, y - 3, 75, 30,    0);
			CbkQue_render_rectangle_shadow(pCbkQue, SHRT_MAX, &render, x - 2, y - 2, 73, 28, 2, 3);
			if(i >= SEC(0.5)) {
				CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 3, 0x220, "CLEAR PRIZE %5d", nClearPrize * 10);
				if(i == SEC(0.5)) {
					sound_play(0, sound_table[SND_SCORE_HYOUJI], 1);	//スコア表示音
				}
			}
			y += 9;
			if(i >= SEC(1.0)) {
				CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 3, 0x220, "TIME x 10   %5d", nTime * 10);
				if(i == SEC(1.0)) {
					sound_play(0, sound_table[SND_SCORE_HYOUJI], 1);	//スコア表示音
				}
			}
			y += 9;
			if(i >= SEC(1.5)) {
				CbkQue_render_printf_framed(pCbkQue, SHRT_MAX, &render, x, y, 3, 0x220, "TOTAL       %5d", nTotal * 10);
				if(i == SEC(1.5)) {
					sound_play(0, sound_table[SND_SCORE_HYOUJI], 1);	//スコア表示音
				}
			}
			if(i == SEC(2.0)) {
				stGame.nScore += nTotal;
				sound_play(0, sound_table[SND_SCORE_KASAN], 1);	//スコア加算音
				if(stGame.nScore > hiscore) { hiscore = stGame.nScore; }
			}
			if(i >= SEC(2.5)) {
				if(joy & (TRG_AB|TRG_SELECT)) { break; }
			}
		}
	}
	return;	//ここまで
	//解き方
L_HELP:
	StopMusic();
	{
		const int HELP_DELAY = 3/*調整可*/;
		int i, iCol1, iRow1, iCol2, iRow2;
		int iPutOrd = stGame.Field.iPutOrd;
		stGame.nScore = 0;
		stGame.iGetOrd = 0;
		memset(stGame.TBL_GetOrd, 0, sizeof stGame.TBL_GetOrd);
		stGame.iGameStat = GAME_STAT_SEL1;
		stGame.iCurCol = stGame.Field.iColMin;
		stGame.iCurRow = stGame.Field.iRowMin;
		for(i = 0; i < SEC(1.0)/*調整可*/; i++) {
			schedule();
			Field_Draw();
		//	Cursor_Draw();
			if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
		}
		do {
			ST_Cell* pCell;
			//動かす(動かさない事も有る)端を検索する。
			for(iRow1 = stGame.Field.iRowMin; iRow1 <= stGame.Field.iRowMax; iRow1++) {
				for(iCol1 = stGame.Field.iColMin; iCol1 <= stGame.Field.iColMax; iCol1++) {
					pCell = Field_GetCell(&stGame.Field, iCol1, iRow1);
					if((pCell->iPutOrd == iPutOrd) &&
					   (pCell->iSide == 1/*動かす(動かさない事も有る)端*/)) { goto L_HELP_10; }
				}
			}
			DIE();	//バグ
		L_HELP_10:
			//移動先を取得する。
			pCell = Field_GetCell(&stGame.Field, iCol1, iRow1);
			iCol2 = pCell->iMovCol;
			iRow2 = pCell->iMovRow;
			if((iCol1 != iCol2) && (iRow1 != iRow2)) { DIE(); }	//少なくともどちらか(又は両方)が同じであるはず。両方とも違ったらバグです。
			//動かす(動かさない事も有る)端を移動する。
			stGame.iGameStat = GAME_STAT_SEL1;
			stGame.iCurCol = iCol1;
			stGame.iCurRow = iRow1;
			for(i = 0; i < HELP_DELAY; i++) {
				schedule();
				Field_Draw();
				Cursor_Draw();
				if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
			}
			joy = TRG_A|PAD_A;
			Cursor_Exec();
			if(stGame.iGameStat != GAME_STAT_MOV1) { DIE(); }
			for(;;) {
				for(i = 0; i < HELP_DELAY; i++) {
					schedule();
					Field_Draw();
					Cursor_Draw();
					if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
				}
				if((stGame.iCurCol == iCol2) &&
				   (stGame.iCurRow == iRow2)) { break; }
				if(stGame.iCurCol < iCol2) { joy = TRG_RI|PAD_A; }
				if(stGame.iCurCol > iCol2) { joy = TRG_LF|PAD_A; }
				if(stGame.iCurRow < iRow2) { joy = TRG_DN|PAD_A; }
				if(stGame.iCurRow > iRow2) { joy = TRG_UP|PAD_A; }
				Cursor_Exec();
				if(stGame.iGameStat != GAME_STAT_MOV1) { DIE(); }
			}
			joy = 0;
			Cursor_Exec();
			if(stGame.iGameStat != GAME_STAT_SEL2) { DIE(); }
			//動かさない端を検索する。
			for(iRow1 = stGame.Field.iRowMin; iRow1 <= stGame.Field.iRowMax; iRow1++) {
				for(iCol1 = stGame.Field.iColMin; iCol1 <= stGame.Field.iColMax; iCol1++) {
					pCell = Field_GetCell(&stGame.Field, iCol1, iRow1);
					if((pCell->iPutOrd == iPutOrd) &&
					   (pCell->iSide == 3/*動かさない端*/)) { goto L_HELP_20; }
				}
			}
			DIE();	//バグ
		L_HELP_20:
			//動かさない端を決定する。
			stGame.iCurCol = iCol1;
			stGame.iCurRow = iRow1;
			for(i = 0; i < HELP_DELAY; i++) {
				schedule();
				Field_Draw();
				Cursor_Draw();
				if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
			}
			joy = TRG_A|PAD_A;
			Cursor_Exec();
			if(stGame.iGameStat != GAME_STAT_MOV2) { DIE(); }
			for(i = 0; i < HELP_DELAY; i++) {
				schedule();
				Field_Draw();
				Cursor_Draw();
				if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
			}
			joy = 0;
			Cursor_Exec();
			if(stGame.iGameStat != GAME_STAT_GET) { DIE(); }
			//牌を消す。
			if(DoGet(1)) { goto L_GAME; }
		} while(--iPutOrd);
		if(!IsCleared()) { DIE(); }	//バグ
		for(i = 0; i < SEC(1.0)/*調整可*/; i++) {
			schedule();
			Field_Draw();
		//	Cursor_Draw();
			if(joy & (TRG_B|TRG_SELECT)) { goto L_GAME; }
		}
	}
	goto L_GAME;
}
/****************************************************************************
 *	Demo_Run
 ****************************************************************************/
void Demo_Run() {
	int iSel = 0, bStart = 0;
	int x, y;
	for(bStart = 0; bStart < SEC(1.0); /** no job **/) {
		schedule();
		if(joy & TRG_SELECT) { exit(EXIT_SUCCESS); }
		//
		x = UPDOWN_MOTION((now / 8), 21) - 10;
		x = clamp(x, -8, 8);
		CbkQue_sprite_draw(pCbkQue, 0, x, 0, SPR_PHOTO, DRW_NOMAL);
		CbkQue_sprite_draw(pCbkQue, 0, 0, 0, SPR_TITLE, DRW_NOMAL);
		x = 103;
		y =  43;
		CbkQue_render_printf_framed(pCbkQue, 0, &render, x  , y  , 3, 0x003, "%s", "ﾓﾄｷ");
		CbkQue_render_printf_framed(pCbkQue, 0, &render, x+2, y-2, 3, 0x003, "%s", " ﾞ ");
		//
		CbkQue_render_printf_framed(pCbkQue, 0, &render,           1, 1, 3, 0x330, "1P %07d", stGame.nScore * 10);
		CbkQue_render_printf_framed(pCbkQue, 0, &render, DISP_X - 42, 1, 3, 0x330, "HI %07d",       hiscore * 10);
		x = 22;
		y = 53;
		if(!bStart) {
			if(iSel == 0) {
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x330, "盤面の幅   %c %2d %c", (cols_sel > 2) ? '<' : ' ', cols_sel, (cols_sel < MAX_COLS) ? '>' : ' ');
				y += 13;
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x331, "盤面の高さ %c %2d %c",                        ' ', rows_sel,                               ' ');
				if((joy & TRG_LF) && (cols_sel > 2)) {
					cols_sel--;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
				if((joy & TRG_RI) && (cols_sel < MAX_COLS)) {
					cols_sel++;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
				if(joy & TRG_DN) {
					iSel = 1;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
			} else {
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x331, "盤面の幅   %c %2d %c",                        ' ', cols_sel,                               ' ');
				y += 13;
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x330, "盤面の高さ %c %2d %c", (rows_sel > 2) ? '<' : ' ', rows_sel, (rows_sel < MAX_ROWS) ? '>' : ' ');
				if((joy & TRG_LF) && (rows_sel > 2)) {
					rows_sel--;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
				if((joy & TRG_RI) && (rows_sel < MAX_ROWS)) {
					rows_sel++;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
				if(joy & TRG_UP) {
					iSel = 0;
					sound_play(0, sound_table[SND_CURSOR_IDOU], 1);	//カーソル移動音
				}
			}
		} else {
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x330, "盤面の幅   %c %2d %c",                        ' ', cols_sel,                               ' ');
				y += 13;
				CbkQue_render_printf_framed(pCbkQue, 0, &render, x, y, 0, 0x330, "盤面の高さ %c %2d %c",                        ' ', rows_sel,                               ' ');
		}
		//
		if(!bStart) {
			if((now % SEC(2.0)) < SEC(1.0)) {
				CbkQue_render_printf_framed(pCbkQue, 0, &render, 18, DISP_Y - 7, 3, 0x330, "PRESS A BUTTON TO START");
			}
			if(joy & TRG_AB) {
				bStart = 1;
				sound_play(0, sound_table[SND_START], 1);	//スタート音
			}
		} else {
			if(bStart & 1) {
				CbkQue_render_printf_framed(pCbkQue, 0, &render, 18, DISP_Y - 7, 3, 0x330, "PRESS A BUTTON TO START");
			}
			bStart++;
		}
	}
}
/****************************************************************************
 *	app_main
 ****************************************************************************/
int app_main(int argc, char* argv[]) {
    //{{隠し操作:Bボタンを押しながら起動すると最大音量にして電池表示を消します。
	delay(1);
	if(joy & PAD_B) {
		pceWaveSetMasterAtt(0);
		pcePowerSetReport(PWR_RPTOFF);
	}
    //}}隠し操作:Bボタンを押しながら起動すると最大音量にして電池表示を消します。
	cols_sel = MAX_COLS/2;
	rows_sel = MAX_ROWS/2;
	for(;;) {
		Demo_Run();
		Game_Run();
		StopMusic();
	}
	return 0;
}
