「15パズルライブラリ」ver0.10


マインスイーパを組むにあたり、旧作クラスライブラリを流用しようとソース眺めてたら、
色々気になりリファクタリングしてしまったのでバージョンアップ版を公開。
サンプル(main.c)は DS 用ですが C 標準ライブラリのみで書いてるので多機種流用とか出来ると思います
(方言は各自弄って対応の方向で…。)。


ソース、バイナリ一式はこちらより。


15パズルライブラリ: レトロゲーム活用研究同好会(レゲ活)
http://regekatsu.seesaa.net/article/353916533.html


Sbp(SlidingBlockPuzzle) library sample


Sbp.h

/*---------------------------------------------------------------------------------
	
	Sbp(SlidingBlockPuzzle) library header
	
	version 0.10
	Apr 03, 2013
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#ifndef _SBP_H_
#define _SBP_H_


//NULL 値、bool 型の定義。
#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

#ifndef bool
#define bool unsigned char
#endif

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif


//オブジェクト(構造体ポインタ)の定義。
typedef struct _Sbp* Sbp;


#ifdef __cplusplus
extern "C" {
#endif


//スライドブロックオブジェクトを生成します。引数にはサイズ(幅、高さ)を指定します。
Sbp SbpCreate(int width, int height);

//スライドブロックオブジェクトを破棄します。
Sbp SbpDestroy(Sbp sbp);

//スライドブロックが全て揃っているか確認します。全て揃ってれば true を、そうでなければ false を返します。
bool SbpIsComplete(Sbp sbp);

//指定された座標のスライドブロック番号を取得します。
int SbpGetNumber(Sbp sbp, int width, int height);

//指定された座標にスライドブロック番号を設定します。元あった番号は設定された番号のあった座標に移ります。
void SbpSetNumber(Sbp sbp, int width, int height, int number);

//スライドブロック領域の幅を取得します。
int SbpGetWidth(Sbp sbp);

//スライドブロック領域の高さを取得します。
int SbpGetHeight(Sbp sbp);

//スライドブロックの総数を取得します。
int SbpGetMax(Sbp sbp);

//ブランクに設定してるスライドブロック番号を取得します。
int SbpGetBlank(Sbp sbp);

//ブランクに設定してるスライドブロック番号を変更します。
void SbpSetBlank(Sbp sbp, int blank);

//スライドブロックをシャッフルします。
void SbpShuffle(Sbp sbp, int seed, int shuffleCount);

//スライドブロックを左へスライドします。成功したら 1 を、そうでなければ 0 を返します。
int SbpSlideLeft(Sbp sbp);
//スライドブロックを左へスライドします。成功したら 1 を、そうでなければ 0 を返します。
int SbpSlideRight(Sbp sbp);
//スライドブロックを上へスライドします。成功したら 1 を、そうでなければ 0 を返します。
int SbpSlideUp(Sbp sbp);
//スライドブロックを下へスライドします。成功したら 1 を、そうでなければ 0 を返します。
int SbpSlideDown(Sbp sbp);


#ifdef __cplusplus
}
#endif

#endif	// _SBP_H_


Sbp.c

/*---------------------------------------------------------------------------------
	
	Sbp(SlidingBlockPuzzle) library routine
	
	version 0.10
	Apr 03, 2013
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Sbp.h"


//実行時エラーを発生させて、処理を中断してエラー情報を表示します。
#define RUN_TIME_ERROR(e)	printf("Error!\nFile: \n%s\n\nLine: %d\n\nFunction: \n%s\n\nCondition: \n"#e"\n", __FILE__, __LINE__, __FUNCTION__); while(1) {}


//オブジェクト(構造体実態)の定義。
typedef struct _Sbp {
	
	//スライドブロック領域。
	int* block;
	
	//スライドブロック領域の幅。
	int width;
	//スライドブロック領域の高さ。
	int height;
	
	//スライドブロックの総数。
	int max;
	//ブランクに設定してるスライドブロック番号。
	int blank;
	
} _Sbp;


//スライドブロック A とスライドブロック B を交換します。
void SbpSwap(int* a, int* b);


//---------------------------------------------------------------------------------
//スライドブロックオブジェクトを生成します。引数にはサイズ(幅、高さ)を指定します。
//---------------------------------------------------------------------------------
Sbp SbpCreate(int width, int height) {
//---------------------------------------------------------------------------------
	
	int i;
	Sbp sbp;
	
	assert((width > 0) && (height > 0));
	
	//オブジェクト領域(構造体ポインタ)を確保する。
	sbp = (Sbp)malloc(sizeof(_Sbp));
	
	//幅、高さ、総数、ブランク(一番最後の番号)を設定する。
	sbp->width = width;
	sbp->height = height;
	sbp->max = sbp->width * sbp->height;
	sbp->blank = sbp->max - 1;
	
	//スライドブロック領域を確保する。
	sbp->block = malloc(sizeof(int) * sbp->max);
	
	//スライドブロック番号と配列の添え字とが等しい値になるように設定する。
	for(i = 0;i < sbp->max;i++) {
		sbp->block[i] = i;
	}
	
	//オブジェクト領域を返す。
	return sbp;
	
}

//---------------------------------------------------------------------------------
//スライドブロックオブジェクトを破棄します。
//---------------------------------------------------------------------------------
Sbp SbpDestroy(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	
	//スライドブロック領域、オブジェクト領域(構造体ポインタ)を解放する。
	free(sbp->block);
	free(sbp);
	
	return NULL;
	
}

//---------------------------------------------------------------------------------
//スライドブロックが全て揃っているか確認します。全て揃ってれば true を、そうでなければ false を返します。
//---------------------------------------------------------------------------------
bool SbpIsComplete(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	int i;
	
	assert(sbp != NULL);
	
	//スライドブロック番号と配列の添え字とが全て揃っれば true を、そうでなければ false を返す。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] != i) {
			return false;
		}
	}
	return true;
	
}

//---------------------------------------------------------------------------------
//指定された座標のスライドブロック番号を取得します。
//---------------------------------------------------------------------------------
int SbpGetNumber(Sbp sbp, int width, int height) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	assert((width >= 0 && width < sbp->width) || (height >= 0 && height < sbp->height));
	
	return sbp->block[width + (sbp->width * height)];
	
}

//---------------------------------------------------------------------------------
//指定された座標にスライドブロック番号を設定します。元あった番号は設定された番号のあった座標に移ります。
//---------------------------------------------------------------------------------
void SbpSetNumber(Sbp sbp, int width, int height, int number) {
//---------------------------------------------------------------------------------
	
	int i;
	
	assert(sbp != NULL);
	assert((width >= 0 && width < sbp->width) || (height >= 0 && height < sbp->height));
	assert(number >= 0 && number < sbp->max);
	
	//指定されたスライドブロック番号の現在座標を取得する。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] == number) {
			break;
		}
	}
	
	//指定座標の元番号と指定番号があった元座標同士の、スライドブロック番号を交換する。
	SbpSwap(&sbp->block[width + (sbp->width * height)], &sbp->block[i]);
	
}

//---------------------------------------------------------------------------------
//スライドブロック領域の幅を取得します。
//---------------------------------------------------------------------------------
int SbpGetWidth(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	
	return sbp->width;
	
}

//---------------------------------------------------------------------------------
//スライドブロック領域の高さを取得します。
//---------------------------------------------------------------------------------
int SbpGetHeight(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	
	return sbp->height;
	
}

//---------------------------------------------------------------------------------
//スライドブロックの総数を取得します。
//---------------------------------------------------------------------------------
int SbpGetMax(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	
	return sbp->max;
	
}
//---------------------------------------------------------------------------------
//ブランクに設定してるスライドブロック番号を取得します。
//---------------------------------------------------------------------------------
int SbpGetBlank(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	
	return sbp->blank;
	
}

//---------------------------------------------------------------------------------
//ブランクに設定してるスライドブロック番号を変更します。
//---------------------------------------------------------------------------------
void SbpSetBlank(Sbp sbp, int blank) {
//---------------------------------------------------------------------------------
	
	assert(sbp != NULL);
	assert(blank > 0 && blank < sbp->max);
	
	sbp->blank = blank;
	
}

//---------------------------------------------------------------------------------
//スライドブロックをシャッフルします。
//---------------------------------------------------------------------------------
void SbpShuffle(Sbp sbp, int seed, int shuffleCount) {
//---------------------------------------------------------------------------------
	
	int i, random;
	
	assert(sbp != NULL);
	
	//指定されたシード値で乱数を初期化する。
	srand(seed);
	
	//設定された回数だけスライドブロックをシャッフルする。
	for(i = 0;i < shuffleCount;i++) {
		
		//0 〜 3 の間で乱数を生成する。
		random = rand() % 4;
		
		switch(random) {
			
		case 0:
			//スライドブロックを右に移動する。
			SbpSlideLeft(sbp);
			break;
			
		case 1:
			//スライドブロックを左に移動する。
			SbpSlideRight(sbp);
			break;
			
		case 2:
			//スライドブロックを上に移動する。
			SbpSlideUp(sbp);
			break;
			
		case 3:
			//スライドブロックを下に移動する。
			SbpSlideDown(sbp);
			break;
			
		default:
			//想定外の値が含まれていた場合、実行時エラーを発生させる。
			RUN_TIME_ERROR(random);
			break;
			
		}
		
	}
	
}

//---------------------------------------------------------------------------------
//スライドブロックを左へスライドします。成功したら 1 を、そうでなければ 0 を返します。
//---------------------------------------------------------------------------------
int SbpSlideLeft(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	int y, i;
	
	assert(sbp != NULL);
	
	//右にスライド可能か調べる(左端が空白ならスライド不可)。
	for(y = 0;y < sbp->height;y++) {
		if(sbp->block[sbp->width * y + sbp->width - 1] == sbp->blank) {
			return 0;
		}
	}
	
	//スライド可能ならスライド先インデックスを調べる。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] == sbp->blank) {
			break;
		}
	}
	
	//スライドブロックを交換する。
	SbpSwap(&sbp->block[i], &sbp->block[i + 1]);
	return 1;
	
}

//---------------------------------------------------------------------------------
//スライドブロックを左へスライドします。成功したら 1 を、そうでなければ 0 を返します。
//---------------------------------------------------------------------------------
int SbpSlideRight(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	int y, i;
	
	assert(sbp != NULL);
	
	//左にスライド可能か調べる(右端が空白ならスライド不可)。
	for(y = 0;y < sbp->height;y++) {
		if(sbp->block[sbp->width * y ] == sbp->blank) {
			return 0;
		}
	}
	
	//スライド可能ならスライド先インデックスを調べる。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] == sbp->blank) {
			break;
		}
	}
	
	//スライドブロックを交換する。
	SbpSwap(&sbp->block[i - 1], &sbp->block[i]);
	return 1;
	
}

//---------------------------------------------------------------------------------
//スライドブロックを上へスライドします。成功したら 1 を、そうでなければ 0 を返します。
//---------------------------------------------------------------------------------
int SbpSlideUp(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	int x, i;
	
	assert(sbp != NULL);
	
	//上にスライド可能か調べる(下端が空白ならスライド不可)。
	for(x = 0;x < sbp->width;x++) {
		if(sbp->block[sbp->width * (sbp->height - 1) + x] == sbp->blank) {
			return 0;
		}
	}
	
	//スライド可能ならスライド先インデックスを調べる。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] == sbp->blank) {
			break;
		}
	}
	
	//スライドブロックを交換する。
	SbpSwap(&sbp->block[i], &sbp->block[i + sbp->width]);
	return 1;
	
}

//---------------------------------------------------------------------------------
//スライドブロックを下へスライドします。成功したら 1 を、そうでなければ 0 を返します。
//---------------------------------------------------------------------------------
int SbpSlideDown(Sbp sbp) {
//---------------------------------------------------------------------------------
	
	int x, i;
	
	assert(sbp != NULL);
	
	//下にスライド可能か調べる(上端が空白ならスライド不可)。
	for(x = 0;x < sbp->width;x++) {
		if(sbp->block[x] == sbp->blank) {
			return 0;
		}
	}
	
	//スライド可能ならスライド先インデックスを調べる。
	for(i = 0;i < sbp->max;i++) {
		if(sbp->block[i] == sbp->blank) {
			break;
		}
	}
	
	//スライドブロックを交換する。
	SbpSwap(&sbp->block[i - sbp->width], &sbp->block[i]);
	return 1;
	
}

//---------------------------------------------------------------------------------
//スライドブロック A とスライドブロック B を交換します。
//---------------------------------------------------------------------------------
void SbpSwap(int* a, int* b) {
//---------------------------------------------------------------------------------
	
	int c;
	
	c = *a;
	*a = *b;
	*b = c;
	
}

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


main.c

/*---------------------------------------------------------------------------------
	
	Sbp(SlidingBlockPuzzle) library sample
	
	version 0.10
	Apr 03, 2013
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

//#define NDEBUG

#include <stdio.h>
#include <time.h>
#include <nds.h>
//スライドブロックライブラリのヘッダをインクルードする。
#include "Sbp.h"


//スライドブロック領域の幅。
#define WIDTH 4
//スライドブロック領域の高さ。
#define HEIGHT 4
//シャッフルする回数。
#define SHUFFLE_COUNT 100


//スライドオブジェクト変数を宣言する。
Sbp sbp;


//ゲーム画面を表示します。
void draw(void);


//---------------------------------------------------------------------------------
//メインプログラム。
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	
	//コンソールを初期化する。
	consoleDemoInit();
	//タイトル、バージョン、公開日、コピーライトを表示する。
	printf("Sbp(SlidingBlockPuzzle)\n library sample\n\nversion 0.10\nApr 03, 2013\n\nBy REGEKATSU\n");
	
	//スライドブロック領域の幅、高さを指定してオブジェクトを生成する。
	sbp = SbpCreate(WIDTH, HEIGHT);
	
	//乱数初期化値を現在の時間として、スライドブロックをシャッフルする。
	SbpShuffle(sbp, (unsigned int)time(NULL), SHUFFLE_COUNT);
	
	//ゲームループ。
	while(1) {
		
		//ゲームを一定間隔(1 / 60 秒)で更新する。
		swiWaitForVBlank();
		//キー入力を取得する。
		scanKeys();
		
		//ゲーム画面を表示する。
		draw();
		
		//スライドブロックを左にスライドする。
		if(keysDown() & KEY_LEFT) {
			SbpSlideLeft(sbp);
			
		//スライドブロックを右にスライドする。
		} else if(keysDown() & KEY_RIGHT) {
			SbpSlideRight(sbp);
			
		//スライドブロックを上にスライドする。
		} else if(keysDown() & KEY_UP) {
			SbpSlideUp(sbp);
			
		//スライドブロックを下にスライドする。
		} else if(keysDown() & KEY_DOWN) {
			SbpSlideDown(sbp);
			
		}
		
		//クリア判定。
		//スライドブロックが全て揃っていたらゲームループを抜ける。
		if(SbpIsComplete(sbp)) {
			break;
		}
		
	}
	
	//ゲーム画面と、ゲームクリアのメッセージを表示する。
	draw();
	printf("\x01b[17;0H...CLEAR!...");
	
	//何か入力があるまで待つ。
	while(!keysCurrent()) {
	};
	
	//スライドブロックオブジェクトを破棄して、ゲームを終了する。
	sbp = SbpDestroy(sbp);
	return 0;
	
}

//---------------------------------------------------------------------------------
//ゲーム画面を表示します。
//---------------------------------------------------------------------------------
void draw(void) {
//---------------------------------------------------------------------------------
	
	int x, y;
	
	//表示開始行を設定する。
	printf("\x01b[10;0H");
	
	//スライドブロック領域の幅、高さに合わせて表示する。
	for(y = 0;y < HEIGHT;y++) {
		for(x = 0;x < WIDTH;x++) {
			//スライドブロック番号がブランクに設定されてたら空白を表示する。
			if(SbpGetNumber(sbp, x, y) == SbpGetBlank(sbp)) {
				printf("  ");
			//それ以外のブロック番号なら番号を 16 進数で表示する。
			} else {
				printf("%2X", SbpGetNumber(sbp, x, y));
			}
		}
		printf("\n");
	}
	
}

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