베스킨라빈스 31 게임 만들기

1. 개요

“베스킨~ 라빈스~ 31!“. 좀 놀아보았다 하신 분들은 모를 수가 없는 게임. 각 게임 플레이어가 차례로 1~3 중의 수를 외쳐서 계속 수를 더하고, 마지막 31을 외치는 플레이어가 지는 게임이다. 컴퓨터와 사용자가 1:1 대결을 한다.

2. 로직

1️⃣ Random.randrange() 를 사용하여 누가 먼저 시작할지 정한다.
2️⃣ 컴퓨터가 말하는 함수를 만든다. 컴퓨터는 random 함수를 이용하여 1과 3 사이를 말하게 한다. 현재 숫자가 어떻게 변화 되고 있는 지는 한 변수를 정하여 누적하여 값을 더하며 확인한다.
3️⃣ 사용자가 말하는 함수를 만든다. 사용자의 숫자는 input()을 이용하여 받는다. 입력 예외처리 해준다. (1~3외에 값을 받지 못하게 한다)
4️⃣ 사용자 친화적으로 게임 진행상황을 출력해주는 함수를 만든다.
5️⃣ 누군가 31을 말하게 되면 상대방이 승리 했음을 알린다.

3. 코드

import random


# 컴퓨터가 말한다
def com_say(cur_num):
    while True:
        com_input = random.randrange(1, 4) # 1 ~ 3 사이 입력
        if is_under_equal_thirtyone(com_input, cur_num): # 입력 값이 31이하 인 경우만
            break

    return cur_num, com_input


# 사용자가 말한다
def user_say(cur_num):
    user_input = False

    while not user_input: # 사용자 입력이 제대로 값을 가지지 않았다면
        try:
            user_input = int(input())
            if not isinstance(user_input, int) or (user_input < 1 or user_input > 3): # 정수형, 1~3사이
                raise ValueError
            if not is_under_equal_thirtyone(user_input, cur_num): # 31이하가 아니라면
                print("최대 31 까지만 입력 할 수 있습니다")
                user_input = False
                continue
        except ValueError:
            print("유효한 수를 입력 해 주세요(1~3)")
            user_input = False

    return cur_num, user_input


# 게임 진행 화면 출력한다
def print_current_situation(who, say_num, cur_num):
    players = ["컴퓨터", "사용자"]
    msg = f"{players[who]}:"
    game_over = False

    for _ in range(say_num):
        cur_num += 1
        msg += f" {cur_num}"
        if cur_num == 31:
            msg += f"\n{players[not who]} 승"
            game_over = True

    print(msg)
    return cur_num, game_over


# 컴퓨터 & 사용자 입력 값이 31 이하 인지 확인한다
def is_under_equal_thirtyone(input_num, cur_num):
    return True if input_num + cur_num <= 31 else False


# 0 is COM
# 1 is USER
who_turn = random.randrange(2)  # 누가 먼저 시작할 지 정한다

print("게임을 시작하지 ...")
cur_num = 0

while True:
    cur_num, num_said = com_say(cur_num) if not who_turn else user_say(cur_num) # 컴퓨터 또는 사용자 입력 값 받기
    cur_num, keep_game = print_current_situation(who_turn, num_said, cur_num)
    if keep_game: # 게임 진행 화면 출력 (game over return)
        break
    who_turn = not who_turn # 턴을 변경

베스킨라빈스 31 인공지능 만들기

1. 개요

이번에는 같은 게임이지만, 컴퓨터가 똑똑해야 한다. 단순히 임의의 값을 말하는 것이 아니라 필승이 되는 수들을 말해야 한다. 우선 어떻게 해야 이 게임을 이길 수 있는가 봐야 한다. 우선은 먼저 26을 말하는 자가 반드시 승리할 수 있다. (다음 상대 말에 엉뚱하게 답만 하지 않는 다면..) 그렇다면! 어떻게 무조건 26을 말할 수 있을까? 이 이전에 4 작은 22을 말하면 된다. 이런식으로 계속 내려가다보면 필승이 되는 수를 알 수 있다. 그 수는 26, 22, 18, 14, 10, 6, 2 이며, 처음부터 필승이 되는 수만 말하게 되면 반드시 이길 수 있다. 즉, 처음시작 할 때 2 값을 먼저 말하게 되고 게임을 진행 하면서 위에 열거 된 수만 말하게 된다면 반드시 이길 수 있게 된다. 컴퓨터는 사용자가 말하는 값을 보면서 다음 필승이 되는 수들만 말하면 된다. 단, 사용자가 먼저 2를 얘기하게 되고 필승의 수만 계속 말하게 되면 컴퓨터는 지게 된다.(유일한 컴퓨터 패배 조건)

2. 로직

1️⃣ 위 코드를 그대로 가져오자. 그리고 컴퓨터가 말하는 함수안에서 코드(머리) 몇 줄 추가 해주면 끝난다.
2️⃣ 컴퓨터는 현재까지 진행 된 수(cur_num)를 보고 자신이 말할 수 있는 수(cur+1, cur+2, cur+3) 중 필승의 수를 말한다. 필승 수에서 2를 더하거나 뺀 후 4로 나눴을 때 나머지가 0 이다.

3. 코드

import random


# 컴퓨터가 말한다
def com_say(cur_num):
    can_say_num = range(cur_num+1, cur_num+4) # 컴퓨터가 말할 수 있는 숫자를 리스트에 담는다
    com_input = 0

    for num in can_say_num:
        if (num+2) % 4 == 0: # 필승 조건에 부합하는 수가 있는 지 확인
            com_input = num - cur_num # 컴퓨터 입력 숫자를 도출

    if com_input == 0: # 필승 조건 값을 입력 할 수 없다면 ...
        while True:
            com_input = random.randrange(1, 4) # 1 ~ 3 사이 입력
            if is_under_equal_thirtyone(com_input, cur_num): # 입력 값이 31이하 인 경우만
                break

    return cur_num, com_input

    # 아래는 생략 (위와 동일)

총평

베스킨라빈스31 이 게임에 이런 비밀이 있는 지 몰랐다. 게임을 할 때 아무 생각 없이 숫자를 불렀었는데 말이다. 이 게임도 그렇고 요즘 알고리즘 문제를 풀면서도 이런 생각을 했다. 어떤 규칙을 찾고 그 규칙을 코드로 표현 하는 게 생각만큼 쉽지가 않다. 뭔가 반복성과 규칙이 보이는데도 코드로 옮기는 게 어렵다. 현재 나를 보니 부족함이 많아 슬프지만, 계속 문제들을 풀고 해결하다보면 사고의 유연함과 구현력이 생기리라 믿고 오늘도 열심히 문제들을 풀어 나가겠다.
[추가] 처음 구현할 때 현재 진행 중인 숫자를 전역변수로 사용했는데, 튜터님이 그건 좋은 방법이 아니라 해서 매개변수로 넣고 리턴해주는 방식으로 누적되는 값을 계속 기억하게 했다. 전역변수 사용을 지양하자!