← 파이썬 강의 목록으로
🎯
실습 프로젝트 (Practice) · ★
함수 분리 + 시도 제한

Lv1 · 숫자 맞추기 (난이도 선택)

1~N 사이 랜덤 숫자를 시도 횟수 제한 안에 맞히는 게임. 난이도 3단계, 함수 분리, 재도전 옵션을 다룹니다.

random함수예외
소요 시간
1~2시간
난이도
📊 기초 응용
선수 조건
🎯 기초 4-5, 9주차
결과물
함수 분리와 입력 검증 기본기

이 강의에서 배우는 것

  • 1난이도별 범위/시도 횟수를 dict 로 관리한다
  • 2ValueError 를 try-except 로 처리한다
  • 3play_round 함수로 한 판을 캡슐화한다

프로젝트 개요

  • 쉬움 1~50/7회, 보통 1~100/7회, 어려움 1~500/10회
  • 매 시도마다 더 큰 수/더 작은 수 힌트 + 남은 횟수
  • 횟수 초과 시 정답 공개, 재도전 옵션

요구사항

  • 난이도 선택 (1/2/3)
  • 랜덤 정답: random.randint(low, high)
  • 정수가 아닌 입력은 재요청
  • 함수 3개 이상 분리

입출력 예시

text
난이도 (1.쉬움 2.보통 3.어려움): 2
1~100, 7번 안에 맞춰보세요
[남은 7회] 숫자: 50  → 더 큰 수
[남은 6회] 숫자: 75  → 더 작은 수
[남은 5회] 숫자: 63  → 정답! (시도 3회)

한 번 더? (y/n): n

풀이 핵심 — 설계 흐름

이 게임의 핵심은 두 가지입니다. ① 난이도 같은 설정값을 코드 분기 대신 한 곳(dict)에 모으고, ② 한 판의 진행을 함수 하나로 캡슐화해 main 은 흐름만 지휘하게 만드는 것입니다. 그러면 난이도를 늘려도 if 가 늘지 않고, 점수·통계·테스트를 나중에 붙이기도 쉬워집니다.

  • LEVELS dict — (이름, 최소, 최대, 시도횟수) 를 한 곳에서 관리. 난이도를 추가해도 분기가 늘지 않습니다.
  • pick_difficulty() — 올바른 선택이 들어올 때까지 되묻는 입력 루프. 잘못된 값은 조용히 넘기지 말고 안내 메시지를 출력합니다.
  • play_round() — 정답 생성·힌트·시도 차감을 담당하고, 한 판이 끝나면 True/False 를 반환해 결과를 알립니다.
  • main() — 난이도 선택 → 한 판 → 재도전 여부만 결정. 세부 구현은 모릅니다(관심사 분리).
💡

함수가 print 만 하지 않고 값을 return 하게 만들면, 나중에 점수·통계·자동 테스트를 붙이기 훨씬 쉬워집니다.

한 단계씩 — 핵심 코드 읽기

① 난이도를 dict 로 — 분기 대신 데이터

if/elif 로 난이도를 나누면 항목이 늘 때마다 코드가 길어집니다. (이름, 범위, 횟수) 를 튜플로 묶어 dict 에 담으면 선택값으로 한 번에 꺼낼 수 있습니다.

python
LEVELS = {
    "1": ("쉬움", 1, 50, 7),
    "2": ("보통", 1, 100, 7),
    "3": ("어려움", 1, 500, 10),
}
name, low, high, tries = LEVELS["2"]   # 보통 난이도를 한 줄로 꺼내기

② 카운트다운 루프 — 남은 횟수를 그대로 표시

range(max_tries, 0, -1) 은 max_tries 부터 1 까지 내려갑니다. 루프 변수 left 가 곧 '남은 횟수' 라 따로 계산할 필요가 없고, 사용한 횟수는 max_tries - left + 1 로 구합니다.

python
for left in range(max_tries, 0, -1):
    guess = int(input(f"[남은 {left}회] 숫자: "))
    if guess < answer:
        print("  → 더 큰 수")
    elif guess > answer:
        print("  → 더 작은 수")
    else:
        print(f"  ✓ 정답! (시도 {max_tries - left + 1}회)")
        return True

③ 입력 검증 — 숫자가 아니면 다시

int(input()) 은 숫자가 아니면 ValueError 로 멈춥니다. try-except 로 감싸고 continue 하면 프로그램이 죽지 않습니다. 이때 '잘못된 입력은 횟수를 차감할지' 를 정책으로 정하세요 — 보통은 차감하지 않는 편이 친절합니다.

python
try:
    guess = int(input(f"[남은 {left}회] 숫자: "))
except ValueError:
    print("  숫자만 입력")
    continue   # 다시 입력받기
⚠️

for 루프에서 continue 하면 그 회차는 이미 소비됩니다. '잘못된 입력은 횟수에서 빼지 않기' 를 원하면 while left > 0 구조로 바꾸고, 성공/유효한 시도일 때만 left 를 줄이세요.

도전 과제

  • 베스트 기록 파일 저장
  • 사용자가 정답 정하고 컴퓨터가 맞히기 (이진 탐색)

채점 체크리스트

  • 난이도 선택 동작
  • 시도 제한 정확
  • 함수 3개 이상 분리

자주 하는 실수 (FAQ)

Q. 시도 횟수가 1번씩 어긋나요.

카운트다운에서 '사용한 횟수' 는 max_tries - left + 1 입니다. left 가 남은 횟수이므로 7회 중 첫 시도(left=7)는 1회차가 됩니다. left 를 그대로 시도 번호로 쓰면 한 칸씩 밀립니다.

Q. 문자를 입력하면 프로그램이 멈춰요.

int(input()) 이 ValueError 를 던지기 때문입니다. try-except ValueError 로 감싸고 continue 하면 멈추지 않고 다시 입력받습니다.

Q. random.randint(1, 100) 은 100 을 포함하나요?

네, 양쪽 끝을 모두 포함합니다(1 과 100 둘 다 나올 수 있음). 끝값을 제외하고 싶으면 random.randrange(1, 100) 을 쓰세요 — 이건 100 을 포함하지 않습니다.

Q. 정답을 맞혀도 계속 물어봐요.

play_round 의 정답 분기에서 return 으로 즉시 빠져나오지 않으면 루프가 계속됩니다. 정답일 때 return True 로 끝내고, 재도전 여부는 main 에서만 묻도록 책임을 나누세요.

정리

이 프로젝트의 핵심은 세 가지입니다 — 데이터로 분기 줄이기(dict), 한 판을 함수로 캡슐화하기, 그리고 입력을 믿지 않기(검증). 같은 패턴이 다음 실습(CLI 계산기·할 일 목록)에서도 그대로 이어집니다.

  • 난이도·설정은 dict 로 모아 확장에 강하게 만든다
  • play_round 처럼 '한 단위' 를 함수로 — 결과는 반환값으로 전달
  • int 변환은 항상 try-except, 횟수 차감 정책도 함께 결정

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.

solution.py전체 동작 코드
CODE
"""숫자 맞추기 — 난이도 선택 + 시도 제한"""
import random

LEVELS = {
    "1": ("쉬움", 1, 50, 7),
    "2": ("보통", 1, 100, 7),
    "3": ("어려움", 1, 500, 10),
}

def pick_difficulty():
    while True:
        choice = input("난이도 (1.쉬움 2.보통 3.어려움): ").strip()
        if choice in LEVELS:
            return LEVELS[choice]
        print("1, 2, 3 중에서 선택")

def play_round(low, high, max_tries):
    answer = random.randint(low, high)
    print(f"{low}~{high}, {max_tries}번 안에 맞춰보세요")
    for left in range(max_tries, 0, -1):
        try:
            guess = int(input(f"[남은 {left}회] 숫자: "))
        except ValueError:
            print("  숫자만 입력")
            continue
        if guess < answer:
            print("  → 더 큰 수")
        elif guess > answer:
            print("  → 더 작은 수")
        else:
            print(f"  ✓ 정답! (시도 {max_tries - left + 1}회)")
            return True
    print(f"  횟수 초과! 정답은 {answer}")
    return False

def main():
    while True:
        name, low, high, tries = pick_difficulty()
        print(f"\n[{name}]")
        play_round(low, high, tries)
        if input("\n한 번 더? (y/n): ").strip().lower() != "y":
            break

if __name__ == "__main__":
    main()
▶ 실행 결과
난이도 (1.쉬움 2.보통 3.어려움): 2
[보통]
1~100, 7번 안에 맞춰보세요
[남은 7회] 숫자: 50  → 더 큰 수
[남은 6회] 숫자: 75  → 더 작은 수
[남은 5회] 숫자: 63  ✓ 정답! (시도 3회)
한 번 더? (y/n): n

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

프로젝트 구현

목표: 위 사양대로 직접 작성한다.

요구사항
  • LEVELS dict 활용
  • play_round 함수
  • ValueError 처리
💡 힌트

range(max_tries, 0, -1) 로 카운트다운

입출력 예시
✓ 정답!
채점
  • · 함수 분리
  • · 예외 처리
  • · 재도전
정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일
▶ 실행 결과
(예제 참고)
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗