awkでRPGゲーム

ゲームでプログラミングとなると、大抵はjavascriptで作ることが多いと思います。
キーボード操作やマウス操作が絡んできますからね。
awkでも、リアルタイムでキーボード操作ができれば、ゲームを作ることができます。
キーボード操作そのものはシェルスクリプトになります。
簡単なRPGゲーム(?)を作成してみました。
いきなり、ドラクエの最後のボスとの直接対決からスタートします。
wキーが上に移動、sキーが下に移動します。
ぜひ、最後の魔王を倒して、世界に平和をもたらしてください。

#! /usr/bin/gawk -f
# rpg.awk


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

#[6-1]ゲームを初期化する関数を宣言する
function Init(){
    #[6-1-1]プレイヤーのステータスを初期化する
    characters[CHARACTER_PLAYER]["hp"]    = monsters[MONSTER_PLAYER]["hp"];
    characters[CHARACTER_PLAYER]["maxHp"] = monsters[MONSTER_PLAYER]["maxHp"];
    characters[CHARACTER_PLAYER]["mp"]    = monsters[MONSTER_PLAYER]["mp"];
    characters[CHARACTER_PLAYER]["maxMp"] = monsters[MONSTER_PLAYER]["maxMp"];
    characters[CHARACTER_PLAYER]["attack"] = monsters[MONSTER_PLAYER]["attack"];
    characters[CHARACTER_PLAYER]["name"]  = monsters[MONSTER_PLAYER]["name"];

}

#[6-2]戦闘シーンの画面を描画する関数を宣言する
function DrawBattleScreen(){
    #[6-2-1]画面をクリアする
    cls();
    # [6-2-2]プレイヤーの名前を表示する
    printf("%s\n",characters[CHARACTER_PLAYER]["name"]);
    # [6-2-3]プレイヤーのステータスを表示する
    printf("HP:%d%d MP:%d%d\n",
           characters[CHARACTER_PLAYER]["hp"],
           characters[CHARACTER_PLAYER]["maxHp"],
           characters[CHARACTER_PLAYER]["mp"],
           characters[CHARACTER_PLAYER]["maxMp"]);
    #[6-2-4]1行空ける
    printf("\n");
    #[6-2-5]モンスターのアスキーアートを描画する
    printf("%s",characters[CHARACTER_MONSTER]["aa"]);
    #[6-2-6]モンスターのHPを表示する
    printf("(HP:%d%d)\n",
           characters[CHARACTER_MONSTER]["hp"],
           characters[CHARACTER_MONSTER]["maxHp"]);
    #[6-2-7]1行空ける
    printf("\n");
}
#[6-3]コマンドを選択する関数を宣言する
function SelectCommand(){
    #[6-3-1]プレイヤーのコマンドを初期化する
    characters[CHARACTER_PLAYER][character_command] = 0;
    #character_command = 0;
    #[6-3-2]コマンドが決定するまでループする
    while(1){
        #[6-3-3]戦闘画面を描画する関数を呼び出す
        DrawBattleScreen();
        #[6-3-4]コマンドの一覧を表示する
        for(i = 0; i < COMMAND_MAX; i++){
            #[6-3-5]選択中のコマンドなら
            if(i == characters[CHARACTER_PLAYER][character_command]){
                #[6-3-6]カーソルを描画する
                printf(">");
            }else{  #[6-3-7]選択中のコマンドでなければ
                printf(" ");
            }
            #[6-3-9]コマンドの名前を表示する
            printf("%s\n",commandNames[i]);
        }
        #[6-3-10]入力されたキーによって分岐する
        switch(getch()){
            case "w":   #[6-3-11]wキーが押されたら
            #[6-3-12]上のコマンドに切り替える
            characters[CHARACTER_PLAYER][character_command]--;
            #character_command--;
            break;
            case "s":   #[6-3-13]sキーが押されたら
            #[6-3-14]下のコマンドに切り替える
            characters[CHARACTER_PLAYER][character_command]++
            #character_command++;
            break;
            default:#[6-3-15]上記以外のキーが押されたら
            return; #[6-3-16]関数を抜ける

        }
        #[6-3-17]カーソルを上下にループさせる
        characters[CHARACTER_PLAYER][character_command] = (COMMAND_MAX + characters[CHARACTER_PLAYER][character_command]) % COMMAND_MAX;
    }
}

#[6-4]戦闘シーンの関数を宣言する
function Battle(_monster){
    #[6-4-1]モンスターのステータスを初期化する
    characters[CHARACTER_MONSTER]["hp"]    = monsters[_monster]["hp"];
    characters[CHARACTER_MONSTER]["maxHp"] = monsters[_monster]["maxHp"];
    characters[CHARACTER_MONSTER]["mp"]    = monsters[_monster]["mp"];
    characters[CHARACTER_MONSTER]["maxMp"] = monsters[_monster]["maxMp"];
    characters[CHARACTER_MONSTER]["name"]  = monsters[_monster]["name"];
    characters[CHARACTER_MONSTER]["attack"]  = monsters[_monster]["attack"];
    characters[CHARACTER_MONSTER]["aa"]    = monsters[_monster]["aa"];

    #[6-4-2]プレイヤーの攻撃対象をモンスターに設定する
    characters[CHARACTER_PLAYER]["target"] = CHARACTER_MONSTER;
    #[6-4-3]モンスターの攻撃対象をプレイヤーに設定する
    characters[CHARACTER_MONSTER]["target"] = CHARACTER_PLAYER;
    #[6-4-3]モンスターの攻撃対象をプレイヤーに設定する

    #[6-4-4]戦闘シーンの画面を描画する関数を呼び出す
    DrawBattleScreen();
    #[6-4-5]戦闘シーンの最初のメッセージを表示する
    printf("%sがあらわれた!\n",characters[CHARACTER_MONSTER]["name"]);
    #[6-4-6]キーボード入力を待つ
    getch();
    #[6-4-7]戦闘シーンが終了するまでループする
    while(1){
        #[6-4-8]コマンドを選択する関数を呼び出す
        SelectCommand();
        #[6-4-9]各キャラクターを反復する
        for(i = 0; i < CHARACTER_MAX; i++){
            #[6-4-10]戦闘シーンの画面を描写する関数を呼び出す
            DrawBattleScreen();
            switch(characters[i][character_command]){
                case 0:    #[6-4-12]戦う
                printf("%sのこうげき!\n",characters[i]["name"]);
                getch();
                #[6-4-15]敵に与えるダメージを計算する
                damage = 1 + int(rand() * 1000) % characters[i]["attack"];
                #[6-4-16]敵にダメージを与える
                characters[characters[i]["target"]]["hp"] -= damage;
                #[6-4-17]敵のHPが負の値になったかどうかを判定する
                if(characters[characters[i]["target"]]["hp"] < 0){
                    #[6-4-18]敵のHPを0にする
                    characters[characters[i]["target"]]["hp"] = 0;
                }
                #[6-4-19]戦闘シーンの画面を再描画する関数を呼び出す
                DrawBattleScreen();
                #[6-4-20:敵にダメージを与えたメッセージを表示する
                printf("%s%dのダメージ!\n",\
                       characters[characters[i]["target"]]["name"],\
                       damage);
                #[6-4-21]キーボード入力を待つ
                getch();
                break;
                case 1:    #[6-4-22]回復
                #[6-4-23]MPが足りるかどうかを判定する
                if(characters[i]["mp"] < SPELL_COST){
                    #[6-4-24]MPが足りないメッセージを表示する
                    printf("MPがたりない!\n");
                    #[6-4-25]キーボード入力を待つ
                    getch();
                    #[6-4-26]呪文を唱える処理を抜ける
                    break;
                }
                #[6-4-27]MPを消費させる
                characters[i]["mp"] -= SPELL_COST;
                #[6-4-28]画面を再描画する
                DrawBattleScreen();
                #[6-4-29]回復するメッセージを表示する
                printf("%sは回復することにした!\n",characters[i]["name"]);
                #[6-4-30]キーボード入力を待つ
                getch();
                #[6-4-31]HPを回復させる
                characters[i]["hp"] = characters[i]["maxHp"];
                #[6-4-32]戦闘シーンの画面を再描画する
                DrawBattleScreen();
                #[6-4-33]HPが回復したメッセージを表示する
                printf("%sのきずがかいふくした!\n",characters[i]["name"]);
                #[6-4-34]キーボード入力を待つ
                getch();
                break;
                case 2:    #[6-4-35]逃げる
                #[6-4-36]逃げ出したメッセージを表示する
                printf("%sはにげだした!\n",characters[i]["name"]);
                #[6-4-37]キーボード入力を待つ
                getch();
                #[6-4-38]戦闘処理を抜ける
                return;
                break;
            }
            #[6-4-39]攻撃対象を倒したかどうかを判定する
            if(characters[characters[i]["target"]]["hp"] <= 0){
                #[6-4-40]攻撃対象によって処理を分岐させる
                switch(characters[i]["target"]){
                    #[6-4-41]プレイヤーなら
                    case 0:
                    #[6-4-42]プレイヤーが死んだメッセージを表示する
                    printf("あなたはしにました");
                    break;
                    #[6-4-43]モンスターなら
                    case 1:
                    #[6-4-44]モンスターのアスキーアートを何も表示しないよう書き換える
                    characters[characters[i]["target"]]["aa"] = "";
                    #[6-4-45]戦闘シーンの画面を再描画する関数を呼び出す
                    DrawBattleScreen();

                    #[6-4-46]モンスターを倒したメッセージを表示する
                    printf("%sをたおした!\n",\
                           characters[characters[i]["target"]]["name"]);
                    break;
                }
                #[6-4-47]キーボード入力を待つ
                getch();
                #[6-4-48]戦闘シーンの関数を抜ける
                return;
            }
        }
    }
}

#[6-6]プログラムの実行開始点を宣言
BEGIN{
    # [5]変数を定義

    MONSTER_PLAYER = 0; #[3-1-1]プレイヤー
    MONSTER_SLIME  = 1; #[3-1-2]スライム
    MONSTER_BOSS   = 2; #[3-1-3]魔王
    MONSTER_MAX    = 3; #[3-1-4]モンスターの種類の数

    monsters[MONSTER_PLAYER]["hp"]     = 100;
    monsters[MONSTER_PLAYER]["maxHp"]  = 100;
    monsters[MONSTER_PLAYER]["mp"]     = 15;
    monsters[MONSTER_PLAYER]["maxMp"]  = 15;
    monsters[MONSTER_PLAYER]["attack"] = 30;
    monsters[MONSTER_PLAYER]["name"]   = "ゆうしゃ";

    monsters[MONSTER_SLIME]["hp"]     = 3;
    monsters[MONSTER_SLIME]["maxHp"]  = 3;
    monsters[MONSTER_SLIME]["mp"]     = 0;
    monsters[MONSTER_SLIME]["maxMp"]  = 0;
    monsters[MONSTER_SLIME]["attack"] = 2;
    monsters[MONSTER_SLIME]["name"]   = "スライム";
    monsters[MONSTER_SLIME]["aa"]     = "/・Д・\\n"\
                                        "~~~~~~";#アスキーアート
    monsters[MONSTER_BOSS]["hp"]      = 255;
    monsters[MONSTER_BOSS]["maxHp"]   = 255;
    monsters[MONSTER_BOSS]["mp"]      = 0;
    monsters[MONSTER_BOSS]["maxMp"]   = 0;
    monsters[MONSTER_BOSS]["attack"]  = 50;
    monsters[MONSTER_BOSS]["name"]    = "まおう";
    monsters[MONSTER_BOSS]["aa"]      = "  A@A\n"\
                                        "~(▼皿▼)~";


    CHARACTER_PLAYER  = 0;   #[3-2-1]プレイヤー
    CHARACTER_MONSTER = 1;   #[3-2-2]モンスター
    CHARACTER_MAX     = 2;   #[3-2-3]キャラクターの種類


    character_command = 0;

    COMMAND_MAX = 3; #コマンドの種類の数

    # [5-3]コマンドの名前を宣言する
    commandNames[0] = "たたかう";   #[5-3-1]
    commandNames[1] = "かいふく";   #[5-3-2]
    commandNames[2] = "にげる";     #[5-3-3]

    SPELL_COST = 3; #呪文の消費MPを定義する

    #[6-6-1]乱数をシャッフルする
    srand();


    # [6-6-2]ゲームを初期化する関数を呼び出す
    Init();

    # [6-6-3]戦闘シーンの関数を呼び出す
    Battle(MONSTER_BOSS);
    # [6-6-4]キーボード入力を待つ
    getch();

}

私自身、awkでここまで大掛かりなプログラミングをしたのは初めてで、かなり苦労しました。
しかし、awkでもここまで出来ることが分かったのではないでしょうか?

utthi_fumi is game programmer!