CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kerotan0820
記事: 91
登録日時: 14年前

CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#1

投稿記事 by kerotan0820 » 11年前

Visual Studio 2008~2013
C を用いて作っております。
とても効率のよいアルゴリズムでは無いですが、まず完成させるまでは自分の考えられる範囲で作りたいと思っております。


今現在ブロックの地面に積む処理で困っております。
ブロックを90度、またはマイナスに90度回した際のみ、なぜか地面に落ちたブロックが崩壊してしまいます。

ブロックは2つの配列を用意し、相互間を転置したブロックを渡しあっております。
回転の状態を記録しておき、どちらの配列を参照するか判断しています。

プログラムはこちらです。

コード:

/***** ヘッダー ****/
#include<stdio.h>
#include<stdlib.h>	//exit()
#include<conio.h>	//getch(),kbhit()
#include<windows.h>	//Sleep

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

/**** プロトタイプ宣言 ****/
void Draw_map(void);				//ステージ(メイン)の出力
void Draw_block(int b);				//空白かブロックの出力
void Check_map();					//ブロックの固定、新規ブロックの取得
void Set_block(int mode);			//回転用ブロックのセット
void Move_block(int dir);			//ブロックの移動
void Turn_block(int dir, int type);	//ブロックの回転
void Hold_block();					//ブロックの固定
void Output_test(int type);
void Key_inp();
void Timer_funk();
void title();						//タイトルの表示

struct BLOCKS{				//ブロックデータ
	int block[4][4];		//最大4 * 4ドット
	int block_size;			//サイズ
};
struct BLOCKS block_data[SUMBLOCK] =
{
	{//四角		//ブロック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[SUMBLOCK];	//回転処理後のデータ

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

int FLAG = 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();

	return 0;
}

/**************** タイマー *******************/
void Timer_funk()
{
	int time=0;
	time = 0;
	while (1){
		for (time = 0; time < 20; time++){
			Sleep(1);		//1[ms]のウェイト
			Key_inp();		//回転処理の常時受け付け
		}
		system("cls");
		Check_map();		//移動先のチェック
		FLAG = 0;			//チェック済み
		Draw_map();
		st.y += 1;
	}
}

/**************** キー入力受付*****************●*/
void Key_inp()
{
	int key=0;
	if (_kbhit() != 0){
		key = _getch();
		if (key == 99){		//C_key(左回り)
			Turn_block(1, st.b_type);
		}
		else if (key == 122){	//Z_key(右回り)
			Turn_block(0, st.b_type);
		}
		else if (key == 75){		//←_key(左へ移動)
			st.x -= 1;
		}
		else if (key == 77){		//→_key(右へ移動)
			st.x += 1;
		}
		system("cls");
		Draw_map();
		//		printf("X=%02d Y=%02d\n", st.x, st.y);
	}
}

/**************** マップ描画 ******************●*/
void Draw_map()
{
	int i, j, size;
	size = block_data[st.b_type].block_size;

	for (i = 0; i < HIGHT; i++){			//マップ縦全体
		for (j = 0; j < WIDTH; j++){		//マップ横全体
			if( (i >= st.y && i <= st.y + size) && ( j >= st.x && j <= st.x + size) ){
				if (st.direction % 2 == 0){
					Draw_block(block_data2[st.b_type].block[i-st.y][j-st.x] + st.stage[i][j]);
				}
				else{
					Draw_block(block_data[st.b_type].block[i-st.y][j-st.x] +  st.stage[i][j]);
				}
			}else{
				Draw_block(st.stage[i][j]);
			}
		}
		printf("\n");
	}
	printf("X=%02d Y=%02d\n",st.x,st.y);
}

/************* 空白orブロック描画 ************●*/

void Draw_block(int b)
{
	if(b >= 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 < SUMBLOCK; i++){
			size = block_data[i].block_size;
			for (j = 0; j < size; j++){
				for (k = 0; k < size; k++){
					block_data2[i].block[j][k] = block_data[i].block[j][k];
				}
			}
		}
	}
	else if (mode >= 0 && mode <= 6){
	}
}

/*************** あたり判定 *******************/
void Check_map()
{
	int i, j, size;
	size = block_data[st.b_type].block_size;

	for(i = st.y; i < st.y + size; i++){				//現在のY座標から現在のY座標+ブロックのサイズ分
		for(j = st.x; j < st.x + size; j++){			//現在のX座標から現在のX座標+ブロックのサイズ分
			if( block_data[st.b_type].block[i-st.y][j-st.x] * st.stage[i+1][j] == 1){
				Hold_block();								//ブロック固定
				FLAG = 1;
				break;
			}else if(i == st.y + size && j == st.x + size){//配列末までマップとの重複なし
				st.y += 1;		//y座標をひとつ下げる
			}
		}
		if(FLAG==1) break;
	}
}

/*************** ブロック固定 *******************/
void Hold_block()
{
	int i,j,size;
	size = block_data[st.b_type].block_size;
	for(i=st.y; i < st.y + size; i++){				//現在のY座標から現在のY座標+ブロックのサイズ分
		for(j=st.x; j < st.x + size; j++){			//現在のX座標から現在のX座標+ブロックのサイズ分
			if (st.direction % 2 == 0 && block_data2[st.b_type].block[i-st.y][j-st.x] != 0){//回転回数偶数でブロックデータがあれば
				st.stage[i][j] = 1;	//ブロック情報をマップに書き込み
			}
			else if(block_data[st.b_type].block[i-st.y][j-st.x] != 0){				 					//回転位置(奇数):a←b
				st.stage[i][j] = 1;	//ブロック情報をマップに書き込み
			}
		}
	}
	st.y = 0;
}

/*************** ブロック回転 *****************/

void Turn_block(int dir, int type)	//回転方向、種類
{
	int i, j;
	int size = block_data[type].block_size;				//ブロックのサイズ

	for (i = 0; i < size; i++){
		for (j = 0; j < size; j++){
			if (dir == 1){						//右回なら
				if (st.direction % 2 == 0){				//回転位置(偶数):b←a
					block_data[type].block[j][size - 1 - i] = block_data2[type].block[i][j];
				}
				else{				 					//回転位置(奇数):a←b
					block_data2[type].block[j][size - 1 - i] = block_data[type].block[i][j];
				}
			}
			else if (dir == 0){					//左回なら
				if (st.direction % 2 == 0){				//回転位置(偶数):b←a
					block_data2[type].block[i][j] = block_data[type].block[j][size - 1 - i];
				}
				else{									//回転位置(奇数):a←b
					block_data[type].block[i][j] = block_data2[type].block[j][size - 1 - i];
				}
			}
		}
	}

	if (dir == 1){										//右回なら回転位置+
		if (st.direction == 3) st.direction = 0;		//回転位置3→0
		else st.direction += 1;
	}
	else if (dir == 0){									//左回なら回転位置-
		if (st.direction == 0) st.direction = 3;		//回転位置0→3
		else st.direction -= 1;
	}
}


void title()
{
	printf(" ■■■  ■   ■  ■ ■■■■\n");
	printf("      ■   ■  ■    ■\n");
	printf("■■■■■ ■■■ ■  ■   ■\n");
	printf("  ■   ■      ■  ■■\n");
	printf(" ■■   ■     ■  ■  ■\n\n");
}

/******* ブロック単体 出力テスト用 *********/
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");
}
インデントが乱れていたらすみません。
長らく悩んでいるのですが原因が分からず困っております。
質問するには長いプログラムで大変恐縮なのですが、ぱっとみて何か思い当たる点がございましたらご教示くださると幸いです。
よろしくお願い致します。
けろけろにゃー (」・ω・)」うー!

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#2

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

とりあえず、268行目の

コード:

            else if(block_data[st.b_type].block[i-st.y][j-st.x] != 0){                                  //回転位置(奇数):a←b

コード:

            else if(st.direction % 2 != 0 && block_data[st.b_type].block[i-st.y][j-st.x] != 0){                                  //回転位置(奇数):a←b
としたところ、

コード:

■■■
  ■
が着地した時に

コード:

 ■■
■■■
 ■■
となる問題は改善しました。

コード:

  ■■■
    ■
■■■
  ■
のように落下させた場合にすり抜ける問題は調査中です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#3

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

Check_map関数を

コード:

void Check_map()
{
    struct BLOCKS* bd = (st.direction % 2 == 0 ? block_data2 : block_data );
    int i, j, size;
    size = block_data[st.b_type].block_size;
 
    for(i = st.y; i < st.y + size; i++){                //現在のY座標から現在のY座標+ブロックのサイズ分
        for(j = st.x; j < st.x + size; j++){            //現在のX座標から現在のX座標+ブロックのサイズ分
            if( bd[st.b_type].block[i-st.y][j-st.x] * st.stage[i+1][j] == 1){
                Hold_block();                               //ブロック固定
                FLAG = 1;
                break;
            }else if(i == st.y + size && j == st.x + size){//配列末までマップとの重複なし
                st.y += 1;      //y座標をひとつ下げる
            }
        }
        if(FLAG==1) break;
    }
}
としたところ、上記のすり抜けが改善されました。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#4

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

今回のプログラムは、無駄にブロックのデータを2本の配列で持っているため、切り替え忘れのバグが発生したようですね。
この程度のプログラムならそんなに速度は要求されないと思うので、
一度別のバッファに書き込んだ回転後のブロックのデータをもとの配列に書き戻せばいいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#5

投稿記事 by kerotan0820 » 11年前

みけCAT さんが書きました:Check_map関数を

コード:

void Check_map()
{
    struct BLOCKS* bd = (st.direction % 2 == 0 ? block_data2 : block_data );
    int i, j, size;
    size = block_data[st.b_type].block_size;
 
    for(i = st.y; i < st.y + size; i++){                //現在のY座標から現在のY座標+ブロックのサイズ分
        for(j = st.x; j < st.x + size; j++){            //現在のX座標から現在のX座標+ブロックのサイズ分
            if( bd[st.b_type].block[i-st.y][j-st.x] * st.stage[i+1][j] == 1){
                Hold_block();                               //ブロック固定
                FLAG = 1;
                break;
            }else if(i == st.y + size && j == st.x + size){//配列末までマップとの重複なし
                st.y += 1;      //y座標をひとつ下げる
            }
        }
        if(FLAG==1) break;
    }
}
としたところ、上記のすり抜けが改善されました。

解答有難うございます。
あまりに早く正確な解答に目から鱗です。

一つ質問があります。

コード:

struct BLOCKS* bd = (st.direction % 2 == 0 ? block_data2 : block_data );
この処理はどういう仕組みになっているのでしょうか。
奇数が偶数かで?演算子を用いて2つの配列を選択しているというのは分かるのですが、BLOCK* という表記と bd には何が格納されるのか、教えて頂けませんでしょうか。
またはどういう単元を理解すれば良いのか教えて頂けると幸いです。
けろけろにゃー (」・ω・)」うー!

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#6

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

BLOCK*という表記はありません。(実際にコンパイルエラーになりました。なんで知っているんですか?)
struct BLOCKS*は見たまんまの意味で、BLOCKS構造体のポインタです。
bdにはst.direction % 2 == 0が真のときはblock_data2[0]のポインタが、偽のときはblock_data[0]のポインタが入ります。

横の壁との当たり判定を実装しようとしましたが、今のバージョンでは壁にくっつけて回転すると死にます。
(複数の箇所を変更したので全文掲載します)
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#7

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

回転時の当たり判定を追加しました。
上のコードのTurn_block関数を以下のものに置き換えます。
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: CUIのテトリスを作っています。ブロック落下後の設置がうまくいきません。

#8

投稿記事 by kerotan0820 » 11年前

みけCAT様解答有難うございました。
ポインタを用いる構造体についてはなんとなく動作が分かりました。また近いうちに詳しく学んでみたいと思います
けろけろにゃー (」・ω・)」うー!

閉鎖

“C言語何でも質問掲示板” へ戻る