vol.2_5 ぷよが積もるようにする

はじめに

今回は連続して組みぷよを生成し、実際にぷよが積もるようにします。
組みぷよの再生成に伴い、色もランダム化します。

組みぷよの連続生成

単純にwhile文を追加するだけです。
組みぷよの再生成の位置には注意しましょう。

____puyo.c____

#include "game.h"

int main(void)
{
    system("clear"); //画面のクリア

    //フィールドの描画
    for(int y = 1; y <= FIELD_HEIGHT; y++){
        for(int x = 1; x <= FIELD_WIDTH; x++){
            if((y != 1) && (x == 1 || x == FIELD_WIDTH || y == FIELD_HEIGHT))
                putColor(y, x, CELL_TYPE_WALL); //壁・床の描画
        }
        printf("\n");
    }
    
    //ゲーム部分
    
    while(1){
        //組みぷよの生成
        twinPuyoes_t tsumo = {
            INIT_ROLL_PUYO_X, INIT_ROLL_PUYO_Y,
            INIT_AXIS_PUYO_X, INIT_AXIS_PUYO_Y,
            CELL_TYPE_GREEN,
            CELL_TYPE_RED
        };
    
        //初期位置に設置
        putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
        putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
        
        struct timeval startTime; //組みぷよ生成時の時間
        gettimeofday(&startTime, NULL); //時間を取得
    
        while(1){
            //自由落下
            if(isPassedTime(FREE_FALL_TIME_PER_SQUARE, &startTime)){
                if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT)
                            break;
            
                twinPuyoes_t old; //再描画用
                old = tsumo;
                old.rollPuyoColor = CELL_TYPE_EMPTY;
                old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                tsumo.ry++;
                tsumo.ay++;
            
                //再描画
                //oldの削除
                putColor(old.ry, old.rx, old.rollPuyoColor);
                putColor(old.ay, old.ax, old.axisPuyoColor);
                //ぷよの描画
                putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
            
                //時間をリセット
                gettimeofday(&startTime, NULL);
            }
        
            if(kbhit()){
                //キーが入力されたかどうかのフラグ
                int keyFlag = FALSE_BOOL;
                //位置を変更するとき、もとの位置のぷよはクリアしたい
                //もとのぷよの情報をここに保持しておく
                twinPuyoes_t old;
            
                switch(getchar()){
                    case 'a':
                        //移動先が適切でなければ終了
                        if(tsumo.rx - 2 == FIELD_START_X || tsumo.ax - 2 == FIELD_START_X)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx -= 2;
                        tsumo.ax -= 2;
                        keyFlag = TRUE_BOOL;
                        break;
                
                    case 'd':
                        //移動先が適切でなければ終了
                        if(tsumo.rx + 2 == FIELD_WIDTH || tsumo.ax + 2 == FIELD_WIDTH)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx += 2;
                        tsumo.ax += 2;
                        keyFlag = TRUE_BOOL;
                        break;
                
                    case 's':
                        //移動先が適切でなければ終了
                        if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.ry++;
                        tsumo.ay++;
                        keyFlag = TRUE_BOOL;
                        break;
                    
                    default: break;
                }
            
                if(keyFlag == TRUE_BOOL){
                    //oldの消去
                    putColor(old.ry, old.rx, old.rollPuyoColor);
                    putColor(old.ay, old.ax, old.axisPuyoColor);
                    //ぷよの描画
                    putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                    putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
                }
            }
        }
    }
    
    return 0;
}

実行結果:
f:id:sion2000114:20191216235901g:plain

ぷよの当たり判定

今現在当たり判定があるのは壁と床のみです。これにぷよを追加します。
ところが壁・床と違い、ぷよはプレイヤーが操作によって任意の場所に置きます。
一定の場所に当たり判定があるわけではないので、設置後の固定されたぷよの座標を取得する必要があります。
まずはその座標の取得から始めましょう。

フィールドの状態を取得

ゲーム開始前に予めフィールドの情報を取得しておくための配列mainFieldを宣言・初期化しておきます。
____puyo.c____(一部)

#include "game.h"

int main(void)
{
    system("clear"); //画面のクリア

    //フィールドの描画
    for(int y = 1; y <= FIELD_HEIGHT; y++){
        for(int x = 1; x <= FIELD_WIDTH; x++){
            if((y != 1) && (x == 1 || x == FIELD_WIDTH || y == FIELD_HEIGHT))
                putColor(y, x, CELL_TYPE_WALL); //壁・床の描画
        }
        printf("\n");
    }
    
    //ぷよの状態をここに格納、壁・床は含めない。
    //簡略のため画面の座標通りに情報を格納していく。
    int mainField[FIELD_HEIGHT - 1][FIELD_WIDTH - 1];
    //初期化
    for(int y = 1; i < FIELD_HEIGHT; y++){
        for(int x = 1 + 2; x < FIELD_WIDTH - 1; x += 2){
            //空白で埋める
            mainField[y][x] = CELL_TYPE_EMPTY;
        }
    }

    //ゲーム部分
    
    while(1){
    ・
    ・
    ・
    (省略)
    ・
    ・
    ・
}

実際に状態を取得するのは設置するときなので、設置のタイミング(2つ目のwhile文)で配列mainFieldを更新していきます。
____puyo.c____(一部)

#include "game.h"

int main(void)
{
    system("clear"); //画面のクリア

    //フィールドの描画
    for(int y = 1; y <= FIELD_HEIGHT; y++){
        for(int x = 1; x <= FIELD_WIDTH; x++){
            if((y != 1) && (x == 1 || x == FIELD_WIDTH || y == FIELD_HEIGHT))
                putColor(y, x, CELL_TYPE_WALL); //壁・床の描画
        }
        printf("\n");
    }
    
    //ぷよの状態をここに格納、壁・床は含めない。
    //簡略のため画面の座標通りに情報を格納していく。
    int mainField[FIELD_HEIGHT - 1][FIELD_WIDTH - 1];
    //初期化
    for(int y = 1; y < FIELD_HEIGHT; y++){
        for(int x = 1 + 2; x < FIELD_WIDTH - 1; x += 2){
            //空白で埋める
            mainField[y][x] = CELL_TYPE_EMPTY;
        }
    }
    
    //ゲーム部分
    while(1){
        //組みぷよの生成
        twinPuyoes_t tsumo = {
            INIT_ROLL_PUYO_X, INIT_ROLL_PUYO_Y,
            INIT_AXIS_PUYO_X, INIT_AXIS_PUYO_Y,
            CELL_TYPE_GREEN,
            CELL_TYPE_RED
        };
    
        //初期位置に設置
        putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
        putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
        
        struct timeval startTime; //組みぷよ生成時の時間
        gettimeofday(&startTime, NULL); //時間を取得
    
        while(1){
            //自由落下
            if(isPassedTime(FREE_FALL_TIME_PER_SQUARE, &startTime)){
                
                //ここが現在設置を担当している
                if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT){
                    //ぷよが設置したら、状態を取得
                    mainField[tsumo.ry][tsumo.rx] = tsumo.rollPuyoColor;
                    mainField[tsumo.ay][tsumo.ax] = tsumo.axisPuyoColor;
                    break;
                }
            
                twinPuyoes_t old; //再描画用
                old = tsumo;
                old.rollPuyoColor = CELL_TYPE_EMPTY;
                old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                tsumo.ry++;
                tsumo.ay++;
            
                //再描画
                //oldの削除
                putColor(old.ry, old.rx, old.rollPuyoColor);
                putColor(old.ay, old.ax, old.axisPuyoColor);
                //ぷよの描画
                putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
            
                //時間をリセット
                gettimeofday(&startTime, NULL);
            }
        
            if(kbhit()){
   ・
   ・
   ・
   (省略)
   ・
   ・
   ・
}

ぷよの当たり判定を追加

壁・床の当たり判定にぷよの当たり判定を追加します。
配列mainFieldを用いて移動先にぷよがあるかどうかを判定します。
CELL_TYPE_EMPTYであれば移動可能ですし、そうでないなら何かしらぷよがあるということになります。

ところがこれには少々問題があります。
落下しているのは「組みぷよ」なので、例えば上側にあるぷよが下へ移動する際の移動先は下側のぷよになってしまうので正常に働きません。
では、どのようにすればいいのか。

今回の問題を解決する手段としては「移動先側にあるぷよを検知」することです。
先程の例では下側のぷよが「移動先側にあるぷよ」という立ち位置になります。
下へ移動しようとするとき、このぷよを判断材料として扱えば正常に動作します。

一足早いのですが、今後のことも視野に入れ組みぷよの回転状態を定義します。
※まだ回転はさせません。あくまでも「移動先側にあるぷよを検知」するために必要なものです。

回転状態は
・無回転(ANGLE_0) = 0
・右に90度回転(ANGLE_90) = 1
・半回転(ANGLE_180) = 2
・左に90度回転(ANGLE_270) = 3
で定義します。
あと一つ、
・処理用アングル(ANGLE_MAX) = 4
を定義しますが、これは後日解説します。
この回転状態は構造体twinPuyoes_tで保持することとします。

____game.h____

・
・
・
(省略)
・
・
・
#define ANGLE_0 0
#define ANGLE_90 1
#define ANGLE_180 2
#define ANGLE_270 3
#define ANGLE_MAX 4 //処理用

typedef struct {
    int rx, ry; //軸でないぷよ(生成時は上側のぷよ)の座標
    int ax, ay; //軸となるぷよ(生成時は下側のぷよ)の座標
    int rollPuyoColor; //軸でないぷよの色情報
    int axisPuyoColor; //軸となるぷよの色情報
    int angle; //回転状態
} twinPuyoes_t;

int kbhit(void);
void putColor(int, int, int);
int isPassedTime(unsigned long int, struct timeval *);

回転状態を定義したので、組みぷよ生成時に回転状態もANGLE_0で初期化します。

組みぷよがANGLE_0の状態なら
・下に移動⇛下側のぷよ
・左右に移動⇛両方のぷよ
を判断材料とすればよくなります。
以下に自由落下時に移動先にぷよがあるかどうかの判定をしたものを書きます。

                //移動先にぷよがあるか判定
                int puyoFlag = FALSE_BOOL; 
                switch(tsumo.angle){
                    case ANGLE_0:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                        
                    case ANGLE_90:
                    case ANGLE_270:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY || mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    case ANGLE_180:
                        if(mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    default: break;
                }

                //移動先が適切でなければ終了
                if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT || puyoFlag == TRUE_BOOL){
                    //ぷよが設置したら、状態を取得
                    mainField[tsumo.ry][tsumo.rx] = tsumo.rollPuyoColor;
                    mainField[tsumo.ay][tsumo.ax] = tsumo.axisPuyoColor;
                    break;
                }

これを基に判定を書き換えて行きましょう!

____puyo.c____

#include "game.h"

int main(void)
{
    system("clear"); //画面のクリア

    //フィールドの描画
    for(int y = 1; y <= FIELD_HEIGHT; y++){
        for(int x = 1; x <= FIELD_WIDTH; x++){
            if((y != 1) && (x == 1 || x == FIELD_WIDTH || y == FIELD_HEIGHT))
                putColor(y, x, CELL_TYPE_WALL); //壁・床の描画
        }
        printf("\n");
    }
    
    //ぷよの状態をここに格納、壁・床は含めない。
    //簡略のため画面の座標通りに情報を格納していく。
    int mainField[FIELD_HEIGHT - 1][FIELD_WIDTH - 1];
    //初期化
    for(int y = 1; y < FIELD_HEIGHT; y++){
        for(int x = 1 + 2; x < FIELD_WIDTH - 1; x += 2){
            //空白で埋める
            mainField[y][x] = CELL_TYPE_EMPTY;
        }
    }
    
    //ゲーム部分
    while(1){
        //組みぷよの生成
        twinPuyoes_t tsumo = {
            INIT_ROLL_PUYO_X, INIT_ROLL_PUYO_Y,
            INIT_AXIS_PUYO_X, INIT_AXIS_PUYO_Y,
            CELL_TYPE_GREEN,
            CELL_TYPE_RED,
            ANGLE_0
        };
    
        //初期位置に設置
        putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
        putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
        
        struct timeval startTime; //組みぷよ生成時の時間
        gettimeofday(&startTime, NULL); //時間を取得
    
        while(1){
            //自由落下
            if(isPassedTime(FREE_FALL_TIME_PER_SQUARE, &startTime)){
                
                //移動先にぷよがあるか判定
                int puyoFlag = FALSE_BOOL; 
                switch(tsumo.angle){
                    case ANGLE_0:
                        if(mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                        
                    case ANGLE_90:
                    case ANGLE_270:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY || mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    case ANGLE_180:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    default: break;
                }
                
                //移動先が適切でなければ終了
                if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT || puyoFlag == TRUE_BOOL){
                    //ぷよが設置したら、状態を取得
                    mainField[tsumo.ry][tsumo.rx] = tsumo.rollPuyoColor;
                    mainField[tsumo.ay][tsumo.ax] = tsumo.axisPuyoColor;
                    break;
                }
            
                twinPuyoes_t old; //再描画用
                old = tsumo;
                old.rollPuyoColor = CELL_TYPE_EMPTY;
                old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                tsumo.ry++;
                tsumo.ay++;
            
                //再描画
                //oldの削除
                putColor(old.ry, old.rx, old.rollPuyoColor);
                putColor(old.ay, old.ax, old.axisPuyoColor);
                //ぷよの描画
                putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
            
                //時間をリセット
                gettimeofday(&startTime, NULL);
            }
        
            if(kbhit()){
                //キーが入力されたかどうかのフラグ
                int keyFlag = FALSE_BOOL;
                //位置を変更するとき、もとの位置のぷよはクリアしたい
                //もとのぷよの情報をここに保持しておく
                twinPuyoes_t old;
            
                switch(getchar()){
                    case 'a':{
                        
                        //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                            case ANGLE_180:
                                if(mainField[tsumo.ry][tsumo.rx - 2] != CELL_TYPE_EMPTY || mainField[tsumo.ay][tsumo.ax - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_90:
                                if(mainField[tsumo.ay][tsumo.ax - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_270:
                                if(mainField[tsumo.ry][tsumo.rx - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;

                            default: break;
                        }
                    
                        //移動先が適切でなければ終了
                        if(tsumo.rx - 2 == FIELD_START_X || tsumo.ax - 2 == FIELD_START_X || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx -= 2;
                        tsumo.ax -= 2;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                    
                    case 'd':{
                    //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                            case ANGLE_180:
                                if(mainField[tsumo.ry][tsumo.rx + 2] != CELL_TYPE_EMPTY || mainField[tsumo.ay][tsumo.ax + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_90:
                                if(mainField[tsumo.ay][tsumo.ax + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_270:
                                if(mainField[tsumo.ry][tsumo.rx + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;

                            default: break;
                        }
                        //移動先が適切でなければ終了
                        if(tsumo.rx + 2 == FIELD_WIDTH || tsumo.ax + 2 == FIELD_WIDTH || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx += 2;
                        tsumo.ax += 2;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                
                    case 's':{
                        //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                                if(mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                        
                            case ANGLE_90:
                            case ANGLE_270:
                                if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY || mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                    
                            case ANGLE_180:
                                if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                    
                            default: break;
                        }
                        
                        //移動先が適切でなければ終了
                        if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.ry++;
                        tsumo.ay++;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                    
                    default: break;
                }
            
                if(keyFlag == TRUE_BOOL){
                    //oldの消去
                    putColor(old.ry, old.rx, old.rollPuyoColor);
                    putColor(old.ay, old.ax, old.axisPuyoColor);
                    //ぷよの描画
                    putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                    putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
                }
            }
        }
    }
    
    return 0;
}

やたらとmain関数が長くなってしまいましたが、ひとまずは実装完了です。
長くなってしまった部分は、気が向けば関数化させてすっきりさせますネ。
実行結果はこの後の色のランダム化を行ってから貼ります。

組みぷよの色のランダム化

ここまでの流れから相対的に見ると最早おまけのようなものです。
組みぷよをランダムに生成するために、initPuyo関数を作成します。

____initPuyo.c____

#include "game.h"

twinPuyoes_t initPuyo(void)
{
    twinPuyoes_t buf;
    
    buf.rx = INIT_ROLL_PUYO_X;
    buf.ry = INIT_ROLL_PUYO_Y;
    buf.ax = INIT_AXIS_PUYO_X;
    buf.ay = INIT_AXIS_PUYO_Y;
    buf.angle = ANGLE_0;
    
    buf.rollPuyoColor = rand() % 4 + 2;
    buf.axisPuyoColor = rand() % 4 + 2;
    
    return buf;
}

ヘッダにプロトタイプ宣言するのを忘れないようにしましょう。

____game.h____

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>

#define TRUE_BOOL 1
#define FALSE_BOOL 0

#define FIELD_HEIGHT 14
#define FIELD_WIDTH 15
#define FIELD_START_X 1

#define INIT_ROLL_PUYO_X 7
#define INIT_ROLL_PUYO_Y 2
#define INIT_AXIS_PUYO_X 7
#define INIT_AXIS_PUYO_Y 3

#define CELL_TYPE_EMPTY 0
#define CELL_TYPE_WALL 1
#define CELL_TYPE_RED 2
#define CELL_TYPE_BLUE 3
#define CELL_TYPE_GREEN 4
#define CELL_TYPE_YELLOW 5
#define CELL_TYPE_PURPLE 6
#define CELL_TYPE_OJAMA 7

#define FREE_FALL_TIME_PER_SQUARE 416000 //自由落下:1マスあたり0.416秒

#define ANGLE_0 0
#define ANGLE_90 1
#define ANGLE_180 2
#define ANGLE_270 3
#define ANGLE_MAX 4 //処理用

typedef struct {
    int rx, ry; //軸でないぷよ(生成時は上側のぷよ)の座標
    int ax, ay; //軸となるぷよ(生成時は下側のぷよ)の座標
    int rollPuyoColor; //軸でないぷよの色情報
    int axisPuyoColor; //軸となるぷよの色情報
    int angle; //回転状態
} twinPuyoes_t;

int kbhit(void);
void putColor(int, int, int);
int isPassedTime(unsigned long int, struct timeval *);
twinPuyoes_t initPuyo(void);

____puyo.c____

#include "game.h"

int main(void)
{
    //乱数の初期化
    srand((unsigned int)time(NULL));
    
    system("clear"); //画面のクリア

    //フィールドの描画
    for(int y = 1; y <= FIELD_HEIGHT; y++){
        for(int x = 1; x <= FIELD_WIDTH; x++){
            if((y != 1) && (x == 1 || x == FIELD_WIDTH || y == FIELD_HEIGHT))
                putColor(y, x, CELL_TYPE_WALL); //壁・床の描画
        }
        printf("\n");
    }
    
    //ぷよの状態をここに格納、壁・床は含めない。
    //簡略のため画面の座標通りに情報を格納していく。
    int mainField[FIELD_HEIGHT - 1][FIELD_WIDTH - 1];
    //初期化
    for(int y = 1; y < FIELD_HEIGHT; y++){
        for(int x = 1 + 2; x < FIELD_WIDTH - 1; x += 2){
            //空白で埋める
            mainField[y][x] = CELL_TYPE_EMPTY;
        }
    }
    
    //ゲーム部分
    while(1){
        //組みぷよの生成
        twinPuyoes_t tsumo = initPuyo();
    
        //初期位置に設置
        putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
        putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
        
        struct timeval startTime; //組みぷよ生成時の時間
        gettimeofday(&startTime, NULL); //時間を取得
    
        while(1){
            //自由落下
            if(isPassedTime(FREE_FALL_TIME_PER_SQUARE, &startTime)){
                
                //移動先にぷよがあるか判定
                int puyoFlag = FALSE_BOOL; 
                switch(tsumo.angle){
                    case ANGLE_0:
                        if(mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                        
                    case ANGLE_90:
                    case ANGLE_270:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY || mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    case ANGLE_180:
                        if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY)
                            puyoFlag = TRUE_BOOL;
                        break;
                    
                    default: break;
                }
                
                //移動先が適切でなければ終了
                if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT || puyoFlag == TRUE_BOOL){
                    //ぷよが設置したら、状態を取得
                    mainField[tsumo.ry][tsumo.rx] = tsumo.rollPuyoColor;
                    mainField[tsumo.ay][tsumo.ax] = tsumo.axisPuyoColor;
                    break;
                }
            
                twinPuyoes_t old; //再描画用
                old = tsumo;
                old.rollPuyoColor = CELL_TYPE_EMPTY;
                old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                tsumo.ry++;
                tsumo.ay++;
            
                //再描画
                //oldの削除
                putColor(old.ry, old.rx, old.rollPuyoColor);
                putColor(old.ay, old.ax, old.axisPuyoColor);
                //ぷよの描画
                putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
            
                //時間をリセット
                gettimeofday(&startTime, NULL);
            }
        
            if(kbhit()){
                //キーが入力されたかどうかのフラグ
                int keyFlag = FALSE_BOOL;
                //位置を変更するとき、もとの位置のぷよはクリアしたい
                //もとのぷよの情報をここに保持しておく
                twinPuyoes_t old;
            
                switch(getchar()){
                    case 'a':{
                        
                        //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                            case ANGLE_180:
                                if(mainField[tsumo.ry][tsumo.rx - 2] != CELL_TYPE_EMPTY || mainField[tsumo.ay][tsumo.ax - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_90:
                                if(mainField[tsumo.ay][tsumo.ax - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_270:
                                if(mainField[tsumo.ry][tsumo.rx - 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;

                            default: break;
                        }
                    
                        //移動先が適切でなければ終了
                        if(tsumo.rx - 2 == FIELD_START_X || tsumo.ax - 2 == FIELD_START_X || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx -= 2;
                        tsumo.ax -= 2;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                    
                    case 'd':{
                    //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                            case ANGLE_180:
                                if(mainField[tsumo.ry][tsumo.rx + 2] != CELL_TYPE_EMPTY || mainField[tsumo.ay][tsumo.ax + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_90:
                                if(mainField[tsumo.ay][tsumo.ax + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                            
                            case ANGLE_270:
                                if(mainField[tsumo.ry][tsumo.rx + 2] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;

                            default: break;
                        }
                        //移動先が適切でなければ終了
                        if(tsumo.rx + 2 == FIELD_WIDTH || tsumo.ax + 2 == FIELD_WIDTH || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.rx += 2;
                        tsumo.ax += 2;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                
                    case 's':{
                        //移動先にぷよがあるか判定
                        int puyoFlag = FALSE_BOOL; 
                        switch(tsumo.angle){
                            case ANGLE_0:
                                if(mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                        
                            case ANGLE_90:
                            case ANGLE_270:
                                if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY || mainField[tsumo.ay + 1][tsumo.ax] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                    
                            case ANGLE_180:
                                if(mainField[tsumo.ry + 1][tsumo.rx] != CELL_TYPE_EMPTY)
                                    puyoFlag = TRUE_BOOL;
                                break;
                    
                            default: break;
                        }
                        
                        //移動先が適切でなければ終了
                        if(tsumo.ry + 1 == FIELD_HEIGHT || tsumo.ay + 1 == FIELD_HEIGHT || puyoFlag == TRUE_BOOL)
                            break;
                        
                        old = tsumo;
                        old.rollPuyoColor = CELL_TYPE_EMPTY;
                        old.axisPuyoColor = CELL_TYPE_EMPTY;
                    
                        tsumo.ry++;
                        tsumo.ay++;
                        keyFlag = TRUE_BOOL;
                        break;
                    }
                    
                    default: break;
                }
            
                if(keyFlag == TRUE_BOOL){
                    //oldの消去
                    putColor(old.ry, old.rx, old.rollPuyoColor);
                    putColor(old.ay, old.ax, old.axisPuyoColor);
                    //ぷよの描画
                    putColor(tsumo.ry, tsumo.rx, tsumo.rollPuyoColor);
                    putColor(tsumo.ay, tsumo.ax, tsumo.axisPuyoColor);
                }
            }
        }
    }
    
    return 0;
}

実行結果:
f:id:sion2000114:20191217000409g:plain

できました!!

おわりに

今回はやることの単純さに反して中々に大変なパートでした。
本来はもっと効率良く動かすようなプログラムを考えるべきなのですが、今はとにかく思いついたことをプログラムに起こしていく事にします。
次回は組みぷよを回転させます。
回転処理はもっと大変そうです。。。
では、お疲れ様でした。