awkでライフゲーム(リアルタイム処理)

以前のブログの記事で、ライフゲームアルゴリズムを紹介しました。

utthi-fumi.hatenablog.com

utthi-fumi.hatenablog.com

今回はawkライフゲームをリアルタイムで動くプログラムを作りたいと思います。

BEGIN{
    # [2]定数を定義
    FIELD_WIDTH = 12;      # [2-1]フィールドの幅を定義する
    FIELD_HEIGHT = 12      # [2-2]フィールドの高さを定義する
    FPS = 1000;              # [2-3]1秒当たりの更新回数を定義する
    INTERVAL = 1000 / FPS; # [2-4]更新間隔(ミリ秒)を定義する

    # [3]変数を定義
    field[0][0] = 0;field[0][1] = 1;field[0][2] = 0;
    field[1][0] = 0;field[1][1] = 0;field[1][2] = 1;
    field[2][0] = 1;field[2][1] = 1;field[2][2] = 1;

    lastClock = systime();  # [4-5-5]前回の経過時間を宣言する

    while(1){
        newClock = systime();   # [4-5-7]現在の経過時間を宣言する
        # [4-5-8]前回の経過時間から、待機時間が経過していなければ
        if(newClock < lastClock + INTERVAL){
            continue;   # [4-5-9]待機時間に戻る
        }
        # [4-5-10]前回経過時間を現在の経過時間で更新する
        lastClock = newClock;
        DrawField();    # [4-5-11]フィールドを描画する関数を呼び出す
        # getch();        # [4-5-12]キーボード入力を待つ
        StepSimulation();   # [4-5-13]シミュレーションを進める

    }
}


# [4] 関数を宣言

function cls(){
    printf("\033[H\033[J");
}

function getch(     cmd,k){
    cmd="bash -c 'read -r -s -n 1 ke;echo $ke'"
    cmd | getline k;
    close(cmd)
    fflush()
    return k
}

# [4-1]フィールドを描画する関数
function DrawField(){
    cls();  # [4-1-1]画面をクリアする
    # [4-1-2]フィールドのすべての行を反復する
    for(y = 0; y < FIELD_HEIGHT; y++){
        # [4-1-3]フィールドのすべての列を反復する
        for(x = 0; x < FIELD_WIDTH; x++){
            # [4-1-4]セルが生きていれば「■」を、死んでいれば「 」を描画する
            printf("%s",field[y][x] ? "■" : " ");
        }
        printf("\n");   # [4-1-5]1行描画するごとに改行する
    }
}

# [4-2]対象のセルと隣接する生きたセルの数を取得する関数
function GetLivingCellscount(_x,_y,     x,y){
    count = 0;  # [4-2-1]生きているセルを数えるカンターを宣言
    # [4-2-2]対象のセルの上下1マスを反復する
    for(y = _y - 1; y <= _y + 1; y++){
        # [4-2-3]上下にループさせない場合は、行が範囲内かどうかを判定する
        # if((y < 0) || (y >= FIELD_HEIGHT)){
        #     continue;   # [4-2-4]範囲外の行なのでスキップする
        # }
        # [4-2-5]上下にループしたY座標を宣言
        roopedY = (FIELD_HEIGHT + y) % FIELD_HEIGHT;
        #print "y=",y,"roopedY=",roopedY,"x=",x,"roopedX=",roopedX,"count=",count;
        # [4-2-6]対象のセルの左右1マスを反復する
        for(x = _x + -1; x <= _x + 1; x++){
            # [4-2-7]左右にループさせない場合は、列が範囲内かどうかを判定する
        #     if((x < 0) || (x >= FIELD_WIDTH)){
        #         continue;   # [4-2-8]範囲外の列なのでスキップする
        #     }
            # [4-2-9]左右にループしたX座標を宣言する
            roopedX = (FIELD_WIDTH + x) % FIELD_WIDTH;
            # [4-2-10]対象の座標が、中心のセルと同じかどうかを判定する
            if((roopedX == _x) && (roopedY == _y)){
                continue;   # [4-2-11]対象の座標をスキップする
            }
            # [4-2-12]対象のセルが生きていれば1を、死んでいれば0を加算する
            count += field[roopedY][roopedX];
        }
    }
    return count;   # [4-2-13]生きているセルの数を返す
}
# [4-3]1ステップ分のシミュレーションを実行する関数
function StepSimulation(){
    # [4-3-2]すべての行を反復する
    for(y = 0; y < FIELD_HEIGHT; y++){
        # [4-3-3]すべての列を反復する
        for(x = 0; x < FIELD_WIDTH; x++){
            # [4-3-4]対象のセルと隣接する、生きているセルの数を宣言
            livingCellCount = GetLivingCellscount(x,y);
            # [4-3-5]隣接する生きたセルの数で分岐する
            if(livingCellCount <= 1){   # [4-3-5]1個なら
                # [4-3-6]対象のセルを死滅させる
                nextField[y][x] = 0;
            }else if(livingCellCount == 2){ # [4-3-7]2個なら
                # [4-3-8]現状維持
                nextField[y][x] = field[y][x];
            }else if(livingCellCount == 3){ # [4-3-9]3個なら
                # [4-3-10]対象のセルを誕生/生存させる
                nextField[y][x] = 1;
            }else{  # [4-3-11]4つ以上なら
                # [4-3-12]対象のセルを死滅させる
                nextField[y][x]  = 0;
            }
        }
    }
    for(y = 0; y < FIELD_HEIGHT; y++){
        for(x = 0; x < FIELD_WIDTH; x++){
            field[y][x] = nextField[y][x];
        }
    }
}

awkで大きなプログラムを作る場合、変数をローカル変数になっているかどうか、きちんと確認する必要があります。
(私もこれにハマってしまい、バグを見つけるのに苦労しました)

utthi_fumi is awker!