N Log

가위, 바위, 보

문제

다음 조건을 만족하는 가위바위보 게임을 작성하시오.

사용자는 바위, 가위, 보, 종료 중 하나를 숫자로 입력한다.
게임은 사용자가 종료를 입력할 때까지 반복된다.

컴퓨터는 가위, 바위, 보 중 하나를 임의로 선택한다.
사용자의 선택과 컴퓨터의 선택을 비교하여 승리, 패배, 무승부를 출력한다.

유효하지 않은 값을 입력하면 유효하지 않은 입력임을 출력하고 다시 입력을 받는다.

풀이

가위, 바위, 보는 조건문으로 풀 수 있는 단순한 문제다.
하지만 가독성을 위해 enum을 도입하거나, 승패 판단 규칙을 lookup table로 정리해 볼 여지가 있다.

하드 코딩

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    srand((unsigned int)time(NULL));

    while (1)
    {
        int user_choice;

        printf("Rock(1), Scissors(2), Paper(3), Exit(0): ");

        if (scanf("%d", &user_choice) != 1)
        {
            int buffer_char;

            while ((buffer_char = getchar()) != '\n' && buffer_char != EOF)
                ;

            printf("Please enter a number.\n");
            continue;
        }

        if (user_choice == 0)
            break;

        if (user_choice < 1 || user_choice > 3)
        {
            printf("Invalid choice. Please choose 1, 2, or 3.\n");
            continue;
        }

        int computer_choice = rand() % 3 + 1;

        printf("User: ");
        if (user_choice == 1)
            printf("Rock");
        else if (user_choice == 2)
            printf("Scissors");
        else
            printf("Paper");

        printf(", Computer: ");
        if (computer_choice == 1)
            printf("Rock");
        else if (computer_choice == 2)
            printf("Scissors");
        else
            printf("Paper");
        printf("\n");

        if (user_choice == 1)
        {
            if (computer_choice == 1)
                printf("Draw.\n");
            else if (computer_choice == 2)
                printf("You win.\n");
            else
                printf("You lose.\n");
        }
        else if (user_choice == 2)
        {
            if (computer_choice == 1)
                printf("You lose.\n");
            else if (computer_choice == 2)
                printf("Draw.\n");
            else
                printf("You win.\n");
        }
        else
        {
            if (computer_choice == 1)
                printf("You win.\n");
            else if (computer_choice == 2)
                printf("You lose.\n");
            else
                printf("Draw.\n");
        }
    }

    return 0;
}

입력값을 0, 1, 2, 3 같은 숫자 리터럴만으로 다루면 user_choice == 2를 보았을 때 2가 무엇을 의미하는지 바로 떠올리기 어렵다.
또한 지금 코드는 사용자가 가위인 경우, 바위인 경우, 보인 경우를 각각 나누고, 그 안에서 컴퓨터의 선택을 다시 비교해 승패를 판단한다.
즉 승패 판단에 필요한 경우의 수를 일일이 나열하는 형태라 코드가 길어지고 가독성이 떨어진다.

enum 활용

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

enum Choice
{
    CHOICE_EXIT = 0,
    CHOICE_ROCK,
    CHOICE_SCISSORS,
    CHOICE_PAPER
};

int main(void)
{
    srand((unsigned int)time(NULL));

    while (1)
    {
        int user_input;

        printf("Rock(1), Scissors(2), Paper(3), Exit(0): ");

        if (scanf("%d", &user_input) != 1)
        {
            int buffer_char;

            while ((buffer_char = getchar()) != '\n' && buffer_char != EOF)
                ;

            printf("Please enter a number.\n");
            continue;
        }

        if (user_input == CHOICE_EXIT)
            break;

        if (user_input < CHOICE_ROCK || user_input > CHOICE_PAPER)
        {
            printf("Invalid choice. Please choose 1, 2, or 3.\n");
            continue;
        }

        enum Choice user_choice = (enum Choice)user_input;
        enum Choice computer_choice = (enum Choice)(rand() % 3 + 1);

        printf("User: ");
        if (user_choice == CHOICE_ROCK)
            printf("Rock");
        else if (user_choice == CHOICE_SCISSORS)
            printf("Scissors");
        else
            printf("Paper");

        printf(", Computer: ");
        if (computer_choice == CHOICE_ROCK)
            printf("Rock");
        else if (computer_choice == CHOICE_SCISSORS)
            printf("Scissors");
        else
            printf("Paper");
        printf("\n");

        if (user_choice == computer_choice)
            printf("Draw.\n");
        else if ((user_choice == CHOICE_ROCK && computer_choice == CHOICE_SCISSORS) ||
                 (user_choice == CHOICE_SCISSORS && computer_choice == CHOICE_PAPER) ||
                 (user_choice == CHOICE_PAPER && computer_choice == CHOICE_ROCK))
            printf("You win.\n");
        else
            printf("You lose.\n");
    }

    return 0;
}

enum을 사용하면 숫자 리터럴 대신 의미 있는 이름으로 값을 비교할 수 있다.
기존에는 사용자의 선택 3가지와 컴퓨터의 선택 3가지를 조합한 9가지 경우를 조건문으로 모두 나열했다.
이제는 무승부와 사용자가 이기는 세 가지 경우만 확인하고, 나머지는 패배로 처리해 코드가 간결해지고 가독성이 높아졌다.

Lookup Table 활용

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define USER_INPUT_EXIT 0

enum Hand
{
    HAND_ROCK = 0,
    HAND_SCISSORS,
    HAND_PAPER,
    HAND_COUNT
};

int main(void)
{
    const char* hand_names[HAND_COUNT] = {
        "Rock",
        "Scissors",
        "Paper"
    };

    const char* result_table[HAND_COUNT][HAND_COUNT] = {
        /*               Rock        Scissors     Paper */
        /* Rock */     {"Draw.",     "You win.", "You lose."},
        /* Scissors */ {"You lose.", "Draw.",    "You win."},
        /* Paper */    {"You win.",  "You lose.", "Draw."}
    };

    srand((unsigned int)time(NULL));

    while (1)
    {
        int user_input;

        printf("Rock(1), Scissors(2), Paper(3), Exit(%d): ", USER_INPUT_EXIT);

        if (scanf("%d", &user_input) != 1)
        {
            int buffer_char;

            while ((buffer_char = getchar()) != '\n' && buffer_char != EOF)
                ;

            printf("Please enter a number.\n");
            continue;
        }

        if (user_input == USER_INPUT_EXIT)
            break;

        if (user_input < 1 || user_input > HAND_COUNT)
        {
            printf("Invalid choice. Please choose 1, 2, or 3.\n");
            continue;
        }

        enum Hand user_hand = (enum Hand)(user_input - 1);
        enum Hand computer_hand = (enum Hand)(rand() % HAND_COUNT);

        printf("User: %s, Computer: %s\n",
               hand_names[user_hand],
               hand_names[computer_hand]);
        printf("%s\n", result_table[user_hand][computer_hand]);
    }

    return 0;
}

가위바위보는 사용자의 선택 3가지와 컴퓨터의 선택 3가지를 조합하면 가능한 경우의 수가 9개로 정해진다.
따라서 각 조합의 결과를 표처럼 정리할 수 있고, 이를 2차원 배열에 미리 저장해 두면 인덱스로 바로 조회할 수 있다.

사용자 입력은 1, 2, 3으로 받지만 result_table 배열은 0부터 시작하는 인덱스를 사용한다.
그래서 user_input - 1을 통해 입력값을 0, 1, 2로 변환해 user_hand에 저장하고 배열의 인덱스로 사용한다.
패 이름도 마찬가지로 배열에서 조회한다.
덕분에 조건문으로 길게 나열하던 코드를 훨씬 간결하게 만들 수 있다.