ページ 11

Visual C++ 2008 テトリスの「ブロックランダム」表示について

Posted: 2012年6月22日(金) 01:08
by たか
「0~7」種類のブロックをランダムで表示し、種類に応じて「番号で形成されたブロック」が落ちてくるように書いたのですが、グローバル変数で「ランダム値」を設けている為、ブロック生成の度に、積み重なっているブロックまで「番号」が変換されてしまいます。

改善する方法を何方かよろしくお願いいたします。

コード:

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


int stage[21][12] = {0};

int field[21][12] = {0};
int block[4][4] = {0};

int blocklist[7][4][4] = { 
	
	{{0,0,0,0},
	 {0,1,1,0},
     {0,1,1,0},
     {0,0,0,0}},

    {{0,0,0,0},
     {0,1,0,0},
     {1,1,1,0},
     {0,0,0,0}},
	
	{{0,0,0,0},
     {0,0,1,0},
     {0,1,1,0},
     {0,1,0,0}},

    {{0,0,0,0},
     {0,1,0,0},
     {0,1,1,0},
     {0,0,1,0}},
 
    {{0,0,0,0},
     {0,1,1,0},
     {0,1,0,0},
     {0,1,0,0}},
 
    {{0,0,0,0},
     {0,1,1,0},
     {0,0,1,0},
     {0,0,1,0}},
 
    {{0,0,1,0},
     {0,0,1,0},
     {0,0,1,0},
	 {0,0,1,0}}


};

int y = 0; //ブロックのステージ上でのy座標
int x = 4; //ブロックのステージ上でのx座標

int gameover = 0; //ゲームオーバー判定。1でゲームオーバー

int oneline,twoline,threeline,fourline;

int block_type; //ブロックの種類用。0~6の乱数を入れる

//関数プロトタイプ宣言
void Initialize(); //ゲーム起動直後の初期設定を行う関数。画面と壁のデータを初期化
int CreateBlock(); //新しいブロックを生成して次のブロックに発生させる
void ShowGameField(); //field[][]の中身に応じて、画面を描画する
void ControlBlock(); //キー入力に応じてブロックに移動や回転等の処理を行わせる
int  CheckOverlap(int, int); //落下中のブロックが壁や固定済みブロックに接触していないか判別
void MoveBlock(int, int); //落下中ブロックを一旦消して、任意の座標に移動させる
int  TrunBlock(); //ブロックの回転を処理する
void DropBlock(); //ブロックを落下させる。下に移動できない場合ブロックをその位置に固定
void LockBlock(); //着地したブロックを固定済みブロックに加える関数
void CheckLines(); //ブロックが横一列にそろえばそこを消去後、上のブロックをそこに下ろす

int B_type(int);//生成したブロックに番号で色のようなものをつける

int main()
{
	int time = 0; //一定時間ごとにブロックを落下させるための時間稼ぎ用
	
	Initialize(); //ステージの初期化
	
	while(!gameover) {
	
		if(kbhit()) {
			ControlBlock();
		}
		if(time < 20000){
			time++;
		}
		else{		
			//重なりがなければ移動
			DropBlock();
			time = 0;
		}
	}
	return 0;
}

void Initialize()
{
	int i, j; //forループ制御用変数
	//画面と壁を初期設定
	for(i = 0; i <=20; i++) {
		for(j = 0; j <= 11; j++) {

			if((j == 0) || (j == 11) || (i == 20)) {
				field[i][j] = stage[i][j] = 9;//枠の棒
			}
			else {
				field[i][j] = stage[i][j] = 0;
			}
		}
	}
	CreateBlock();
	ShowGameField(); //ゲーム直後の画面を描画
}


int CreateBlock()
{
	int i, j;
	
	//ブロックの座標を(block4*4)最上段中央にセット
	x = 4;
	y = 0;

    srand((unsigned)time(NULL));
	block_type = rand() % 7;
	
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 4; j++) {

			block[i][j] = 0;
			block[i][j] = blocklist[block_type][i][j];
		}
	}
	
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 4; j++) {
			field[i][j + 4] = stage[i][j + 4] + block[i][j];//+4で中央セット
			
			//初期位置に置いたブロックが既に固定ブロックに重なっていればゲームオーバー
			if(field[i][j + 4] > 1) {//0だと既に入っているので1
				gameover = 1;
				return 1;
			}
		}
	}
	return 0;
}

int B_type()
{
	int b = 0;

	switch(block_type){//CreateBlock内のblock_typeと同じ値
     case 0:
		b = 11;
		break;
	 case 1:
		b = 22;
		break;
	 case 2:
		b = 33;
		break;
	 case 3:
		b = 44;
		break;
	 case 4:
		b = 55;
		break;
	 case 5:
		b = 66;
		break;
	 case 6:
		b = 77;
		break;
	}
	return b;
}

//画面表示
void ShowGameField()
{
	int i, j; //forループ制御用変数
	//画面を一旦クリア
	system("cls");
	//データに応じてブロックや空白を画面表示
	for(i = 0; i<21; i++) {
		for(j = 0; j < 12; j++) {
			switch(field[i][j]) {
	 case 0:
		printf("  ");
		break;
	 case 9:
		printf("□");
		break;
	 
	 default:
		printf("%d",B_type());//番号でブロックを形成
		break;
				}
			}
			printf("\n");
	}
	//得点表示
	printf("\n1行消し:%d回 2行消し:%d回 3行消し:%d回 4行消し:%d回\n", 
		oneline, twoline, threeline, fourline);
	//ゲームオーバーの場合はGAME OVERを表示
	if(gameover){
		system("cls");
		printf("\n\n\n\n\nGAME OVER\n\n");
	}
}

void ControlBlock()
{
	char key; //受け付けたキーを保存する変数
	
	key = getch(); //キーから一文字入力
	//キーに応じて各方向へブロックを移動したり、回転させたりする
	switch(key)
	{
	case 'f':
		if(!CheckOverlap(x+1, y)) {
			MoveBlock(x+1, y);
		}
		break;
	case 's':
		if(!CheckOverlap(x-1, y)) {
			MoveBlock(x-1, y);
		}
		break;
	case 'd':
		if(!CheckOverlap(x, y+1)) {
			MoveBlock(x, y+1);
		}
		break;
	case ' ':
		TrunBlock();
	}
}


void MoveBlock(int x2, int y2)
{
	int i, j; //forループ制御用変数
	//今までのブロックを消して
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 4; j++) {
			field[y+i][x+j] -= block[i][j];//blockで引くことにより0にする。
		}
	}
	//ブロックの座標を更新
	x = x2;
	y = y2;
	
	//新しい座標でブロックを入れなおす
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 4; j++) {
			field[y+i][x+j] += block[i][j];
		}
	}
	ShowGameField();//再度読み込み描画
}

int CheckOverlap(int x2, int y2)
{
	int i, j; //forループ制御用変数
	//ブロックが向かう位置に固定ブロックもしくは壁があるかどうかを検査
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 4; j++) {
			if(block[i][j]) {
				if(stage[y2 + i][x2 + j] != 0) {
					return 1;
				}
			}
		}
	}
	return 0;
}

//ブロックを落とす
void DropBlock()
{
	//重なりがなければ移動
	if(!CheckOverlap(x, y + 1)) {
		MoveBlock(x, y + 1);//下にobj一個分降下
	}
	//重なりがあれば壁にする
	else{
		LockBlock();
		CreateBlock();
		ShowGameField();
	}
}

void LockBlock()
{
	int i, j; //forループ制御用変数
	
	//ブロックを壁に加える 
	for(i = 0; i < 21; i++) {
		for(j = 0; j < 12; j++) {
		 
		        stage[i][j] = field[i][j];
    
		}
	}

	CheckLines(); //横一列がそろってるか判定して処理する関数を呼ぶ
	//列完成判定後の壁をフィールドへ
	for(i = 0; i<21; i++) {
	    for(j = 0; j<12; j++) {
	   
	      field[i][j] = stage[i][j];
	    }
	}
}

//ブロックを回転させる処理
int TrunBlock()
{
	int i,j;//forループ制御用
	int temp[4][4] = {0};//ブロックを一時的に保存する配列

	//ブロックを回転させる前にtenpにセーブ
	for(i = 0; i < 4; i++){
		for(j = 0; j < 4; j++){

			temp[i][j] = block[i][j];
		}
	}

	//ブロックを回転(右回り)
	for(i = 0; i < 4; i++){
		for(j = 0; j < 4; j++){
//                x  y           y    x    xとyを反転させ、さらにyも反転
			block[i][j] = temp[3 - j][i];//0~3 = 4 (3-j)によってjを反転
		}
	}
	//重なっているブロックが出てしまったらブロックを回転前に戻して中止
	if(CheckOverlap(x, y)){//検査結果、引数で真(1)なら回転中止
		for(i = 0; i < 4; i++){
			for(j = 0; j < 4; j++){

			    block[i][j] = temp[i][j];
			}
		}
		return 1;
	}
	//一旦fildeからblockを消して回転後のblockを再表示
	for(i = 0; i < 4; i++){
		for(j = 0; j < 4; j++){
			field[y + i][x + j] -= temp[i][j];
			field[y + i][x + j] += block[i][j];
		}
	}
	ShowGameField();
	return 0;
	
}
//横一列が完成しているかを検査。そろっていればそこを消して上のブロック群を下す
void CheckLines()
{
	int i,j,k;
	int comp;//横一列そろっていれば1、一つでも隙間があると0になる
	int lines = 0;//同時に消したラインの数

	while(1){
		for(i = 0; i < 20; i++){
			comp = 1;//compを1にしておき下のfor文で0を代入する

			for(j = 1; j < 11; j++){
				if(stage[i][j] == 0){//横に見ていき一つでも隙間があると0になる
					comp = 0;
				}
			}
			if(comp == 1) break;//「横一列そろっていれば1」forを抜ける
		}
		if(comp == 0) break;

		lines++;

		//列の消去

		for(j = 1; j < 11; j++){
			stage[i][j] = 0;//blockを0に置き換える
		}
		//消えた列より上にあった固定ブロックを列の消えたところへ下す
		for(k = i; k > 0; k--){//消えた「行」から上限の「行」まで遡る
			for(j = 1; j < 11; j++){//横に見ていく

				stage[k][j] = stage[k -1][j];
			}
		}
	}
	
		//同時に消したラインの数をカウント
	switch(lines){
        case 1:
		    oneline++;
		    break;
	    case 2:
		    twoline++;
		    break;
     	case 3:
	    	threeline++;
		    break;
    	case 4:
	    	fourline++;
		    break;
    	default:
	    	break;
	}
}

Re: Visual C++ 2008 テトリスの「ブロックランダム」表示について

Posted: 2012年6月22日(金) 07:08
by へにっくす
たか さんが書きました:「0~7」種類のブロックをランダムで表示し、種類に応じて「番号で形成されたブロック」が落ちてくるように書いたのですが、グローバル変数で「ランダム値」を設けている為、ブロック生成の度に、積み重なっているブロックまで「番号」が変換されてしまいます。
そこまで分かってて、何で別の変数にすることを思いつかないのかなあ。。
そもそも落ち始めたら、ブロックタイプは変わらないんだから、現在落ちているブロックのタイプ用に変数を用意するべきなんじゃないのかな?

Re: Visual C++ 2008 テトリスの「ブロックランダム」表示について

Posted: 2012年6月22日(金) 23:38
by たか
int B_type()←消去
int CreateBlock()←一部変更
void ShowGameField()←一部変更

上記三つの変更で無事、希望道理できました。