diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..7db31c9 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,23 @@ +name: Run tests and upload coverage +on: + pull_request +jobs: + test: + name: Run tests and collect coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v4 + - name: Install dependencies + run: pip install pytest pytest-cov + - name: Run tests + run: pytest --cov=. --cov-report=xml --cov-branch + - name: Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: ${{ github.repository }} diff --git a/docs/README.md b/docs/README.md index e69de29..aeed71f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,60 @@ +## 기능 요구 사항 +- [X] 입력 기능 + - [X] 반복 입력, 1~9 서로다른 3자리의 수 (ex:123) + - [X] 게임이 끝난 경우 재시작/종료를 선택하는 1과 2 중 하나의 수 + +- [X] 출력 기능 + - [X] 게임 시작 문구 "숫자 야구 게임을 시작합니다." + - [X] 숫자 입력 요구 문구 "숫자를 입력해주세요 : " + - [X] 입력한 수에 대한 결과를 볼, 스트라이크 개수로 표시 + - [X] 볼 먼저 출력 (ex: 2볼 1스트라이크) + - [X] 모두 0개이면 "낫싱" + - [X] 일치 시, 게임 종료 안내 문구 "3개의 숫자를 모두 맞히셨습니다! 게임 종료" + - [X] 게임 새로 시작 여부 확인 질문 "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요." + +- [X] 컴퓨터 기능 + - [X] 1~9의 서로 다른 임의의 수 3개를 선택 + - [X] 입력한 숫자와 비교하여 ball, strike 카운트 + +- [X] 게임 새로 시작 또는 종료 기능 + - [X] 1 입력 시 재시작 + - [X] 2 입력 시 종료 + +- [X] 예외 처리 및 종료 기능 + - [X] 사용자가 잘못된 값을 입력할 경우 ValueError를 발생 후 게임 종료 + - [X] 3자리 수 입력 시 예외 사항 + - [X] 정자가 아닌 것을 포함한 경우 + - [X] 3자리 수가 아닌 경우 + - [X] 0이 포함된 경우 + - [X] 중복 값이 있는 경우 + - [X] 게임 새로 시작 여부 확인 질문 시 + - [X] 1, 2 이외의 값을 입력한 경우 + +## 기능 추가 +1. make_computer_num() +> 1에서 9까지 서로 다른 임의의 수 3개 생성 기능 함수 추가 [\#1] +2. player_input() +> 플레이어에게 3개의 숫자 입력 받는 기능 함수 추가 [\#2] +3. check_input() +> 플레이어가 입력한 숫자에 대한 결과를 출력하는 함수 추가 [\#3] +4. loop_check() +> 1에서 3까지의 과정을 반복해 3개의 숫자를 모두 맞히면 게임이 종료되는 기능 함수 추가 [\#4] +5. restart_baseball() +> 게임 종료 후 '1' 입력시 다시 시작, '2' 입력시 완전히 종료하는 기능 함수 추가 [\#5] +6. validate_input() +> 잘못된 값을 입력한 경우 ValueError가 발생하여 프로그램을 종료하는 기능 함수 추가 [\#6] +7. same_num_check() +> 입력 값에서 중복되는 수를 인식하는 기능 함수 추가 [\#6] + +## 오류 수정 +1. make_computer_num() 수정 +> 랜덤 숫자 생성 과정에서 숫자 범위가 1에서 8인 오류 1에서 9 정상 작동하도록 수정 [\#7] +>> random.sample(range(1, ~~9~~10), 3) +2. same_num_check() 수정 +> 첫 번째 숫자만 확인하고 바로 반환하는 오류를 전체 배열의 중복을 확인하도록 수정 [\#7] + +## 기능 수정 +1. loop_check() 재귀 호출에서 while 루프로 변경 +> 재귀 호출은 스택 오버플로우를 일으킬 수 있으므로, while 루프로 변경 [\#8] +2. validate_input() ValueError 발생 시 에러 메시지 추가 +> 입력 길이, 입력 숫자, 중복 유무 3가지로로 나눠 에러 메시지 출력 [\#8] \ No newline at end of file diff --git a/src/baseball/main.py b/src/baseball/main.py index cd8d2f3..6be849a 100644 --- a/src/baseball/main.py +++ b/src/baseball/main.py @@ -1,9 +1,83 @@ +import random + +def same_num_check(check_array): # list에서 중복되는 수를 인식하는 기능 함수 + for i in check_array: + if check_array.count(i) != 1: + return 0 + return 1 + +def validate_input(compare_array, want_len): # 잘못된 값을 입력한 경우 ValueError가 발생하여 프로그램을 종료하는 기능 함수 + if (len(compare_array) != want_len): # 플레이어가 입력한 값이 예상되는 값의 길이가 아니거나, 0이 포함되거나, 중복되는 수가 입력되어도 예외 처리 + raise ValueError("3개의 숫자를 입력해주세요.") + elif (0 in compare_array): + raise ValueError("1 ~ 9의 숫자만 입력해주세요.") + elif (same_num_check(compare_array) == 0): + raise ValueError("중복되는 수가 있습니다.") + +def make_computer_num(): # random 모듈을 사용하여 임의의 수 생성하는 함수 (요구사항) + computer = random.sample(range(1, 10), 3) # basecode + return computer + +def player_input(): # 플레이어에게 3개의 숫자 입력 받는 기능 함수 + get_array = list(map(int, input("숫자를 입력해주세요 : "))) + validate_input(get_array, 3) + return get_array + +def check_input(get, com): # 플레이어가 입력한 숫자에 대한 결과를 출력하는 함수 + ball, strike = 0, 0 + for i in range(0,3): + if get[i] == com[i]: + strike += 1 + elif get[i] in com: + ball += 1 + else: + continue + if strike == 3: + return "3스트라이크" + elif ball + strike == 0: + return "낫싱" + elif ball == 0: + return "{0}스트라이크".format(strike) + elif strike == 0: + return "{0}볼".format(ball) + else: + return "{0}볼 {1}스트라이크".format(ball, strike) + +def loop_check(com): # 1에서 3까지의 과정을 반복해 3개의 숫자를 모두 맞히면 게임이 종료되는 기능 함수 + while True: + get = player_input() + result_check_input = check_input(get, com) + print(result_check_input) + if result_check_input == "3스트라이크": # check_input()의 결과가 "3스트라이크"일 경우 게임을 종료하고, 아닐 경우 다시 loop_check() 수해 + print("3개의 숫자를 모두 맞히셨습니다! 게임 종료") + return 0 + +def restart_baseball(): # 게임 종료 후 '1' 입력시 다시 시작, '2' 입력시 완전히 종료하는 기능 함수 + print("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.") + try: + user_key = int(input()) + if user_key in [1, 2]: + return user_key + else: # 잘못된 값을 입력할 경우 'ValueError'를 발생 + raise ValueError("1 또는 2만 입력 가능합니다") + except ValueError: + raise ValueError("올바른 숫자를 입력해주세요 (1 또는 2)") + def main(): """ 프로그램의 진입점 함수. 여기에서 전체 프로그램 로직을 시작합니다. """ # 프로그램의 메인 로직을 여기에 구현 + print("숫자 야구 게임을 시작합니다.") + while True: + com_num = make_computer_num() + loop_check(com_num) + restart = restart_baseball() + if restart == 1: + continue + else: + break if __name__ == "__main__": # 프로그램이 직접 실행될 때만 main() 함수를 호출 diff --git a/tests/test_application.py b/tests/test_main.py similarity index 79% rename from tests/test_application.py rename to tests/test_main.py index 81a176e..225e276 100644 --- a/tests/test_application.py +++ b/tests/test_main.py @@ -8,19 +8,19 @@ def test_게임종료_후_재시작(capsys): # 랜덤값 임의 정의 with patch('random.sample', side_effect=[[1, 3, 5], [5, 8, 9]]): # 입력값 모의 처리 - with patch('builtins.input', side_effect=["246", "135", "1", "597", "589", "2"]): + with patch('builtins.input', side_effect=["246", "135", "1", "512", "894", "597", "589", "2"]): main() # 출력값 캡처 캡처된_출력 = capsys.readouterr().out # 결과 검증 - assert all(예상_출력 in 캡처된_출력 for 예상_출력 in ["낫싱", "3스트라이크", "1볼 1스트라이크", "3스트라이크", "게임 종료"]) + assert all(예상_출력 in 캡처된_출력 for 예상_출력 in ["낫싱", "3스트라이크", "1볼 1스트라이크", "1스트라이크", "2볼", "3스트라이크", "게임 종료"]) # 예외 테스트 def test_예외_테스트(): with pytest.raises(ValueError): # 잘못된 입력 처리 - with patch('builtins.input', side_effect=["1234"]): + with patch('builtins.input', side_effect=["1234", "012", "233", "3", "ㄱ"]): main()