Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

テトリスを回転したときほかの設置されているテトリミノに重なってしまう件

解決したいこと

テトリミノを回転したときに設置されているテトリミノに重なってしまいます。

スクリーンショット 2024-12-04 014508.png
Rキーでテトリミノを回転すると
スクリーンショット 2024-12-04 014513.png
このように設置されているテトリミノと重なってしまう結果になってしまいます。
一枚目の写真の状態からRキーを押しても回転しないようにしたいです。

↓は私が書いたコードです
例)

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

#define FIELD_HIGHT 20
#define FIELD_WIDE 10

int field[FIELD_HIGHT][FIELD_WIDE];
int backBuffer[FIELD_HIGHT][FIELD_WIDE];

//テトリミノの定義
typedef struct {
    int shape[4][4];
    int width;
    int hight;
    int currentX;
    int currentY;
} Tetrimino;

Tetrimino I = {
    {
        {1, 1, 1, 1},
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    4, // 横のサイズ
    1, // 縦のサイズ
    0, // 初期のX座標
    0  // 初期のY座標
};

Tetrimino T = {
    {
        {0, 1, 0, 0},
        {1, 1, 1, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    3, // 横のサイズ
    2, // 縦のサイズ
    0, // 初期のX座標
    0  // 初期のY座標
};

Tetrimino O = {
    {
        {1, 1, 0, 0},
        {1, 1, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    2, // 横のサイズ
    2, // 縦のサイズ
    0, // 初期のX座標
    0  // 初期のY座標
};

Tetrimino S = {
    {
        {0, 1, 1, 0},
        {1, 1, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    3, // 横のサイズ
    2, // 縦のサイズ
    0, // 初期のX座標
    0  // 初期のY座標
};

Tetrimino Z = {
    {
        {1, 1, 0, 0},
        {0, 1, 1, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    3, // 横のサイズ
    2, // 縦のサイズ
    0, // 初期のX座標
    0  // 初期のY座標
};

// 他のテトリミノ省略...
Tetrimino tetriminos[] = { I,T,Z,S,O };
Tetrimino selectedTetrimino;

void initializeField();
int rotateTetrimino(Tetrimino* tetrimino,int newX,int newY);
void DrawScreen();
void InitMino();
int hit_Mino(int newX,int newY);
void put_Mino();


int main() {
    srand((unsigned int)time(NULL));// 乱数の初期化  

    initializeField();
    InitMino();


    // キーボード入力を待つループ
    while (1) {
        if (_kbhit()) {
            switch (_getch()) {
            case 'a':
                if (!hit_Mino(selectedTetrimino.currentX - 1, selectedTetrimino.currentY)) {
                    selectedTetrimino.currentX--;
                }       
                break;
            case 'd':
                if (!hit_Mino(selectedTetrimino.currentX + 1, selectedTetrimino.currentY)){
                    selectedTetrimino.currentX++;
                }
                break;
            case 's':
                if (!hit_Mino(selectedTetrimino.currentX , selectedTetrimino.currentY+1)){
                    selectedTetrimino.currentY++;
                }
                else {
                    put_Mino();
                }
                break;
            case 'r':
                rotateTetrimino(&selectedTetrimino, selectedTetrimino.currentX, selectedTetrimino.currentY);              
                break;
            }           
        } 
        // カーソルを左上に配置
        printf("\033[H");
        DrawScreen();      
    }
 
}

// フィールドを初期化
void initializeField() {
    memset(field, 0, sizeof field);
}

void InitMino() {

    int rendomIndex = rand() % 5; // 一時的に1つだけ使用   
    selectedTetrimino = tetriminos[rendomIndex];
    
    // テトリミノの初期位置を指定
    selectedTetrimino.currentX = (FIELD_WIDE - selectedTetrimino.currentX) / 2;
    selectedTetrimino.currentY = 0;
}

// 描画処理
void DrawScreen() {
    memset(backBuffer, 0, sizeof backBuffer);

    for (int y = 0; y < FIELD_HIGHT; y++) {
        for (int x = 0; x < FIELD_WIDE; x++) {
            backBuffer[y][x] = field[y][x];
        }
   }

   

    // テトリミノの描画
    for (int y = 0; y < selectedTetrimino.hight; y++) {
        for (int x = 0; x < selectedTetrimino.width; x++) {
            if (selectedTetrimino.shape[y][x] == 1) {
                backBuffer[selectedTetrimino.currentY + y ][selectedTetrimino.currentX + x] = 1;
            }
        }
    }
    
    // バックバッファをコンソール上に出力
    for (int y = 0; y < FIELD_HIGHT; y++) {
        printf("■");
        for (int x = 0; x < FIELD_WIDE; x++) {         
            if (backBuffer[y][x] == 1)
                printf("■");
            else
                printf(" ");
        }
        printf("■\n");
    }
    for (int x = 0; x < FIELD_WIDE+2; x++)
        printf("■");
}

// テトリミノの回転
int rotateTetrimino(Tetrimino* tetrimino,int newX,int newY) {
    int original[4][4] = { 0 };
    int rotated[4][4] = { 0 };

    // 回転前の形状を保存
    for (int y = 0; y < tetrimino->hight; y++) {
        for (int x = 0; x < tetrimino->width; x++) {
            original[y][x] = tetrimino->shape[y][x];
        }
    }

    // 回転の計算
    for (int y = 0; y < tetrimino->hight; y++) {
        for (int x = 0; x < tetrimino->width; x++) {
            rotated[x][tetrimino->hight - 1- y] = tetrimino->shape[y][x];
        }
    }

    // 回転時にテトリミノとの衝突判定
    for (int y = 0; y < tetrimino->hight; y++) {
        for (int x = 0; x < tetrimino->width; x++) {
            if (rotated[y][x] == 1) {
                if (newX + x < 0 || newX + x >= FIELD_WIDE ||
                    newY + y >= FIELD_HIGHT ||
                    field[newY + y][newX + x] == 1) {
                    // 衝突時に元の状態に戻す
                    for (int j = 0; j < tetrimino->hight; j++) {
                        for (int i = 0; i < tetrimino->width; i++) {
                            tetrimino->shape[j][i] = original[j][i];
                        }
                    }
                    return false;// 回転失敗

                }
            }
        }
    }

    // 回転後の形状を元の形状にコピー
    for (int y = 0; y < 4; y++) {
        for (int x = 0; x < 4; x++) {
            tetrimino->shape[y][x] = rotated[y][x];
        }
    }

    // 高さと幅の値を入れ替える
    int temp = tetrimino->width;
    tetrimino->width = tetrimino->hight;
    tetrimino->hight = temp;
  
    // 枠外修正
    if (tetrimino->currentX + tetrimino->width > FIELD_WIDE) {
        tetrimino->currentX = FIELD_WIDE - tetrimino->width;
    }
    if (tetrimino->currentY + tetrimino->hight > FIELD_HIGHT) {
        tetrimino->currentY = FIELD_HIGHT - tetrimino->hight;
    }  
    return true;// 回転成功
}

void put_Mino() {
    for (int y = 0; y < selectedTetrimino.hight; y++) {
        for (int x = 0; x < selectedTetrimino.width; x++) {
            if (selectedTetrimino.shape[y][x] == 1) {
                field[selectedTetrimino.currentY + y][selectedTetrimino.currentX + x] = 1;
            }
        }
    }

    InitMino();
}

// 衝突判定
int hit_Mino(int newX,int newY) {
    for (int y = 0; y < selectedTetrimino.hight; y++) {
        for (int x = 0; x < selectedTetrimino.width; x++) {
            if (selectedTetrimino.shape[y][x] == 1) {
                if (newX + x < 0 || newX + x >=FIELD_WIDE ||
                    newY + y >=FIELD_HIGHT ||
                    field[newY + y][newX + x] == 1) {
                    return true;
                }
          }
        }
    } 
    return false;
}

自分で確かめたこと

rotateTetrimino関数内で回転前の形状を保存するoriginal[][]を作り、回転時の衝突判定で引っかかった場合、回転前のテトリミノにするプログラムを組みましたがうまくいきませんでした。

0

2Answer

落下物の処理方法自体もちょっとおかしいですね

現状→入力発生→現状をコピーして入力に応じた回転落下を行う→衝突していなければコピーを最新に、衝突していればコピーを破棄

こうなると思います
「元の形状に戻す」なんてしていたらエラーの元です

プログラムとしておかしいのはここかな?

NG.cpp
               if (newX + x < 0 || newX + x >= FIELD_WIDE ||
                    newY + y >= FIELD_HIGHT ||
                    field[newY + y][newX + x] == 1) {

#define FIELD_HIGHT 20
#define FIELD_WIDE 10

なのに

newX + x >= FIELD_WIDE

じゃ条件判定文に入らないかと

こうじゃね?

OK.cpp
               if (newX + x >= 0 || newX + x < FIELD_WIDE ||
                    newY + y < FIELD_HIGHT ||
                    field[newY + y][newX + x] == 1) {

でまあこのソース見た人ほぼ全員が思うけど
ソースが見にくすぎます

二重ループの中に条件判定2つでその中に二重ループとか
見にくいプログラムを書いてるとバグも発生しやすいので今のうちに見やすくしましょう

0Like

この判定ですが

    // 回転時にテトリミノとの衝突判定
-    for (int y = 0; y < tetrimino->hight; y++) {
+    for (int y = 0; y < 4; y++) {
-        for (int x = 0; x < tetrimino->width; x++) {
+        for (int x = 0; x < 4; x++) {
            if (rotated[y][x] == 1) {
                if (newX + x < 0 || newX + x >= FIELD_WIDE ||
                    newY + y >= FIELD_HIGHT ||
                    field[newY + y][newX + x] == 1) {
                    // 衝突時に元の状態に戻す
                    for (int j = 0; j < tetrimino->hight; j++) {
                        for (int i = 0; i < tetrimino->width; i++) {
                            tetrimino->shape[j][i] = original[j][i];
                        }
                    }
                    return false;// 回転失敗

                }
            }
        }
    }

tetrimino->width と tetrimino->height が 回転してもデフォルトの形状の範囲を示すので回転後のfieldを判断できていません。

例えば 横バーの形状の場合

Tetrimino I = {
    {
        {1, 1, 1, 1},
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    },
    4, // 横のサイズ   ☜ tetrimino->widt
    1, // 縦のサイズ   ☜ tetrimino->height
    0, // 初期のX座標
    0  // 初期のY座標
};

tetrimino->width と tetrimino->heightは 4x4マス中 横方向に4、縦に1マスです。
回転すると縦バーになるのですが、tetrimino->width と tetrimino->height は 変わらず、横方向に4て縦に1マスなので 4x4マス中 縦下3マスは確認しない事になります。

4 X 4マスの範囲を判断すれば良いと思います。

0Like

Your answer might help someone💌