# 숫자야구게임
---
## 1. Number Baseball Game
> 컴퓨터가 제시하는 임의의 숫자 3개와 사용자가 입력하는 숫자 3개를 비교하여 3번의 `스트라이크`를 달성하면 사용자가 승리하고, 주어진 9번의 기회안에 달성하지 못하면 컴퓨터의 승리로 게임종료.
>
> * `스트라이크` / `볼` 판정
> * `스트라이크`와 `볼`은 사용자가 입력하는 수와 컴퓨터가 만들어낸 임의의 수의 비교를 통해 판정합니다
> * 사용자가 입력한 수와 컴퓨터가 만든 수, 그리고 그 수의 위치가 모두 일치하면 `스트라이크`
> * 사용자가 입력한 수와 컴퓨터가 만든 수가 일치하지만, 위치가 다른 경우 `볼`
### Flowchart

> :warning: Step1(2021.10.06)에서는 사용자 메뉴 출력과 사용자의 입력은 미구현되었으며, 사용자 숫자를 매 차례마다 임의의 숫자를 생성하여 대신한다.
---
### 함수들의 기능 설명
- startGame()
- 게임을 시작하고 임의의 사용자 숫자를 생성해 컴퓨터 숫자와 비교하여 `strike`, `ball`을 판정하고 결과에 따라 게임을 반복한다.
- 임의 숫자 생성과 `스트라이크` / `볼` 결과 도출을 위해 `generatedRandomNumbers()`와 `compareResult(with:)` 함수를 호출한다.
- generatedRandomNumbers() -> [Int]
- Set가 중복 `.insert`를 허용하지 않는 특성을 이용하여 중복 없는 임의의 숫자 3개를 생성하여 반환한다.
- 판정을 위해 원소들의 순서가 필요하므로, 인덱스를 가지는`Array<Int>` 타입으로 변환하여 반환한다.
- compareResult(with userNumbers: [Int]) -> [Int]
- 사용자 숫자 3개를 전달받아 숫자를 비교하고 `strike`와 `ball` 판정을 반환한다.
- `스트라이크` 개수와 `볼` 개수를 함께 반환하기 위해 `Array<Int>`에 담아 반환한다.
## 2. Key Experience
### 이슈, 해결방법
- Issue : startGame() 함수를 내부의 while 반복문에 두가지 조건을 작성하는 과정에서 연산자 `&&`을 `||`로 표기해 반복문이 무한 반복 됨.
- Solution : 두 조건의을 모두 충족하는 연산자 `&&`으로 변경해 이슈 해결.
------------
코드를 작성할 때 고민되었던 점, 해결이 되지 않은 점, 조언을 얻고 싶은 부분 등 상세하게 작성
코드만으로는 고민이나 과정을 유추하기 어렵습니다. 따라서 해결한 문제를 적고 왜 이 방법을 택했는지 설명합니다. 해결하지 못한 경우에는 진행 과정과 시도한 점을 상세히 설명합니다.
## 고민되었던 점
- 함수 내부에 while문을 작성해본 적이 없었고, 그런 코드를 본 적도 없어서 함수 안에서는 while문을 작성할 수 없다고 생각했어요. 하지만 나무의 도움을 받아 함수 내부에도 while문을 작성할 수 있다는 것을 알게되었습니다 :)
- 함수와 변수 및 상수에 대한 적절한 네이밍에 대한 고민을 많이 했습니다. 공식문서를 가급적 참고하려 했지만, 아직 많이 어려운 부분인 것 같아요 :(
- 고차함수의 활용이 익숙하지 않았고, `$0`와 같은 생소한 표현 방법이 어려웠는데, 이번 기회로 적용해보게 되어서 많이 배웠습니다.
## 조언을 얻고 싶은 부분
- 플로우차트를 작성할 때 고민했던 점은,
`1.실제 코드에 있는 함수명을 표기하고 이 함수들이 호출되는 순서도를 만들지`
`2.지금 작성한 것과 같이 프로그램의 기능으로 표현하고 그 작동 흐름을 표현할지` 였습니다.
어떤 방식이 독자 입장에서 코드를 이해하는데 더 도움이 될 지가 궁금합니다. Fezz는 어떠신가요? Flowchart가 저희 코드를 이해하는데 도움이 되셨나요?
- 프로젝트 요구사항에 따라 순서대로 전역변수로 `computerNumbers`와 `tryCount`를 생성했습니다. 이렇게 전역 변수로 두면 휴먼에러나 예외적인 상황들로 인해 의도치 않은 동작을 하게 될 수 있어 가급적 피해야 한다고 알고있었는데, 이 부분을 `startGame()`내부로 옮기는 것이 더 좋은 코드일지 궁금합니다.
- `generatedRandomNumbers()`는 원래 `chooseRandomNumber()`라고 이름지었습니다. 추후 코드를 수정하는 과정에서 이 함수가 리턴값을 갖게 되었는데, 리턴값을 갖는 함수는 네이밍을 명사형으로 작성하는 것이 좋다고 본 것 같습니다.
실제 동작은 숫자를 생성하는 것이지만 호출할 때는 하나의 값 처럼 쓰이기에 명사형으로 쓰는게 맞다고 이해했는데, 저희가 이해한게 맞을까요? 그리고, 리턴값을 가지는 함수는 예외 없이 명사형으로 네이밍 해야 하는 것인지 궁금합니다!
- startGame
`compareResult(with:)`함수는 스트라이크와 볼 카운트를 동시에 반환하기 위해 `Array<Int>` 타입의 반환값을 갖습니다. 그로 인해 `startGame()` 함수 내부에서 스트라이크와 볼 카운트의 값을 사용할 때 `strikeAndBallCount[0]`,` strikeAndBallCount[1]` 처럼 표현하게 되는데, 다른 분이 코드를 읽을 때 의미를 바로 이해하기 어려울 수도 있다고 생각했습니다. 혹시 다른 방법이 있을지 궁금합니다.
```swift=
func startGame() {
computerNumbers = generatedRandomNumbers()
var strikeCount: Int = 0
while tryCount != 0 && strikeCount != 3 {
tryCount = tryCount - 1
let userNumbers = generatedRandomNumbers()
let strikeAndBallCount: [Int] = compareResult(with: userNumbers)
print("임의의 수 : " + userNumbers.map{String($0)}.joined(separator: " "))
print("\(strikeAndBallCount[0]) 스트라이크, \(strikeAndBallCount[1]) 볼")
print("남은 기회 : \(tryCount)")
strikeCount = strikeAndBallCount[0]
}
if strikeCount == 3 {
print("사용자 승리!")
} else {
print("컴퓨터 승리...!")
}
}
```
Fezz! step2 PR 보냅니다.
디스코드에서 대화하며 알려주신 것들 너무 도움 많이 되었습니다! 코드에 모두 반영하지 못해 아쉽지만,
잊지 않고 다음 프로젝트때 활용해 보고 싶어요! :pray:
step1 리뷰때도 공부거리를 많이 주셔서 유익한 프로젝트였던 것 같아요:smile:
#### 아직 끝내지 못한 작업들
- 전반적인 매직넘버 및 매직리터럴 관리
- 함수 이름에 대한 개선
- 전역(global)에 존재하는 while문을 함수화 하기
- 사용자 정의 타입 사용불가 조건에 맞춰 열거형 제거
## 고민되었던 점
- 오류를 처리하는 과정에서 throw로 오류를 던지고 try와 do-catch로 오류를 처리하는 과정이 생소했습니다. 이번 프로젝트를 하며 처음 알게 된 개념이지만 좀 더 공부해봐야 깊게 이해할 수 있을 것 같아요. :sweat_smile:
- 그 외에도 예외처리를 하기 위한 코드 작성에 가장 시간을 많이 들인 것 같아요. guard문의 조건을 표현하는 방식이나 타입캐스팅을 위한 옵셔널 바인딩에 대해서도 많은 고민을 할 수 있었습니다.
- 생소했던 고차함수들을 활용해보려 이런저런 시도를 하는 과정에서 조금은 사용법을 익히게 된 것 같아요!