#include <iostream>
#include <cstdlib>   // rand()
#include <conio.h>   // _getch()
#include <windows.h> // Sleep()
using namespace std;
void cursor(int x, int y); // 関数プロトタイプ宣言

const int CELL_W = 24; // 迷路のx方向のマスの数
const int CELL_H = 13; // 迷路のy方向のマスの数
const int MON_MAX = 3; // モンスターの数
enum { UP, DOWN, LEFT, RIGHT }; // 進む向きを列挙定数とする

char maze[CELL_H][CELL_W + 1] = { // 文字列配列で迷路を定義
    "########################",
    "#     G   #            #",
    "# ##### # # # ###### # #",
    "# #     # # #      # #G#",
    "#G# ##### # ###### # # #",
    "#          G         #G#",
    "### ##### # ###### # # #",
    "#  G    #          # #G#",
    "# # ####### ###### # # #",
    "# #                #   #",
    "# # # ##### ########## #",
    "#   #         G G G    #",
    "########################"
};

class Character { // キャラクター用のクラス定義
private: // メンバ変数を非公開とする
    int x;
    int y;
    int dir;
public: // メンバ関数を公開する
    Character(int x, int y, int dir) { // コンストラクタ
        this->x = x;
        this->y = y;
        this->dir = dir;
    }
    int getX() { return this->x; } // xの値を返すゲッタ
    int getY() { return this->y; } // yの値を返すゲッタ
    void setDir(int dir) { this->dir = dir; } // 向きを代入するセッタ
    bool move() { // 移動処理
        int nx = this->x, ny = this->y; // 現在の座標をnxとnyに代入
        if (this->dir == UP) ny -= 1; // 上に進む
        if (this->dir == DOWN) ny += 1; // 下に進む
        if (this->dir == LEFT) nx -= 1; // 左に進む
        if (this->dir == RIGHT) nx += 1; // 右に進む
        if (maze[ny][nx] != '#') { // 移動先が壁ではない
            this->x = nx;
            this->y = ny;
            return true;
        }
        return false;
    }
    void draw(char c) { // キャラクターの描画（文字の出力）
        cursor(this->x, this->y);
        cout << c;
    }
};

void cursor(int x, int y) { // カーソル位置を指定する関数
    cout << "\x1b[" << y + 1 << ";" << x + 1 << "H";
}

int main() { // main関数
    int score = 0; // スコアを代入する変数
    int timer = 0; // 時間（フレーム数）を数える変数
    Character player = { 1, 1, UP }; // プレイヤーのキャラクター用のオブジェクト
    Character monster[MON_MAX] = { // モンスター用のオブジェクトの配列
        { 22,  1, UP   },
        {  1, 11, DOWN },
        { 22, 11, LEFT }
    };

    while (true) { // 無限ループで繰り返す
        timer++; // フレーム数をカウント

        cursor(0, 0); // カーソル位置を左上角にする
        for (int i = 0; i < CELL_H; i++) cout << maze[i] << endl; // 迷路を表示

        // プレイヤーの処理
        if (_kbhit()) { // キー入力があった時
            char key = _getch();
            if (key == 72) player.setDir(UP); // 上キー
            if (key == 80) player.setDir(DOWN); // 下キー
            if (key == 75) player.setDir(LEFT); // 左キー
            if (key == 77) player.setDir(RIGHT); // 右キー
            if (key == 72 || key == 80 || key == 75 || key == 77) player.move();
        }
        if (maze[player.getY()][player.getX()] == 'G') { // 宝に載った
            maze[player.getY()][player.getX()] = ' ';
            score += 100;
        }
        player.draw('B'); // プレイヤーキャラを表示

        // モンスターの処理
        bool caught = false; // モンスターに捕まったか
        for (int i = 0; i < MON_MAX; i++) {
            if (timer % 2 == 0) { // 1フレームおきに動かす
                // 移動処理を行い、壁にぶつかったらランダムに向きを変える
                if (monster[i].move() == false) monster[i].setDir(rand() % 4);
            }
            monster[i].draw('D'); // モンスターを表示
            if (player.getX() == monster[i].getX() && player.getY() == monster[i].getY()) caught = true; // ヒットチェック
        }

        if (timer % 30 == 0) { // 定期的に宝が出現
            int x = 1 + rand() % (CELL_W - 2);
            int y = 1 + rand() % (CELL_H - 2);
            if (maze[y][x] == ' ') maze[y][x] = 'G';
        }

        cursor(0, 14);
        cout << "SCORE " << score; // スコア
        if (caught) break; // モンスターに捕まったらwhileを抜ける
        Sleep(100); // 一定時間待って繰り返す
    }

    // ゲームオーバー時に表示
    cursor(0, 16);
    cout << "GAME OVER" << endl;
    Sleep(3000); // 一定時間待って処理を終える
}