NimotsuKun for MS-DOS その3

せっかくなのでC++ぽく書いてみる。なおCOMファイルは生成できない。

>msdos tcc -ms nimotsu2.cpp
>msdos nimotsu.exe
########
# .. p #
# oo   #
#      #
########
a:left s:right w:up z:down. command?

nimotsu2.cpp

#include <iostream.h>
typedef int bool;
#define true 1
#define false 0
#define max(a,b) (((a)>(b))?(a):(b))
#define matrix(x,y) (x + y * mWidth)

//#壁 _空間 .ゴール oブロック p人
const char gStageData[] = "\
########\n\
# .. p #\n\
# oo   #\n\
#      #\n\
########\n";

enum Object{
	OBJ_SPACE,
	OBJ_WALL,
	OBJ_GOAL,
	OBJ_BLOCK,
	OBJ_BLOCK_ON_GOAL,
	OBJ_MAN,
	OBJ_MAN_ON_GOAL,

	OBJ_UNKNOWN,
};

//状態クラス
class State{
public:
	State( const char* stageData, int size );
	void update( char input );
	void draw();
	bool hasCleared();
private:
	void setSize( const char* stageData, int size );
	//マス描画関数
	static void drawCell( int x, int y, unsigned color );
	int mWidth;
	int mHeight;
	int* mObjects;
	int* mGoalFlags;
};

//グローバル変数
State* gState = 0;

int main(){
	gState = new State(gStageData, sizeof(gStageData));
	gState->draw();
	//メインループ
	while (true) {
		//メインループ
		//入力取得
		cout << "a:left s:right w:up z:down. command?" << endl; //操作説明
		char input;
		cin >> input;
		//更新
		gState->update( input );
		//描画
		gState->draw();
		//クリアチェック
		if ( gState->hasCleared() ){
			//祝いのメッセージ
			cout << "Congratulation! you win." << endl;
			delete gState;
			gState = 0;
			return 0;
		}
	}
}

//---------------------以下関数定義------------------------------------------
State::State(const char* stageData, int size ){
	int x = 0;
	int y = 0;
	//サイズ測定
	setSize( stageData, size );
	//配列確保
	mObjects = new int[ mWidth * mHeight ]; //状態配列確保
	mGoalFlags = new int[ mWidth * mHeight ]; //状態配列確保
	//初期値で埋めとく
	for ( y = 0; y < mHeight; ++y ){
		for ( x = 0; x < mWidth; ++x ){
			mObjects[matrix(x , y)] = OBJ_WALL; //あまった部分は壁
			mGoalFlags[matrix(x , y)] = false; //ゴールじゃない
		}
	}
	const char* d = stageData; //読み込みポインタ
	x = 0;
	y = 0;
	for (int i = 0; i < size; ++i ){
		Object t;
		bool goalFlag = false;
		switch ( *d ){
			case '#': t = OBJ_WALL; break;
			case ' ': t = OBJ_SPACE; break;
			case 'o': t = OBJ_BLOCK; break;
			case 'O': t = OBJ_BLOCK; goalFlag = true; break;
			case '.': t = OBJ_SPACE; goalFlag = true; break;
			case 'p': t = OBJ_MAN; break;
			case 'P': t = OBJ_MAN; goalFlag = true; break;
			case '\n': x = 0; ++y; t = OBJ_UNKNOWN; break; //改行処理
			default: t = OBJ_UNKNOWN; break;
		}
		++d;
		if ( t != OBJ_UNKNOWN ){ //知らない文字なら無視するのでこのif文がある
			mObjects[matrix(x , y)] = t; //書き込み
			mGoalFlags[matrix(x , y)] = goalFlag; //ゴール情報
			++x;
		}
	}
}

void State::setSize( const char* stageData, int size ){
	mWidth = mHeight = 0; //初期化
	//現在位置
	int x = 0;
	int y = 0;
	for ( int i = 0; i < size; ++i ){
		switch ( stageData[ i ] ){
			case '#': case ' ': case 'o': case 'O':
			case '.': case 'p': case 'P':
				++x;
				break;
			case '\n': 
				++y;
				//最大値更新
				mWidth = max( mWidth, x );
				mHeight = max( mHeight, y );
				x = 0; 
				break;
		}
	}
}

void State::draw() {
	for ( int y = 0; y < mHeight; ++y ){
		for ( int x = 0; x < mWidth; ++x ){
			Object o = mObjects[matrix(x , y)];
			bool goalFlag = mGoalFlags[matrix(x , y)];
			unsigned color = 0;
			if ( goalFlag ){
				switch ( o ){
					case OBJ_SPACE: cout << '.'; color = 0x0000ff; break;
					case OBJ_WALL: cout << '#'; color = 0xffffff; break;
					case OBJ_BLOCK: cout << 'O'; color = 0xff00ff; break;
					case OBJ_MAN: cout << 'P'; color = 0x00ffff; break;
				}
			}else{
				switch ( o ){
					case OBJ_SPACE: cout << ' '; color = 0x000000; break;
					case OBJ_WALL: cout << '#'; color = 0xffffff; break;
					case OBJ_BLOCK: cout << 'o'; color = 0xff0000; break;
					case OBJ_MAN: cout << 'p'; color = 0x00ff00; break;
				}
			}
			drawCell( x, y, color );
		}
		cout << endl;
	}
}

void State::drawCell( int x, int y, unsigned color ){
///	unsigned* vram = Framework::instance().videoMemory();
///	unsigned windowWidth = Framework::instance().width();
///	for ( int i = 0; i < 16; ++i ){
///		for ( int j = 0; j < 16; ++j ){
///			vram[ (y*16+i) * windowWidth + (x*16+j) ] = color;
///		}
///	}
	return;
}

void State::update( char input ){
	//移動差分に変換
	int dx = 0;
	int dy = 0;
	switch ( input ){
		case 'a': dx = -1; break; //左
		case 's': dx = 1; break; //右
		case 'w': dy = -1; break; //上。Yは下がプラス
		case 'z': dy = 1; break; //下。
	}
	//短い変数名をつける。
	int w = mWidth;
	int h = mHeight;
	//人座標を検索
	int x, y;
	x = y = -1;
	bool found = false;
	for ( y = 0; y < mHeight; ++y ){
		for ( x = 0; x < mWidth; ++x ){
			if ( mObjects[matrix(x , y)] == OBJ_MAN ){
				found = true;
				break;
			}
		}
		if ( found ){
			break;
		}
	}
	//移動
	//移動後座標
	int tx = x + dx;
	int ty = y + dy;
	//座標の最大最小チェック。外れていれば不許可
	if ( tx < 0 || ty < 0 || tx >= w || ty >= h ){
		return;
	}
	//A.その方向が空白またはゴール。人が移動。
	if ( mObjects[ tx + ty * mWidth] == OBJ_SPACE ){
		mObjects[matrix(tx,ty)] = OBJ_MAN;
		mObjects[matrix(x , y)] = OBJ_SPACE;
	//B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。
	}else if ( mObjects[matrix(tx,ty)] == OBJ_BLOCK ){
		//2マス先が範囲内かチェック
		int tx2 = tx + dx;
		int ty2 = ty + dy; 
		if ( tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h ){ //押せない
			return;
		}
		if ( mObjects[matrix(tx2,ty2)] == OBJ_SPACE ){
			//順次入れ替え
			mObjects[matrix(tx2,ty2)] = OBJ_BLOCK;
			mObjects[matrix(tx ,ty )] = OBJ_MAN;
			mObjects[matrix( x , y )] = OBJ_SPACE;
		}
	}
}

//ブロックのところのgoalFlagが一つでもfalseなら
//まだクリアしてない
bool State::hasCleared() {
	for ( int y = 0; y < mHeight; ++y ){
		for ( int x = 0; x < mWidth; ++x ){
			if ( mObjects[matrix(x , y)] == OBJ_BLOCK ){
				if ( mGoalFlags[matrix(x , y)] == false ){
					return false;
				}
			}
		}
	}
	return true;
}