NimotsuKun for MS-DOS その2

C++からCOMファイルが生成できない。

>msdos tcc -mt nimotsu.cpp
Turbo C++  Version 1.01 Copyright (c) 1990 Borland International
nimotsu.cpp:
Turbo Link  Version 3.01 Copyright (c) 1987, 1990 Borland International
Warning: No stack

        Available memory 408752

>msdos exe2bin nimotsu.exe nimotsu.com
File cannot be converted

Cがあるじゃないか!

>msdos tcc -mt nimotsu.c
Turbo C++  Version 1.01 Copyright (c) 1990 Borland International
nimotsu.c:
Turbo Link  Version 3.01 Copyright (c) 1987, 1990 Borland International
Warning: No stack

        Available memory 492048

>msdos exe2bin nimotsu.exe nimotsu.com

>msdos nimotsu.com
########
# .. p #
# oo   #
#      #
########
a:left s:right w:up z:down. command?

nimotsu.c

#include <stdio.h>
#include <string.h>
typedef int bool;
typedef int Object;
#define true 1
#define false 0

/* #壁 _空間 .ゴール oブロック p人 */
char gStageData[] = "\
########\n\
# .. p #\n\
# oo   #\n\
#      #\n\
########";
#define gStageWidth 8
#define gStageHeight 5

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

	OBJ_UNKNOWN,
};

/* 関数プロトタイプ */
void initialize( Object* state, int w, char* stageData );
void draw( Object* state, int w, int h );
void update( Object* state, char input, int w, int h );
bool checkClear( Object* state, int w, int h );

int main(){
        char input[1];
	/* 一次元配列である理由は本文参照 */
	Object* state = (Object*)malloc(sizeof(int) * gStageWidth * gStageHeight); /* 状態配列確保 */

	initialize( state, gStageWidth, gStageData ); /* ステージ初期化 */
	/* メインループ */
	while ( true ){
		/* まず描画 */
		draw( state, gStageWidth, gStageHeight );
		/* クリアチェック */
		if ( checkClear(state, gStageWidth, gStageHeight ) ){
			break; /* クリアチェック */
		}
		/* 入力取得 */
		printf("a:left s:right w:up z:down. command?\n"); /* 操作説明 */
		
		gets(input);
		/* 更新 */
		update( state, input[0], gStageWidth, gStageHeight ); 	
	}
	/* 祝いのメッセージ */
	printf("Congratulation's! you won.\n");
	/* 後始末 */
        free(state);
	state = 0;

	return 0;
}

/* ---------------------以下関数定義------------------------------------------ */


/* いつか使う日も来るだろうと高さも渡す仕様にしたが、現状使っていないので名前だけ(height)コメントアウトしてある。 */
void initialize( Object* state, int width, char* stageData ){	
	const char* d = stageData; /* 読み込みポインタ */
	int x = 0;
	int y = 0;
	while ( *d != '\0' ){ /* NULL文字でない間 */
		Object t; /* 特に意味はないが使う回数が多い変数に私は良くtを使う。temporaryの略。たぶんよくない習慣だが、無駄に長い名前にして読みにくいのも困り物だろう。 */
		switch ( *d ){
			case '#': t = OBJ_WALL; break;
			case ' ': t = OBJ_SPACE; break;
			case 'o': t = OBJ_BLOCK; break;
			case 'O': t = OBJ_BLOCK_ON_GOAL; break;
			case '.': t = OBJ_GOAL; break;
			case 'p': t = OBJ_MAN; break;
			case 'P': t = OBJ_MAN_ON_GOAL; break;
			case '\n': x = 0; ++y; t = OBJ_UNKNOWN; break; /* 改行処理 */
			default: t = OBJ_UNKNOWN; break;
		}
		++d;
		if ( t != OBJ_UNKNOWN ){ /* 知らない文字なら無視するのでこのif文がある */
			state[ y*width + x ] = t; /* 書き込み */
			++x;
		}
	}
}

void draw(Object* state, int width, int height ){
        int x, y;
	char font[] = {' ', '#', '.', 'o', 'O', 'p', 'P'}; /* Object列挙の順 */
	for ( y = 0; y < height; ++y ){
		for (x=0; x < width; ++x ){
			Object o = state[ y*width + x ];
                        printf("%c",font[ o ]);
		}
                printf("\n");
	}
}

/* 第一引数はほかの関数ではstateとしているが、あまりに頻繁に使うので
短いsで済ませている。w,hもそれぞれwidth,heightである。 */
void update( Object* s, char input, int w, int h ){
	/* 移動差分に変換(dはdifferenceでもdeltaでもお好きな方の略だと思って欲しい) */
	int dx = 0; 
	int dy = 0;
	int i = -1;
        int x, y, tx, ty, p, tp, tx2, ty2, tp2;
	switch ( input ){
		case 'a': dx = -1; break;
		case 's': dx = 1; break;
		case 'w': dy = -1; break;
		case 'z': dy = 1; break;
	}
	/* 人座標を検索 */
	for ( i = 0; i < w * h; ++i ){
		if ( s[ i ] == OBJ_MAN || s[ i ] == OBJ_MAN_ON_GOAL ){
			break;
		}
	}
	x = i % w; /* xは幅で割ったあまり */
	y = i / w; /* yは幅で割った商 */

	/* 移動後座標(tに意味はない。ごめんなさい) */
	tx = x + dx;
	ty = y + dy;
	/* 座標の最大最小チェック。外れていれば不許可 */
	if ( tx < 0 || ty < 0 || tx >= w || ty >= h ){
		return;
	}
	/* A.その方向が空白またはゴール。人が移動。 */
	p = y*w + x; /* 人位置 */
	tp = ty*w + tx; /* ターゲット位置(TargetPosition) */
	if ( s[ tp ] == OBJ_SPACE || s[ tp ] == OBJ_GOAL ){
		s[ tp ] = ( s[ tp ] == OBJ_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN; /* ゴールならゴール上の人に */
		s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE; /* もともとゴール上ならゴールに */
	/* B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。 */
	}else if ( s[ tp ] == OBJ_BLOCK || s[ tp ] == OBJ_BLOCK_ON_GOAL ){
		/* 2マス先が範囲内かチェック */
		tx2 = tx + dx;
		ty2 = ty + dy; 
		if ( tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h ){ /* 押せない */
			return;
		}

		tp2 = ( ty + dy )*w + ( tx + dx ); /* 2マス先 */
		if ( s[ tp2 ] == OBJ_SPACE || s[ tp2 ] == OBJ_GOAL ){
			/* 順次入れ替え */
			s[ tp2 ] = ( s[ tp2 ] == OBJ_GOAL ) ? OBJ_BLOCK_ON_GOAL : OBJ_BLOCK;
			s[ tp ] = ( s[ tp ] == OBJ_BLOCK_ON_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN;
			s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE;
		}
	}
}

/* ブロックのみがなければクリアしている。 */
bool checkClear(Object* s, int width, int height ){
        int i;
	for (i = 0; i < width*height; ++i ){
		if ( s[ i ] == OBJ_BLOCK ){
			return false;
		}
	}
	return true;
}