CUIでテトリスを作ってみました。

kerotan0820
記事: 91
登録日時: 14年前

CUIでテトリスを作ってみました。

投稿記事 by kerotan0820 » 11年前

いくつか掲示板で力をお借りしましたが、なるべくウェブサイトなどは見ずに作ってみました。

http://u7.getuploader.com/kerotan/downl ... TRIS01.exe

構造体の使い方をまた忘れてしまったので本を読みながら無理やり使ってみました。
ブロックのデータなどはグローバル変数で宣言するのは好ましいのでしょうかね…。
かといってmain関数にブロックデータを全て書き込んだらきたないし…。
他の方のプログラムをいくつか見ていたら、tetris.h なんていうヘッダーを作っている方が何人かいた。

あと悩んでいるのが処理速度が不安定ということ。
キーボードの入力を受付けている間はタイマーがカウントされず、ブロックも落下しない…。
コレに関してはどうすればよいか想像もつきません。

高校生の時に先生に頂いた「ネコでも分かるゲームプログラミング」なんていう本が手元にあるので、WINAPIを勉強してみようかなと思っています。
やっと勉強してみようと思えるようになりました。

CODE:

/***** ヘッダー ****/
#include
#include	//exit(),rand()
#include	//time()
#include	//getch(),kbhit()
#include	//Sleep()

/**** 定数 ****/
#define HIGHT 20
#define WIDTH 15
#define SUMBLOCKS 7

/**** プロトタイプ宣言 ****/
void Draw_map(void);				//ステージ(メイン)の出力
void Draw_block(int b);				//空白かブロックの出力
void Check_map();					//ブロックの固定、新規ブロックの取得
int  Check_wall(int dir);					//ブロック移動の制限
int  Check_roll(int dir);			//回転時の壁判定
void Check_line();					//ラインが揃ってるか調べる
void Del_line(int line);			//ラインを一行削除
void Sort_block(int line);			//消されたラインを詰める
void Set_block(int mode);			//回転用ブロックのセット
void Move_block(int dir);			//ブロックの移動
void Turn_block(int dir);			//ブロックの回転
void Hold_block();					//ブロックの固定
void Output_test(int type);
void Key_inp();
void Timer_funk();
void Title();						//タイトルの表示
void Game_over();					//ゲームオーバー画面

struct BLOCKS{				//ブロックデータ
	int block[4][4];		//最大4 * 4ドット
	int block_size;			//サイズ
};
struct BLOCKS block_data[SUMBLOCKS] =
{
	{//四角		//ブロック0
		{
			{ 1, 1 },
			{ 1, 1 }
		}, 2
	},
	{//とつ		//ブロック1
		{
			{ 0, 1, 0 },
			{ 1, 1, 1 },
			{ 0, 0, 0 }
		}, 3
	},
	{//エル		//ブロック2
		{
			{ 1, 0, 0 },
			{ 1, 1, 1 },
			{ 0, 0, 0 }
		}, 3
	},
		{//逆エル	//ブロック3
		{
			{ 0, 0, 1 },
			{ 1, 1, 1 },
			{ 0, 0, 0 }
		}, 3
	},
		{//エヌ		//ブロック4
		{
			{ 0, 1, 0 },
			{ 1, 1, 0 },
			{ 1, 0, 0 }
		}, 3
	},
		{//逆エヌ	//ブロック5
		{
			{ 0, 1, 0 },
			{ 0, 1, 1 },
			{ 0, 0, 1 }
		}, 3
	},
		{//横棒		//ブロック6
		{
			{ 0, 1, 0, 0 },
			{ 0, 1, 0, 0 },
			{ 0, 1, 0, 0 },
			{ 0, 1, 0, 0 }
		}, 4
	},
};
struct BLOCKS block_data2[SUMBLOCKS];	//回転処理後のデータ

struct STAGE{
	int stage[20][15];	//ステージ全体 20*15
	int x;				//ブロックの現在位置 X	
	int y;				//ブロックの現在位置 Y
	int direction;		//回転位置
	int ground;			//地面に付いているかいないか
	int b_type;			//落下中ブロックのタイプ
};
struct STAGE st =		//マップデータ
{
	{
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
	}, 5, 0, 0, 0, 3
};

struct SCORE {
	int score;
	int delline;
	int turnnum;
};

struct SCORE score_data = {
	200,	//総合得点
	0,		//消したライン数
	0		//回転した回数
};

int GAMEOVER = 0;

/***********************************************************/
/************************* MAIN関数 ************************/
/***********************************************************/

int main(void)
{
	int c=0;
	Set_block(8);
	Title();	//タイトル
	printf("[S]Key:Start!\n");
	while (1){
		if (_kbhit() != 0){
			c = _getch();
			if (c == 115){				//S_key
				printf("Start!!!\n");
				break;
			}
		}
	}
	system("cls");
	Timer_funk();
	system("cls");
	Game_over();
	system("pause");
	return 0;
}

/**************** タイマー *******************/
void Timer_funk()
{
	int time=0;
	time = 0;
	while (GAMEOVER != 1){
		for (time = 0; time = st.y && i = st.x && j =15 && i != 19) {
				Del_line(i);	//1行すべてが揃っていたら
				Sort_block(i);	//ブロックを詰める
				score_data.score += 100;	//スコアを+100
				score_data.delline += 1;	//消したライン数を+1
			}
		}
		printf("\n");
		sum = 0;

	}
//	printf("X=%02d Y=%02d\n", st.x, st.y);
	printf("DEL LINE: %d\n", score_data.delline);
	printf("TURN BLOCK: %d\n", score_data.turnnum);
	printf("TOTAL SCORE: %d", score_data.score);
}

/*************** 削除 *******************/
//呼び出し元:マップ描画
void Del_line(int line)	
{
	int i;
	for(i = 1; i  0; i--){
		for( j = 1; j = 1) printf("■");
	else printf(" ");
}

/********** 回転用ブロックのセット ***********●*/

void Set_block(int mode)	//mode:8 全初期化 mode:0~6 指定初期化
{	//初期ブロックデータを回転用データにセット
	int i, j, k, size;
	if (mode == 8){	//全初期化
		for (i = 0; i = 0 && mode > 100 × %d = +%d point\n",score_data.delline, score_data.delline, 100 * score_data.delline);
	printf("TURN BLOCK: %d >> -10 × %d = -%d point\n", score_data.turnnum, score_data.turnnum, 10 * score_data.turnnum);
	printf("TOTAL: %d point\n", score_data.score);
}

/******* ブロック単体 出力テスト用 *********/
void Output_test(int type)
{
	int i, j;
	int size;
	size = block_data[type].block_size;
	for (i = 0; i < size; i++){
		for (j = 0; j < size; j++){
			if (st.direction % 2 == 0){
				Draw_block(block_data2[type].block[i][j]);
			}
			else{
				Draw_block(block_data[type].block[i][j]);
			}
		}
		printf("\n");
	}printf("\n");
}
最後に編集したユーザー kerotan0820 on 2013年12月04日(水) 02:17 [ 編集 2 回目 ]

アバター
みけCAT
記事: 6734
登録日時: 14年前

Re: CUIでテトリスを作ってみました。

投稿記事 by みけCAT » 11年前

試してみました。

●プレイした感想
 ・テトリミノの落下速度が最初から速すぎる。全く横移動できずに下まで行くことも。
 ・ソースコード以外に「S:Start」以外の操作説明が見当たらない。
 ・ブロックを回転させると減点されるという説明も、プレイ開始前に無い。
 ・最初に200点持っているという説明もないため、結果の解釈がわかりにくい。
 ・結果画面のTURN BLOCKの計算が合わない。例えば+0-20のとき、TOTAL: 160 pointになっている。

●ソースコードを読んだ感想
 ・せっかくHIGHTとWIDTHを定義しているのに、91行目の要素数や220行目の判定にマジックナンバーを使用しているのが気になる。
 ・実際はソートではなく落とすだけなのに関数名がSort_blockなのはわかりにくい。
 ・342行目の条件式はst.y<0 && st.x<0でないと成立しないはず。そのような状況はある?
 ・Check_roll関数で当たっていない時およびdir!=1の時は不定の値が返る。
 ・Check_wall関数も移動不可能でない時に不定の値が返る。
 ・298行目及び316行目の条件文は(デバッガなどで変数の値を外部から書き換えない限り)絶対に成立しないはず。
 ・374行目で毎回srandする必要はない。むしろするべきではない。

とりあえず気付いたのはこのくらいです。

kerotan0820
記事: 91
登録日時: 14年前

Re: CUIでテトリスを作ってみました。

投稿記事 by kerotan0820 » 11年前

みけCAT さんが書きました:試してみました。

●プレイした感想
 ・テトリミノの落下速度が最初から速すぎる。全く横移動できずに下まで行くことも。
 ・ソースコード以外に「S:Start」以外の操作説明が見当たらない。
 ・ブロックを回転させると減点されるという説明も、プレイ開始前に無い。
 ・最初に200点持っているという説明もないため、結果の解釈がわかりにくい。
 ・結果画面のTURN BLOCKの計算が合わない。例えば+0-20のとき、TOTAL: 160 pointになっている。

●ソースコードを読んだ感想
 ・せっかくHIGHTとWIDTHを定義しているのに、91行目の要素数や220行目の判定にマジックナンバーを使用しているのが気になる。
 ・実際はソートではなく落とすだけなのに関数名がSort_blockなのはわかりにくい。
 ・342行目の条件式はst.y
#include //exit(),rand()
#include //time()
#include //getch(),kbhit()
#include //Sleep()

/**** 定数 ****/
#define HIGHT 20
#define WIDTH 15
#define SUMBLOCKS 8

/**** プロトタイプ宣言 ****/
void Draw_map(void); //ステージ(メイン)の出力
void Draw_block(int b); //空白かブロックの出力
int Check_wall(int dir); //ブロック移動の制限
int Check_roll(int dir); //回転時の壁判定
void Check_line(); //ラインが揃ってるか調べる
void Del_line(int line); //ラインを一行削除
void Sort_block(int line); //消されたラインを詰める
void Set_block(int mode); //回転用ブロックのセット
void Fall_block(); //ブロックの固定、新規ブロックの取得
void Move_block(int dir); //ブロックの移動
void Turn_block(int dir); //ブロックの回転
void Hold_block(); //ブロックの固定
void Key_inp(); //キーボード入力
void Timer_funk();
void Title(); //タイトルの表示
void Game_over(); //ゲームオーバー画面

struct BLOCKS{ //ブロックデータ
int block[4][4]; //最大4 * 4ドット
int block_size; //サイズ
};
struct BLOCKS block_data[SUMBLOCKS] =
{
{//四角 //ブロック0
{
{ 1, 1 },
{ 1, 1 }
}, 2
},
{//とつ //ブロック1
{
{ 0, 1, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
}, 3
},
{//エル //ブロック2
{
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
}, 3
},
{//逆エル //ブロック3
{
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
}, 3
},
{//エヌ //ブロック4
{
{ 0, 1, 0 },
{ 1, 1, 0 },
{ 1, 0, 0 }
}, 3
},
{//逆エヌ //ブロック5
{
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 0, 0, 1 }
}, 3
},
{//横棒 //ブロック6
{
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 }
}, 4
},
{//斜め棒 //ブロック7
{
{0, 1},
{1, 0}
}, 2
}
};
struct BLOCKS block_data2[SUMBLOCKS]; //回転処理後のデータ

struct STAGE{
int stage[HIGHT][WIDTH]; //ステージ全体 20*15
int x; //ブロックの現在位置 X
int y; //ブロックの現在位置 Y
int direction; //回転位置
int ground; //地面に付いているかいないか
int b_type; //落下中ブロックのタイプ
};
struct STAGE st = //マップデータ
{
{
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
}, 5, 0, 0, 0, 3
};

struct SCORE {
int score;
int delline;
int turnnum;
};

struct SCORE score_data = {
0, //総合得点
0, //消したライン数
0 //回転した回数
};

int GAMEOVER = 0;
int WAIT = 20;

/***********************************************************/
/************************* MAIN関数 ************************/
/***********************************************************/

int main(void)
{
int c=0;
srand((unsigned) time(NULL));
Set_block(8);
Title(); //タイトル
printf("Key:Start!\n");
while (1){
if (_kbhit() != 0){
c = _getch();
if (c == 115){ //S_key
printf("Start!!!\n");
break;
}
}
}
system("cls");
Timer_funk();
system("cls");
Game_over();
system("pause");
return 0;
}

/**************** タイマー *******************/
void Timer_funk()
{
int time=0;
time = 0;
while (GAMEOVER != 1){
for (time = 0; time = st.y && i = st.x && j =15 && i != 19) {
Del_line(i); //1行すべてが揃っていたら
Sort_block(i); //ブロックを詰める
score_data.score += 100; //スコアを+100
score_data.delline += 1; //消したライン数を+1
}
}
printf("\n");
sum = 0;

}
// printf("X=%02d Y=%02d\n", st.x, st.y);
printf("DEL LINE: %d\n", score_data.delline);
printf("TURN BLOCK: %d\n", score_data.turnnum);
printf("TOTAL SCORE: %d", score_data.score);
}

/*************** 削除 *******************/
//呼び出し元:マップ描画
void Del_line(int line)
{
int i;
for(i = 1; i 0; i--){
for( j = 1; j = 1) printf("■");
else printf(" ");
}

/********** 回転用ブロックのセット ***********●*/

void Set_block(int mode) //mode:8 全初期化 mode:0~6 指定初期化
{ //初期ブロックデータを回転用データにセット
int i, j, k, size;
if (mode == 8){ //全初期化
for (i = 0; i = 0 && mode >");
scanf("%d",&mode);
if(mode == 0) WAIT = 20;
else if(mode == 1) WAIT = 15;
else if(mode == 2) WAIT = 10;
}

void Game_over()
{
puts("■■■■ ■■■■ ■■■■■ ■■■■");
puts("■    ■  ■ ■ ■ ■ ■");
puts("■ ■■ ■■■■ ■ ■ ■ ■■■■");
puts("■  ■ ■  ■ ■ ■ ■ ■");
puts("■■■■ ■  ■ ■ ■ ■ ■■■■");
puts("");
puts("■■■■ ■  ■ ■■■■  ■■■■");
puts("■  ■ ■  ■ ■     ■  ■");
puts("■  ■ ■  ■ ■■■■  ■■■■");
puts("■  ■ ■  ■ ■     ■ ■");
puts("■■■■  ■■  ■■■■  ■  ■");
puts("");
printf("DEL LINE: %d >> 100 × %d = +%d point\n",score_data.delline, score_data.delline, 100 * score_data.delline);
printf("TURN BLOCK: %d >> -20 × %d = -%d point\n", score_data.turnnum, score_data.turnnum, 20 * score_data.turnnum);
printf("TOTAL: %d point\n", score_data.score);
}
[/code]

アバター
みけCAT
記事: 6734
登録日時: 14年前

Re: CUIでテトリスを作ってみました。

投稿記事 by みけCAT » 11年前

kerotan0820 さんが書きました:Check_roll および Check_wall 関数について、不定の値が返るとのことですが、これらの関数はキーボードから入力があった際にだけ呼び出される関数です。
なので必ず結果的に 0 か 1 を返すと思うのですが…。
これらの関数の値は、呼び出されたのがキーボードから入力があった際かどうかとは関係ないと思います。
Check_roll関数はこっそり修正したようですが、Check_wall関数が本当に0か1しか返していないかどうかチェックしてみましょう。
新しいコードの189行目~214行目を以下のコードに入れ替えます。

CODE:

/**************** キー入力受付*****************●*/
int Check_wall_wrapper(int a) {
    int ret=Check_wall(a);
    if(ret!=0 && ret!=1) {
        printf("Check_wall(%d) returned a value other than 0 and 1 !!! (%d)\n",a,ret);
        exit(1);
    }
    return ret;
}

void Key_inp()
{
    int key=0;
    if (_kbhit() != 0){
        key = _getch();
        if (key == 99){     //C_key(左回り)
            if(Check_roll(1)) Turn_block(1);
        }
        else if (key == 122){   //Z_key(右回り)
            if(Check_roll(0)) Turn_block(0);
        }
        else if (key == 88){
            Fall_block();
        }
        else if (key == 75){        //←_key(左へ移動)
            if(Check_wall_wrapper(-1)) st.x -= 1;   //現在地の左にブロックがなければ移動
        }
        else if (key == 77){        //→_key(右へ移動)
            if(Check_wall_wrapper(1))  st.x += 1;   //現在地の右にブロックがなければ移動
        }
 
        system("cls");
        Draw_map();
    }
}