Sablog Models/자작자작

환타 오목 게임 재구성. (2)

어­리 2009. 6. 7. 21:10

지난번 글에서는 move() 함수는 'move'답게 만들고

다른 함수가 move()를 반복하여 한 판을 조정하여,

main()에서는 판을 반복하게 했습니다.


물론 제가 five_in_a_row()에 단 주석과 약간 다릅니다.

제 주석에 따르면 플레이어가 재경기를 원할 때 재경기를 시켜 줘야 합니다.

게다가 원래 게임과도 약간 달라져 버렸지요.

그.러.나.


그 점은 미뤄 두고, 일단 다른 부분을 시작하도록 하죠. (-_-ㅗ;;)


이번에는 판을 그리는 부분을 바꾸어 보겠습니다.

switch-case 부분이 너무 길어서 저 내부를 한 함수로 빼낼 수 있으면 좋겠군요.

#define BOARD_SIZE 19

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}


판 크기는 맨 위에 따로 정의한 후,

전각 문자의 크기로 인한 문제는 board_write()에서 해결하도록 합니다.

move()에서는 x도 한 칸씩만 움직이게 해야겠습니다.

그러면 check()에서도 돌이 일렬로 있는지 검사하기 위해 x를 1씩만 움직여도 되고,

코드를 오히려 줄일 수 있습니다.


또한 move()의 switch-case의 각 방향키 case 안에서,

필요 없는 연산을 수행하고 있습니니다.

예를 들자면,

y += 1;
oldy = y - 1;

이 있겠습니다. 이런 건,

oldy = y;
y++;
(더 줄여서 oldy = y++;)

로 만들 수 있습니다.

이런 식으로 코드를 2차 개선하면 five_in_a_row.h는 아래와 같이 바뀝니다.

/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13

#define BOARD_SIZE 19

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[BOARD_SIZE][BOARD_SIZE];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"};

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 1 && board[px-1][y] == turn)
        px--;
    while(px < BOARD_SIZE && board[px][y] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 세로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py < BOARD_SIZE && board[x][py++] == turn)
        count++;

    if(count == 5)
        return 1;


    // 좌상우하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-1][py-1] == turn)
        px--, py--;
    while(px < BOARD_SIZE && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 우상좌하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px < BOARD_SIZE && py > 0 && board[px+1][py-1] == turn)
        px++, py--;
    while(px >= 0 && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px--;
        count++;
    }

    if(count == 5)
        return 1;

    return 0;
}

int knock(int x, int y)
{
    if(board[x][y] != 0)
    {
        gotoxy(x * 2, y);
        puts(stone[board[x][y]]);
        return 0;
    }
    else
        return 1;
}

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}

void draw()
{        
    int i, j;
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board_write(i, j);
}

int move()
{
    char input = 0;
    input = getch();

    switch(input)
    {
    case UP :
        if(y > 0)
        {
            oldy = y--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case DOWN :
        if(y < BOARD_SIZE - 1)
        {
            oldy = y++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case LEFT :
        if(x > 0)
        {
            oldx = x--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);
        }
        return 1;
        break;

    case RIGHT :
        if(x < BOARD_SIZE - 1)
        {
            oldx = x++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);

        }
        return 1;
        break;

    case ENTER :
        if(board[x][y] == 0)
        { 
            board[x][y] = turn;
            gotoxy(x * 2, y);
            puts(stone[turn]);
            if(check(x, y, turn))
            {
                return 0;
            }
            turn++;
            if(turn == 3)
                turn = 1;
        }
        return 1;
        break;
    }
    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board[i][j] = 0;
    while (move());
    system("CLS");
    printf("%s 승리\n", who[turn]);
    system("PAUSE");

    return 0;
}


#endif

뭐 내친 김에 draw()도 write_board()를 이용하도록 바꾸어 버렸습니다.


이번에 move() 바꾸는 게 가장 중요한 작업인데 어느 정도 성공입니다.

지난번 20줄 정도 늘렸던 게 이번에 거기서 110줄 정도 줄었군요.

뭐 C 파일에서 분리하고, 헤더 하나 더 만든 것까지 포함하면 아직 멀었지만...



다음에는 check()을 좀 바꿔 봐야겠습니다.