2017년 12월 1일 금요일

②테트리스 강화학습 - 테트리스 구현하기(C++)

Github https://github.com/lyzqm123/Tetris-Reinforcement-Learning
일단 강화학습을 하기위한 기본 환경이 필요하기 때문에 테트리스 게임을 구현해 줍니다.
총 개발시간은 7~8시간 정도 걸린것 같고 약간 수정해야하는 부분이 있긴합니다.

객체지향 공부를 중심적으로 프로그램을 구현했습니다.
확실히 알고리즘을 많이 하다보니 예전에 비해 구현력도 많이 늘었던것 같습니다.



#include "stdafx.h"
int main(){
    //커서 깜빡임 없앰
    CONSOLE_CURSOR_INFO curInfo;
    GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo);
    curInfo.bVisible = 0
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); 
    FILE *tetris_log = fopen("tetris_log.txt""w");
    int test = 1;
    while (1) {
        goCursor(046); printf("<Test Count> : %d", test);
        Tetris *tetris = new Tetris();
        int score = tetris->start();
        fprintf(tetris_log, "<%d> - score : %d\n", test++, score);
        delete tetris;
    }
    return 0;
}
cs
우선 main문입니다. 
Tetris 객체에서 무한히 게임을 돌리는 방식입니다.


#include "ScoreBoard.h"
#include "NextBlcokBoard.h"
#define BASE_Y_SIZE 30
#define BASE_X_SIZE 32
class Tetris {
public:
    Tetris();
    ~Tetris();
    Tetris(int ysize, int xsize);
    int start();
    bool PossibleToMove(Dir dir);        //방향키 입력받았을 때 가능한 지
    bool PossibleToRotate();            //현재 블록을 회전시킬 때 가능한 지
    void ArrayPaint();
    bool ArrayHorizon();
    void BlinkBlock(int cursor_y, int cursor_x);
    void ArrayEraseAndPull(int y);
    void MapErase();
    void MapPrint();
    bool isGameEnd();
private:
    const int map_ysize, map_xsize;
    void BasicPrint();
    Board *score_board;
    Board *next_block_board;
    Block curr_block, next_block;
    bool map[BASE_Y_SIZE][BASE_X_SIZE / 2 + 1];
};
cs
Tetris 클래스는 Board 객체 2개와 Block객체, 함수들을 가집니다.
Board 클래스는 ScoreBoard 클래스와 NextBlockBoard 클래스의 부모 클래스입니다.
각각 점수판, 다음 블록 판에대해 처리를 합니다.
Block 클래스는 블록에대한 정보를 관리합니다.


int Tetris::start() {
    double plus = 0;
    curr_block.SetKind(GetRandomData(06));
    next_block.SetKind(GetRandomData(06));
    dynamic_cast<NextBlcokBoard*>(next_block_board)->SetBlockInfo(next_block);
    next_block_board->BasicPrint();
    score_board->BasicPrint();
    BasicPrint();
    curr_block.Print();
    auto start_time = clock();
    while (dynamic_cast<ScoreBoard*>(score_board)->GetScore() < LIMIT_SCORE) {
        int key = 0;
        for (int n = 0;n < 5;n++) {
            if (_kbhit()) {
                key = _getch();
                if (key == 224) {
                    key = _getch();
                    switch (key) {
                    case LEFT:
                        if (PossibleToMove(Dir::Left)) curr_block.Move(Dir::Left);
                        break;
                    case RIGHT:
                        if (PossibleToMove(Dir::Right)) curr_block.Move(Dir::Right);
                        break;
                    case DOWN:
                        if (PossibleToMove(Dir::Down)) curr_block.Move(Dir::Down);
                        break;
                    case UP:
                        if (PossibleToRotate()) curr_block.Rotate();
                        break;
                    defaultbreak;
                    }
                }
                else if (key == SPACE) {
                    while (PossibleToMove(Dir::Down)) curr_block.Move(Dir::Down);
                    while (_kbhit()) _getch();
                    break;
                }
                while (_kbhit()) _getch();
            }
        }
        auto end_time = clock();
        if ((long double)(end_time - start_time) / (long double)CLOCKS_PER_SEC >= GAME_VELOCITY - plus) {
            start_time = clock();
            if (PossibleToMove(Dir::Down)) {
                curr_block.Move(Dir::Down);
            }
            else if (isGameEnd()) return dynamic_cast<ScoreBoard*>(score_board)->GetScore();
            else {
                curr_block.Print(1);
                ArrayPaint();
                while (ArrayHorizon()) {
                    dynamic_cast<ScoreBoard*>(score_board)->SetScore(PLUS_SCORE);
                    plus += DIFFICULTY_WEIGHT;
                }
                curr_block.SetKind(next_block.GetKind());
                next_block.SetKind(GetRandomData(06));
                dynamic_cast<NextBlcokBoard*>(next_block_board)->SetBlockInfo(next_block);
                next_block_board->DataPrint();
            }
        }
    }
    return LIMIT_SCORE;
}
cs
start함수에서 모든과정이 시작하며 반복되며 끝이납니다.
초기에 현재 블록과 다음 블록을 랜덤하게 정해주고 기본 그림들을 그려줍니다.
키보드 입력값이 존재하고 가능하다면 처리를 해줍니다.
일정 시간이 흐르면 자동으로 한칸씩 내려오도록 만들었는데
이것을 이용해서 게임 난이도를 조절합니다.

현재 게임점수와 난이도를 비례하도록 만들었습니다. 

댓글 없음:

댓글 쓰기