Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#### 컴퓨터
* [x] 로또 구입 금액만큼 로또 발행
* [x] 발행한 로또 오름차순으로 정렬하여 출력
* [x] 로또 구입 금액 유효성 검사
* [x] 로또 번호와 당첨 번호 비교
* [x] 수익률 계산 (둘째자리 반올림)
* [x] 당첨 내역 및 수익률 출력
* [x] 예외 상황 시 에러 문구 출력 후 다시 입력 받음

#### 사용자
* [x] 당첨 번호 입력
* [x] 보너스 번호 입력
* [x] 사용자 입력 유효성 검사

#### 조건
* [x] 함수나 메서드 길이 15라인 초과 금지
* [x] else 예약어 사용 금지
* [x] Enum 적용
* [x] 로직 분리
4 changes: 1 addition & 3 deletions src/lotto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# src/lotto/__init__.py

# 📌 이 패키지는 로또 관련 기능을 제공하는 모듈입니다.
# 외부에서 `from lotto import Lotto`와 같은 방식으로 사용할 수 있도록
# 외부에서 `from lotto import Lotto`와 같은 방식으로 사용할 수 있도록
# 필요한 모듈을 여기에 등록하세요.
#
# ✅ 새로운 모듈을 추가할 경우:
Expand Down
77 changes: 71 additions & 6 deletions src/lotto/lotto.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,77 @@
from typing import List
from enum import Enum
import random


class Lotto:
def __init__(self, numbers: List[int]):
"""
로또 번호 생성 및 유효성 검증하는 클래스
"""

def __init__(self, numbers: list[int]):
"""
로또 객체 초기화
"""
self._validate(numbers)
self._numbers = numbers
self._numbers = sorted(numbers)

def _validate(self, numbers: List[int]):
def _validate(self, numbers: list[int]):
"""
로또 번호 유효성 검사
(로또 번호 6개, 중복 불가, 1~45의 범위)
"""
if len(numbers) != 6:
raise ValueError
raise ValueError("로또 번호는 6개여야 합니다.")
if len(set(numbers)) < 6:
raise ValueError("로또 번호는 중복되어서는 안됩니다.")
if max(numbers) > 45 or min(numbers) < 1:
raise ValueError("로또 번호의 숫자 범위는 1~45까지입니다.")

Check warning on line 27 in src/lotto/lotto.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/lotto.py#L27

Added line #L27 was not covered by tests

@classmethod
def generate_num(cls):
"""
1~45 사이의 랜덤한 6개의 숫자로 로또 객체 생성
"""
return cls(random.sample(range(1, 46), 6))

def get_numbers(self):
"""
로또 번호 리스트 반환
"""
return self._numbers

def __str__(self):
"""
로또 번호 문자열로 반환
"""
return str(self._numbers)


class Rank(Enum):
"""
로또 당첨 순위 정의하는 클래스
"""

FIFTH = (3, False, 5_000)
FOURTH = (4, False, 50_000)
THIRD = (5, False, 1_500_000)
SECOND = (5, True, 30_000_000)
FIRST = (6, False, 2_000_000_000)
NONE = (0, False, 0)

def __init__(self, match_cnt, bonus_match, prize):
"""
Rank 객체 초기화
"""
self.match_cnt = match_cnt
self.bonus_match = bonus_match
self.prize = prize

# TODO: 추가 기능 구현
@classmethod
def get_rank(cls, match_cnt, bonus):
"""
일치 개수와 보너스 번호 여부를 기반으로 당첨 순위 반환
"""
for rank in cls:
if rank.match_cnt == match_cnt and rank.bonus_match == bonus:
return rank
return cls.NONE
141 changes: 139 additions & 2 deletions src/lotto/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,143 @@
from .lotto import Rank,Lotto
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

PEP8 스타일 가이드를 준수해주세요.

쉼표 뒤에 공백이 누락되었습니다.

- from .lotto import Rank,Lotto
+ from .lotto import Rank, Lotto
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from .lotto import Rank,Lotto
from .lotto import Rank, Lotto
🧰 Tools
🪛 GitHub Actions: Check PEP8 Style

[error] 1-1: E231 missing whitespace after ','

"""
로또 프로그램 모듈.
이 모듈은 로또 번호 생성, 당첨 확인, 결과 출력 등의 기능을 포함한다.
"""


def main():
# TODO: 프로그램 구현
pass
"""
로또 프로그램 메인 함수
1. 사용자에게 금액 입력받고 해당 개수만큼 로또 번호 생성
2. 사용자에게서 당첨 번호와 보너스 번호 입력 받음
3. 로또 번호와 사용자가 선택한 번호 비교하여 결과 출력
"""
count = input_price()
lotto_list = [Lotto.generate_num() for _ in range(count)]
print_lotto(lotto_list)

user_num = user_input()
bonus_num = bonus_input(user_num)

result, total_prize = compare_lotto(lotto_list, user_num, bonus_num)
print_result(result, total_prize, count)


def input_price():
"""
사용자에게서 로또 구입 금액 입력받아 유효성 검사하는 함수
"""
while True:
try:
print("구입금액을 입력해 주세요.")
price = input()
return validate_price(price)
except ValueError as e:
print(f"[ERROR] {e}")
raise
Comment on lines +32 to +39
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 처리 로직을 개선해주세요.

에러를 출력하고 다시 발생시키는 것은 중복된 처리입니다. 다음과 같이 수정하는 것이 좋겠습니다:

         try:
             print("구입금액을 입력해 주세요.")
             price = input()
             return validate_price(price)
         except ValueError as e:
             print(f"[ERROR] {e}")
-            raise
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while True:
try:
print("구입금액을 입력해 주세요.")
price = input()
return validate_price(price)
except ValueError as e:
print(f"[ERROR] {e}")
raise
while True:
try:
print("구입금액을 입력해 주세요.")
price = input()
return validate_price(price)
except ValueError as e:
print(f"[ERROR] {e}")



def validate_price(price):
"""
입력된 금액 유효성 검증 후 로또 개수 반환
"""
if not price.isdigit():
raise ValueError("[ERROR] 숫자를 입력해 주세요.\n")
if int(price) % 1000 != 0:
raise ValueError("구입 금액은 1,000원으로 나누어 떨어져야 합니다.\n")

Check warning on line 48 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L48

Added line #L48 was not covered by tests
if int(price) < 1000:
raise ValueError("구입 금액은 1,000원 이상이어야 합니다.\n")

Check warning on line 50 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L50

Added line #L50 was not covered by tests

return int(price) // 1000


def print_lotto(lotto_list):
"""
생성된 로또 번호 출력
"""
print(f"\n{len(lotto_list)}개를 구매했습니다.")
for lotto in lotto_list:
print(lotto)


def user_input():
"""
사용자로부터 당첨 번호 입력받아 반환
"""
while True:
print("\n당첨 번호를 입력해 주세요.")
try:
user_num = list(map(int, input().split(",")))
return Lotto(user_num).get_numbers()
except ValueError as e:
print(f"[ERROR] {e}")

Check warning on line 74 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L73-L74

Added lines #L73 - L74 were not covered by tests


def bonus_input(user_num):
"""
사용자로부터 보너스 번호 입력받아 반환
"""
while True:
try:
bonus_num = input("\n보너스 번호를 입력해 주세요.\n")
return validate_bonus(bonus_num, user_num)
except ValueError as e:
print(f"[ERROR] {e}")

Check warning on line 86 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L85-L86

Added lines #L85 - L86 were not covered by tests


def validate_bonus(bonus_num, user_num):
"""
입력된 보너스 번호 유효성 검사 후 반환
"""
if not bonus_num.isdigit():
raise ValueError("숫자를 입력해 주세요.")

Check warning on line 94 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L94

Added line #L94 was not covered by tests
if int(bonus_num) in user_num:
raise ValueError("보너스 숫자와 입력한 당첨 번호는 중복되지 않아야 합니다.")

Check warning on line 96 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L96

Added line #L96 was not covered by tests
if int(bonus_num) > 45 or int(bonus_num) < 1:
raise ValueError("로또 번호의 숫자 범위는 1~45까지입니다.")

Check warning on line 98 in src/lotto/main.py

View check run for this annotation

Codecov / codecov/patch

src/lotto/main.py#L98

Added line #L98 was not covered by tests

return int(bonus_num)


def compare_lotto(lotto_list, user_num, bonus_num):
"""
로또 번호와 사용자가 선택한 번호 비교하여 당첨 결과 계산
"""
result = {rank: 0 for rank in Rank}
total_prize = 0

for lotto in lotto_list:
lotto_num = lotto.get_numbers()
match_cnt = len(set(user_num) & set(lotto_num))
bonus = bonus_num in lotto_num

rank = Rank.get_rank(match_cnt, bonus)
result[rank] += 1
total_prize += rank.prize

return result, total_prize


def print_result(result, total_prize, count):
"""
당첨 결과 출력
"""
profit_rate = round((total_prize / (count * 1000)) * 100, 2)

print("\n당첨 통계")
print("---")
for rank in Rank:
if rank == Rank.SECOND:
print(f"{rank.match_cnt}개 일치, 보너스 볼 일치 ({rank.prize:,}원) - "
f"{result[rank]}개")

if rank != Rank.NONE and rank != Rank.SECOND:
print(f"{rank.match_cnt}개 일치 ({rank.prize:,}원) - "
f"{result[rank]}개")

print(f"총 수익률은 {profit_rate}%입니다.")


if __name__ == "__main__":
main()
Loading