Lv1 · 숫자 맞추기 (난이도 선택)
1~N 사이 랜덤 숫자를 시도 횟수 제한 안에 맞히는 게임. 난이도 3단계, 함수 분리, 재도전 옵션을 다룹니다.
이 강의에서 배우는 것
- 1난이도별 범위/시도 횟수를 dict 로 관리한다
- 2ValueError 를 try-except 로 처리한다
- 3play_round 함수로 한 판을 캡슐화한다
프로젝트 개요
- 쉬움 1~50/7회, 보통 1~100/7회, 어려움 1~500/10회
- 매 시도마다 더 큰 수/더 작은 수 힌트 + 남은 횟수
- 횟수 초과 시 정답 공개, 재도전 옵션
요구사항
- 난이도 선택 (1/2/3)
- 랜덤 정답: random.randint(low, high)
- 정수가 아닌 입력은 재요청
- 함수 3개 이상 분리
입출력 예시
난이도 (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 에 담으면 선택값으로 한 번에 꺼낼 수 있습니다.
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 로 구합니다.
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 하면 프로그램이 죽지 않습니다. 이때 '잘못된 입력은 횟수를 차감할지' 를 정책으로 정하세요 — 보통은 차감하지 않는 편이 친절합니다.
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)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
"""숫자 맞추기 — 난이도 선택 + 시도 제한"""
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)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
프로젝트 구현
목표: 위 사양대로 직접 작성한다.
- LEVELS dict 활용
- play_round 함수
- ValueError 처리
range(max_tries, 0, -1) 로 카운트다운
✓ 정답!- · 함수 분리
- · 예외 처리
- · 재도전
▶정답 코드 펼치기 / 접기
# 위 solution.py 와 동일(예제 참고)