読者です 読者をやめる 読者になる 読者になる

Cave。サンプルソース版。


一応こんなん書いてますよと。
本用なので VC2010 部分は後で取り除きます。
職場では VC2010 でチェックして、家では devkitPro でってなると
ソース分けるより #define 文で条件付コンパイルで書いて
それぞれのプロジェクトで同一ソースをビルドするのが自分的に楽だったので。


まあ自分以外の人もメンテするようなソースには避けとくべきな記述ですか。見辛いので。
条件付代入とか制御文を1行にまとめたりとかも慣れないと見辛いですが
製本考えるとコードの行数削減は必要なのでこれは見逃して欲しいかなと。
因みに最近の gcc だと #elif はエラー吐くので #ifdef 〜 #endif が2つ並んだりしてます。


コメントも多めです。
本用というのもありますが昔のソースを久しぶりにメンテしようかなと思ったときとか
コメント全くないと多少不便だということが理解できたので。
#define 多いのは改造してもらう用です。
コメント代わりのものも一部ありますが可読性を少しでも求めるなら必要悪かなと。
何でもかんでもは禁物ですが(昔の俺。)。


devkitPro - NDS

・ビルド手順
下記ソースを *.c ファイルに保存して、
devkitPro/examples/nds/templates/arm9 等のテンプレートプロジェクトを流用して
./source/main.c を *.c と差し替えてビルド。


Visual C++ 2010 - Win32 コンソール版

・ビルド手順
下記ソースを *.c ファイルに保存して、
/* コンパイラ_ターゲットの選択設定 */ の部分にある
//#define VC2010_WIN のコメントアウトを除いて、
#define DEVKITPRO_NDSコメントアウト
ファイル-新規作成-プロジェクト-Win32 コンソール アプリケーション で空のプロジェクト作って
ソース ファイル-追加-既存の項目 で *.c を追加してビルド。

/*---------------------------------------------------------------------------------
	
	sample program (Cave Game)
	
	version 0.01
	Nov 19, 2011
	
	By REGEKATU
	
---------------------------------------------------------------------------------*/

/* コンパイラ_ターゲットの選択設定 */
//#define	VC2010_WIN
#define	DEVKITPRO_NDS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef	VC2010_WIN
#include <Windows.h>
#endif
#ifdef	DEVKITPRO_NDS
#include <nds.h>
#endif


#ifndef bool
#define bool unsigned char
#endif

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif


#ifdef	VC2010_WIN
#define CONSOLE_WIDTH  80							/* 表示環境の横表示可能文字数*/
#define CONSOLE_HEIGHT 25							/* 表示環境の縦表示可能文字数*/
#endif
#ifdef	DEVKITPRO_NDS
#define CONSOLE_WIDTH  32							/* 表示環境の横表示可能文字数*/
#define CONSOLE_HEIGHT 24							/* 表示環境の縦表示可能文字数*/
#endif


#define CAVE_WIDTH  32								/* 洞窟横幅 */
#define CAVE_HEIGHT 24								/* 洞窟縦幅 */

#define WAIT_FRAME 12								/* ゲーム更新待ち間隔 */

#define DEPTH_X 0									/* 深度表示横位置 */
#define DEPTH_Y 0									/* 深度表示縦位置 */

#define DEPTH_DEF 0									/* 深度初期値 */

#define PLAYER_X 16									/* プレイヤー横初期位置 */
#define PLAYER_Y 3									/* プレイヤー縦初期位置 */

#define WALL_LEFT 10								/* 空洞左壁初期位置 */

#define SIZE_FIRST 12								/* 空洞幅初期値 */
#define SIZE_LAST  2								/* 空洞幅末期値 */
#define SIZE_NEXT  30								/* 空洞幅更新深度 */

#define MOVE_MIN 0									/* 洞窟左移動上限値 */
#define MOVE_MAX (CAVE_WIDTH - 1)					/* 洞窟右移動上限値 */

#define MOVE_LEFT  (-1)								/* 空洞左移動量 */
#define MOVE_RIGHT 1								/* 空洞右移動量 */

#define CHAR_HOLLOW ' '								/* 空洞画像 */
#define CHAR_WALL   '#'								/* 壁画像 */
#define CHAR_PLAYER 'V'								/* プレイヤー画像 */
#define CHAR_CRASH  '*'								/* プレイヤー衝突画像 */

typedef enum{										/* 状態遷移定数 */
	INIT = 0, 										/* ゲーム初期化 */
	TITLE, 											/* タイトル表示 */
	READY, 											/* ゲームプレイ待ち */
	PLAY, 											/* ゲームプレイ */
	MISS, 											/* ゲームオーバー */
	END												/* ゲーム終了 */
}ACT;


int act;											/* 状態遷移変数 */
int wait;											/* ゲーム更新待ち間隔用変数 */

int depth;											/* 洞窟深度 */

int player_x;										/* プレイヤー横位置 */
int player_y;										/* プレイヤー縦位置 */
bool player_crash;									/* プレイヤー衝突判定 */

int wall_left;										/* 空洞左壁位置 */
int size;											/* 空洞幅 */

int cave[CAVE_WIDTH * CAVE_HEIGHT];					/* 洞窟情報用配列 */

#ifdef	VC2010_WIN
HANDLE hOut;										/* コンソールスクリーンバッファのハンドル */
COORD dwPos;										/* コンソールスクリーンカーソル指定用構造体 */
CONSOLE_SCREEN_BUFFER_INFO csbi;					/* コンソールスクリーンバッファ情報の構造体 */
DWORD written;										/* 書き込まれたセル数へのポインタ */
#endif


int main(void);										/* メインプログラム */

void init(void);									/* ゲームを初期化する */
void title(void);									/* タイトルを表示する */
void ready(void);									/* ゲームプレイ待ちをする */
void play(void);									/* ゲームプレイを実行する */
void miss(void);									/* ゲームオーバーを実行する */
void end(void);										/* ゲーム終了と再会待ちをする */

void playDraw(void);								/* ゲームプレイ画面を表示する */
void playCave(void);								/* 洞窟情報を更新する */
void playPlayer(void);								/* プレイヤー情報を更新する */

void setLocate(int x, int y);						/* 文字の表示位置を設定する */
void clearScreen(void);								/* コンソール画面を消去する */


//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	
	/* メインプログラム */
	
	/* コンソールを初期化する */
#ifdef	VC2010_WIN
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
#ifdef	DEVKITPRO_NDS
	consoleDemoInit();
#endif
	
	/* 乱数を現在の時間で初期化する */
	srand((unsigned int)time(NULL));
	
	/* ゲームループ */
	while(1){
		
		/* ゲームを一定間隔で更新する */
#ifdef	VC2010_WIN
		Sleep(16);
#endif
#ifdef	DEVKITPRO_NDS
		swiWaitForVBlank();
#endif
		
#ifdef DEVKITPRO_NDS
		/* キー情報を更新する */
		scanKeys();
#endif
		
		/* ゲームメインループ */
		switch(act){
			
		/* ゲームを初期化する */
		case INIT:
			init();
			break;
			
		/* タイトルを表示する */
		case TITLE:
			title();
			break;
			
		/* ゲームプレイ待ちをする */
		case READY:
			ready();
			break;
			
		/* ゲームプレイを実行する */
		case PLAY:
			play();
			break;
			
		/* ゲームオーバーを実行する */
		case MISS:
			miss();
			break;
			
		/* ゲーム終了と再会待ちをする */
		case END:
			end();
			break;
			
		}
		
	}
	
}

//---------------------------------------------------------------------------------
void init(void) {
//---------------------------------------------------------------------------------
	
	/* ゲームを初期化する */
	
	/* 画面を消去する */
	clearScreen();
	
	/* 洞窟情報を初期化する */
	memset(&cave, CHAR_HOLLOW, sizeof(cave));
	
	/* ゲーム初期値を設定する */
	wait         = WAIT_FRAME;
	depth        = DEPTH_DEF;
	player_x     = PLAYER_X;
	player_y     = PLAYER_Y;
	player_crash = false;
	wall_left    = WALL_LEFT;
	size         = SIZE_FIRST;
	
	/* ゲーム初期化が終わったらタイトル表示に進む */
	act = TITLE;
	
}

//---------------------------------------------------------------------------------
void title(void){
//---------------------------------------------------------------------------------
	
	/* タイトルを表示する */
	
	/* タイトル画面を表示してゲームプレイ待ちに進む */
	
	setLocate(10, 7);
	printf("C A V E");
	
	setLocate(8, 9);
#ifdef VC2010_WIN
	printf("push space key");
#endif
#ifdef DEVKITPRO_NDS
	printf("push start key");
#endif
	
	act = READY;
	
}

//---------------------------------------------------------------------------------
void ready(void){
//---------------------------------------------------------------------------------
	
	/* ゲームプレイ待ちをする */
	
#ifdef VC2010_WIN
	/* スペースキーが押されたらタイトル画面を消去してゲームプレイに進む */
	if(GetKeyState(VK_SPACE) < 0){
#endif
#ifdef DEVKITPRO_NDS
	/* スタートキーが押されたらタイトル画面を消去してゲームプレイに進む */
	if(keysDown() & KEY_START){
#endif
		clearScreen();
		act = PLAY;
	}
	
}

//---------------------------------------------------------------------------------
void play(void){
//---------------------------------------------------------------------------------
	
	/* ゲームプレイを実行する */
	
	/* ゲーム更新待ち間隔に従ってゲームプレイ速度を調整する */
	if(wait-- != 0) return;
	wait = WAIT_FRAME;
	
	/* ゲームプレイ画面を表示する */
	playDraw();
	
	/* プレイヤーが壁に衝突してたらゲームオーバーに進む */
	if(player_crash == true){
		act = MISS;
		return;
	}
	
	/* 洞窟情報を更新する */
	playCave();
	
	/* プレイヤー情報を更新する */
	playPlayer();
	
}

//---------------------------------------------------------------------------------
void miss(void){
//---------------------------------------------------------------------------------
	
	/* ゲームオーバーを実行する */
	
	/* ゲームオーバーを表示して、ゲーム終了に進む */
	setLocate(10, 7);
	printf("GAME OVER");
	act = END;
	
}

//---------------------------------------------------------------------------------
void end(void){
//---------------------------------------------------------------------------------
	
	/* ゲーム終了と再会待ちをする */
	
#ifdef VC2010_WIN
	/* スペースキーが押されたらゲーム初期化に戻る */
	if(GetKeyState(VK_SPACE) < 0) act = INIT;
#endif
#ifdef DEVKITPRO_NDS
	/* スタートキーが押されたらゲーム初期化に戻る */
	if(keysDown() & KEY_START) act = INIT;
#endif
	
}

//---------------------------------------------------------------------------------
void playDraw(void){
//---------------------------------------------------------------------------------
	
	/* ゲームプレイ画面を表示する */
	
	int x, y;
	
	/* 洞窟を表示する */
	setLocate(0, 0);
	for(y = 0;y < CAVE_HEIGHT;y++){
		for(x = 0;x < CAVE_WIDTH;x++)
			printf("%c", cave[x + y * CAVE_WIDTH]);
		
		if(x < CONSOLE_WIDTH) printf("\n");
	}
	
	/* 洞窟深度を表示する */
	setLocate(DEPTH_X, DEPTH_Y);
	printf("DEPTH %d", depth);
	
}

//---------------------------------------------------------------------------------
void playCave(void){
//---------------------------------------------------------------------------------
	
	/* 洞窟情報を更新する */
	
	int i, rnd;
	
	/* 設定深度毎に空洞幅を更新する */
	if(depth != 0 && depth % SIZE_NEXT == 0){
		if(SIZE_FIRST > SIZE_LAST && size > SIZE_LAST) size--;
		else if(SIZE_FIRST < SIZE_LAST && size < SIZE_LAST) size++;
	}
	
	/* 空洞を右または左に動かす */
	if( (rnd = rand() % 2) == MOVE_RIGHT ){
		if(wall_left + 1 + size < MOVE_MAX ) wall_left += MOVE_RIGHT;
	}else{
		if(wall_left > MOVE_MIN) wall_left += MOVE_LEFT;
	}
	
	/* 洞窟深部を進める */
	for(i = CAVE_WIDTH;i < CAVE_WIDTH * CAVE_HEIGHT;i++)
		cave[ i - CAVE_WIDTH ] = cave[i];
	
	/* 洞窟最深部を更新する */
	for(i = CAVE_WIDTH * CAVE_HEIGHT - CAVE_WIDTH;i < CAVE_WIDTH * CAVE_HEIGHT;i++)
		cave[i] = ( (i - (CAVE_WIDTH * CAVE_HEIGHT - CAVE_WIDTH) > wall_left) && 
					(i - (CAVE_WIDTH * CAVE_HEIGHT - CAVE_WIDTH) <= wall_left + size) ) ? CHAR_HOLLOW : CHAR_WALL;
	
	/* 洞窟深度を更新する */
	++depth;
	
}

//---------------------------------------------------------------------------------
void playPlayer(void){
//---------------------------------------------------------------------------------
	
	/* プレイヤー情報を更新する */
	
	/* プレイヤーの軌跡を表示する */
	cave[ player_x + (player_y - 1) * CAVE_WIDTH ] = '.';
	
	/* プレイヤーを左右に操作する */
#ifdef VC2010_WIN
	if(GetKeyState(VK_LEFT) < 0){ if(player_x > MOVE_MIN) player_x--; }
	else if(GetKeyState(VK_RIGHT) < 0){ if(player_x < MOVE_MAX) player_x++; }
#endif
#ifdef	DEVKITPRO_NDS
	if(keysCurrent() & KEY_LEFT) { if(player_x > MOVE_MIN) player_x--; }
	else if(keysCurrent() & KEY_RIGHT) { if(player_x < MOVE_MAX) player_x++; }
#endif
	
	/* プレイヤーと壁との衝突を判定する */
	if(cave[ player_x + player_y * CAVE_WIDTH ] == CHAR_WALL) player_crash = true;
	
	/* プレイヤーを表示する */
	cave[ player_x + player_y * CAVE_WIDTH ] = (player_crash == false) ? CHAR_PLAYER : CHAR_CRASH;
	
}

//---------------------------------------------------------------------------------
void setLocate(int x, int y) {
//---------------------------------------------------------------------------------
	
	/* 文字の表示位置を設定する */
	
#ifdef	VC2010_WIN
	dwPos.X = (short)x;
	dwPos.Y = (short)y;
	SetConsoleCursorPosition(hOut, dwPos);
#else
	printf("\x01b[%d;%dH", y, x);
#endif
	
}

//---------------------------------------------------------------------------------
void clearScreen(void) {
//---------------------------------------------------------------------------------
	
	/* コンソール画面を消去する */
	
#ifdef	VC2010_WIN
	dwPos.X = 0;
	dwPos.Y = 0;
	if(GetConsoleScreenBufferInfo(hOut, &csbi))
		FillConsoleOutputCharacter(hOut, ' ', 
			csbi.dwSize.X * csbi.dwSize.Y, dwPos, &written);
#else
	printf("\x1b[2J");
#endif
	
}

//---------------------------------------------------------------------------------