tanamの日記

2017-12-07

NimotsuKun for WSC その2

08:55

ゲームプログラマになる前に覚えておきたい技術

http://www.shuwasystem.co.jp/support/7980html/2118.html

f:id:tanam:20171207085523p:image:w360

サンプルプログラム荷物君にシーケンスをつけてみました。

http://www.geocities.jp/parallel_computer_inc/nimotsu.zip

gamelib.c

/*

  Nimotsu Kun for WSC
                               by tanam [2017]

  WonderWitch replacement lib for Wonderswan without using WonderWitch hardware
  Use Turbo C 2.01 and WSXLib by zalas

*/

#include "wwwlib.h"
#include "gamelib.h"
#include "nimotsu.h"

int stage, k;

void init( void ){
	wwc_set_color_mode(COLOR_MODE_4COLOR);
	wwc_palette_set_color(0, 0, 0x0000);	/* black */
	wwc_palette_set_color(0, 1, 0x0f00);	/* red */
	wwc_palette_set_color(0, 2, 0x00f0);	/* green */
	wwc_palette_set_color(0, 3, 0x000f);	/* blue */
	font_set_colordata(0,126, nimotsu_chr);
	display_control(DCM_SCR2);
	return;
}

void title( void ){
	drawascii( gStageData[4], gStageWidth, gStageHeight );
	screen_set_char(SCREEN2, 0, 0, 18, 17, screen);
	while (key_press_check() != KEY_START ) sys_wait(1);
	return;
}

int select( void ){
	drawascii( gStageData[5], gStageWidth, gStageHeight );
	screen_set_char(SCREEN2, 0, 0, 18, 17, screen);
	sys_wait(7);
	while (true) {
		k = key_press_check();
		if (k==KEY_LEFT1) {gStageData[5][18 + stage]=' ';if (stage>0) stage--;}
		if (k==KEY_RIGHT1) {gStageData[5][18 + stage]=' ';if (stage<4) stage++;}
		gStageData[5][18 + stage]='v';
		if (k==KEY_START) {gStageData[5][18 + stage]=' ';break;}
		drawascii( gStageData[5], gStageWidth, gStageHeight );
		screen_set_char(SCREEN2, 0, 0, 18, 9, screen);
		if (stage<4) initialize( state, gStageData[stage] );
		if (stage<4) draw( state, gStageWidth, gStageHeight );
		if (stage<4) screen_set_char(SCREEN2, 0, 10, 9, 7, screen);
		sys_wait(7);
	}
	return stage;
}

void load( void ){
	memset(screen, BLACK, 2048);
	drawascii( gStageData[6], gStageWidth, gStageHeight );
	screen_set_char(SCREEN2, 0, 0, 18, 17, screen);
	sys_wait(70);
	return;
}

void clear( void ){
	memset(screen, BLACK, 2048);
	drawascii( gStageData[7], gStageWidth, gStageHeight );
	screen_set_char(SCREEN2, 0, 0, 18, 17, screen);
	sys_wait(70);
	return;
}

int main(){
	 /* 初期化 */
	init();
	 /* タイトル画面 */
	title();
	 /* ステージセレクト */
	stage=0;
SELECT:
	if (4==select()) {
		sys_wait(7);
		return 0;
	}
	 /* スタート */
	load();
	for (; stage<4; stage++) {
		initialize( state, gStageData[stage] ); /* ステージ初期化 */
		/* メインループ */
		while ( true ){
			/* まず描画 */
			sys_wait(7);
			draw4x( state, gStageWidth, gStageHeight );
			screen_set_char(SCREEN2, 0, 0, 18, 18, screen);
			/* クリアチェック */
			if ( checkClear(state, gStageWidth, gStageHeight ) ){
				break; /* クリアチェック */
			}
			k=key_press_check();
			switch (k) {
			case KEY_START: goto SELECT;
			case KEY_UP1: k = UP;break;
			case KEY_DOWN1: k = DOWN;break;
			case KEY_LEFT1: k = LEFT;break;
			case KEY_RIGHT1: k = RIGHT;break;
			}
			update( state, k, gStageWidth, gStageHeight );
		}
	}
	 /* ゲームクリア */
	clear();
	return 0;
}

2017-12-05

NimotsuKun for WSC

22:31

ゲームプログラマになる前に覚えておきたい技術

http://www.shuwasystem.co.jp/support/7980html/2118.html

f:id:tanam:20171206055219j:image:w360

サンプルプログラム荷物君(NimotsuKunBox)をワンダースワンで動かしてみます。

http://www.geocities.jp/parallel_computer_inc/nimotsu2.zip

nimotsu2.c


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

unsigned short nimotsu_chr[] = {
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x00AA,0x0055,0x00AA,0x0055,0x00AA,0x0055,0x00AA,0x0055, /*   1 */
0xAA00,0x5500,0xAA00,0x5500,0xAA00,0x5500,0xAA00,0x5500,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,0x5555, /*   3 */
0xAA55,0x55AA,0xAA55,0x55AA,0xAA55,0x55AA,0xAA55,0x55AA,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, /*   5 */
};

u16 game[224];

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

#define gStageWidth 8
#define gStageHeight 7

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

	OBJ_UNKNOWN,
};

Object state[56];

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

int main(){
	int stage;
	wwc_set_color_mode(COLOR_MODE_4COLOR);
	wwc_palette_set_color(0, 0, 0x0000);	/* black */
	wwc_palette_set_color(0, 1, 0x000f);	/* blue */
	wwc_palette_set_color(0, 2, 0x0f00);	/* red */
	wwc_palette_set_color(0, 3, 0x0fff);	/* white */
	font_set_colordata(1, 6, nimotsu_chr);
	display_control(DCM_SCR2);
	for (stage=0; stage<2; stage++) {
		initialize( state, gStageData[stage] ); /* ステージ初期化 */
		/* メインループ */
		while ( true ){
			/* まず描画 */
			sys_wait(7);
			draw( state, gStageWidth, gStageHeight );
			/* クリアチェック */
			if ( checkClear(state, gStageWidth, gStageHeight ) ){
				break; /* クリアチェック */
			}
			/* 更新 */
			update( state, key_press_check(), gStageWidth, gStageHeight );
		}
	}
	return 0;
}

/* ---------------------以下関数定義------------------------------------------ */
void initialize( Object* state, 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 * gStageWidth + x ] = t; /* 書き込み */
			++x;
		}
	}
}

void draw(Object* state, int width, int height ){
        int x, y;
/* 	char font[] = {' ', '#', '.', 'o', 'O', 'p', 'P'}; */
	u16 font[] = {1, 4, 2, 3, 5, 6, 6}; /* Object列挙の順 */
	for ( y = 0; y < height; ++y ){
		for (x=0; x < width; ++x ){
			Object o = state[ y * width + x ];
			game[x * 2 + 0 + (y * 2 + 0) * gStageWidth * 2] = font[ o ];
			game[x * 2 + 1 + (y * 2 + 0) * gStageWidth * 2] = font[ o ];
			game[x * 2 + 0 + (y * 2 + 1) * gStageWidth * 2] = font[ o ];
			game[x * 2 + 1 + (y * 2 + 1) * gStageWidth * 2] = font[ o ];
		}
	}
	screen_set_char(SCREEN2, 0, 0, 16, 14, game);
}

/* 第一引数はほかの関数ではstateとしているが、あまりに頻繁に使うので
短いsで済ませている。w,hもそれぞれwidth,heightである。 */
void update( Object* s, u16 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 KEY_LEFT1: dx = -1; break;
		case KEY_RIGHT1: dx = 1; break;
		case KEY_UP1: dy = -1; break;
		case KEY_DOWN1: 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;
}

2017-12-04

NimotsuKun for MS-DOS その3

22:06

せっかくなので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;
}

2017-11-29

NimotsuKun for MS-DOS その2

21:49

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;
}

2017-11-27

NimotsuKun for MS-DOS

21:26

ゲームプログラマになる前に覚えておきたい技術

http://www.shuwasystem.co.jp/support/7980html/2118.html

サンプルプログラム荷物君をTurbo C++ 1.0.1でビルドしてMS-DOSで動かしてみます。

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

main.cppをnimotsu.cppにリネームして改修しました。

nimotsu.cpp

#include <iostream.h>
// using namespace std;
typedef int bool;
#define true 1
#define false 0

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


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

	OBJ_UNKNOWN,
};
const int gStageWidth = 8;
const int gStageHeight = 5;

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

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

(省略)

	//後始末
//	delete[] state;
	delete[ gStageWidth * gStageHeight ] state;
	state = 0;

	//Visual Studioから実行する人のために無限ループ。コマンドラインからはCtrl-Cで終えてください。
//	while( true ){
//		;
//	} 
	return 0;
}