-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
묵찌빠 게임 [STEP 2] 햄찌, 가마 #230
base: ic_11_hamzzi
Are you sure you want to change the base?
Changes from all commits
a67a9f4
8a4451f
df8577e
1b14eab
8357c9a
bd4e229
ee30aeb
5f4f479
b98264f
61c9798
843d506
ecd9f56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,83 @@ | ||
## iOS 커리어 스타터 캠프 | ||
# 묵찌빠 프로젝트 | ||
|
||
### 묵찌빠 프로젝트 저장소 | ||
## 0. 목차 | ||
[1. 소개](#1-소개) | ||
[2. 팀원](#2-팀원) | ||
[3. 타임라인](#3-타임라인) | ||
[4. 실행 화면](#4-실행-화면) | ||
[5. 트러블 슈팅](#5-트러블-슈팅) | ||
[6. 팀 회고](#6-팀-회고) | ||
[7. 참고 자료](#7-참고-자료) | ||
|
||
- 이 저장소를 자신의 저장소로 fork하여 프로젝트를 진행합니다 | ||
## 1. 소개 | ||
Step 1는 가위, 바위, 보 게임입니다. 사용자가 숫자 0, 1, 2, 3 중 하나를 입력하여 게임을 진행합니다. 0은 게임 종료, 1은 가위, 2는 바위, 3은 보를 나타냅니다. 1, 2, 3이 아닌 문자를 입력하면 잘못된 입력으로 처리되어 최초 실행 상태로 돌아갑니다. 또한 사용자의 패와 컴퓨터의 패가 같아 비긴 경우에도 최초 실행 상태로 복귀합니다. 컴퓨터는 1, 2, 3 중 랜덤한 숫자를 생성하여 자신의 패를 결정합니다. 사용자가 이기거나(컴퓨터가 지는), 사용자가 지는(컴퓨터가 이기는) 승패가 나누어지는 경우에 대해서만 Step 2로 넘어갑니다. | ||
Step 2는 묵, 찌, 빠 게임입니다. 사용자 입력에 대한 처리는 Step 1과 거의 같습니다. 1은 묵, 2는 찌, 3은 빠를 나타냅니다. Step 2부터는 턴을 활용하게 됩니다. Step 1에서 마지막으로 이긴 쪽이 처음에 턴을 가지게 됩니다. 사용자가 잘못된 입력을 할 경우에 컴퓨터로 턴이 넘어갑니다. 사용자의 패와 컴퓨터의 패가 동일할 경우, 턴을 쥐고 있는 쪽이 승리하게 됩니다. 패가 다른 경우에는 이긴 쪽이 턴을 쥐게 되고 Step 2의 최초 실행 상태로 복귀하여 게임을 계속 진행하게 됩니다. | ||
|
||
## 2. 팀원 | ||
| [햄찌](https://github.com/kkomgi) | [가마](https://github.com/forseaest) | | ||
| --- | --- | | ||
| <img src="https://avatars.githubusercontent.com/u/65929788?v=4" width="200"> | <img src="https://avatars.githubusercontent.com/u/96014314?v=4" width="200"> | | ||
|
||
## 3. 타임라인 | ||
| 날짜 | 내용 | | ||
| --- | --- | | ||
| 24.01.08 | Ground Rules 논의, Step1 코드 작성 | | ||
| 24.01.09 | Step1 코드 마무리, Step1 PR | | ||
| 24.01.10 | Step1 리팩토링, Step1 PR | | ||
| 24.01.11 | Step2 코드 작성 | | ||
| 24.01.12 | Step2 리팩토링, Step2 PR | | ||
|
||
## 4. 실행 화면 | ||
### 4-1. Step 1 | ||
| 잘못된 입력 처리 | 진행 | 게임 종료 | | ||
| --- | --- | --- | | ||
| <img src="Screenshot/rsp_invalidinput.png" width="350"> | <img src="Screenshot/rsp_run.png" width="350"> | <img src="Screenshot/rsp_exit.png" width="350"> | | ||
### 4-2. Step 2 | ||
| 잘못된 입력 처리 | 진행 | 게임 종료 | | ||
| --- | --- | --- | | ||
| <img src="Screenshot/mcb_invalidinput.png" width="350"> | <img src="Screenshot/mcb_run_1.png" width="350"><br><img src="Screenshot/mcb_run_2.png" width="350"> | <img src="Screenshot/mcb_exit.png" width="350"> | | ||
|
||
## 5. 트러블 슈팅 | ||
### 5-1. 사용자 입력시, 문자열 유효성 검사 | ||
- 정규식을 이용하여 0에서 3까지의 한 자리 숫자만 받을 수 있도록 하였습니다. | ||
- 또한 SQL Injection 을 예방하는데 도움을 줍니다. | ||
- 사용된 정규식은, `#"^[0-3]$"#` 입니다. | ||
### 5-2. 함수 리팩토링 | ||
- 작성한 수도코드를 토대로 작성된 코드에서 함수를 분리하여야 하였습니다. 그러나 그 과정에서 중복 소스가 생성되었습니다. 결국 함수 리팩토링하게 되었습니다. | ||
- 특히 기존 코드에서 새 함수를 생성할 때는 함수의 이름을 직관적으로 만들었습니다. 유지보수에 용이하게 만드는 것이 포인트였기 때문입니다. | ||
- 이렇게 완성된 함수만 호출하여 재사용할 수 있기에 작업시간이 줄어들고 프로그램 유지보수에도 굉장히 좋아질 거라고 예상합니다. | ||
### 5-3. rawValue 사용 | ||
- enum의 형태에는 원시값 추가가 가능하여 불필요한 변수 선언을 하지 않게 되었습니다. | ||
```Swift | ||
enum GamePlayer: String { | ||
case player = "사용자" | ||
case computer = "컴퓨터" | ||
... | ||
} | ||
``` | ||
- 튜플(Tuple)을 사용하여 하나의 케이스에 서로 다른 연관값들을 저장하였고 아래와 같이 원시값을 불러올 수 있도록 하였습니다. | ||
- enum 에 대해서 새로운 방법을 알게 되는 계기가 되었습니다. | ||
```Swift | ||
switch gameResult { | ||
case .win: | ||
whoseTurn = .player | ||
print("\(whoseTurn.rawValue)의 턴입니다.") | ||
case .lose: | ||
whoseTurn = .computer | ||
print("\(whoseTurn.rawValue)의 턴입니다.") | ||
default: | ||
print("\(whoseTurn.rawValue)의 승리!") | ||
loopFlag = false | ||
} | ||
``` | ||
|
||
## 6. 팀 회고 | ||
| 햄찌 | 가마 | | ||
| --- | --- | | ||
| 제시한 프로젝트를 의도를 이해하고 로직을 순서대로 구현하기 위하여 수도코드부터 작성하였습니다. 그리고 추후 유지보수를 생각하여 메소드 목적에 따라 정규식 규칙을 정할 수 있도록 하였습니다. 팀원과 같이 작업하면서 메소드의 재활용성에 대해서 더 생각할 수 있었습니다. | 사용자로부터 문자열을 입력 받을 때 정규식을 통해 유효성과 특정 필터링을 걸 수 있다는 것을 체득하게 되어, 프로그램뿐만 아니라 코딩 테스트에서도 활용할 수 있을 것 같다고 긍정적으로 생각되는 보람 찬 프로젝트였습니다. | | ||
|
||
## 7. 참고 자료 | ||
- https://www.swift.org/documentation/api-design-guidelines/ | ||
- https://developer.apple.com/documentation/swift | ||
- 클린 코드 | ||
- 객체지향의 사실과 오해 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,54 +6,151 @@ | |
|
||
import Foundation | ||
|
||
let regex = #"^[0-3]"# | ||
func isNumber(_ input: String) -> Bool { | ||
return input.range(of: regex, options: .regularExpression) != nil | ||
enum GamePlayer: String { | ||
case player = "사용자" | ||
case computer = "컴퓨터" | ||
} | ||
|
||
enum GameResult { | ||
case draw | ||
case win | ||
case lose | ||
} | ||
|
||
func gameRun() { | ||
enum GameState { | ||
case rockScissorPaper | ||
case mukChiba | ||
} | ||
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 타입을 활용하여 어떤 게임을 실행할지 제어하는게 아닌, 단순히 명시적으로 보여주기 위함이라고 한다면 게임을 실행하는 함수명을 |
||
|
||
var gameState: GameState = .rockScissorPaper | ||
var whoseTurn: GamePlayer = .computer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. turn의 초기값이 computer인게 어색하다고 볼 수도 있을 것 같아요. |
||
|
||
func runGameFirstStep() { | ||
var loopFlag = true | ||
let regex = #"^[0-3]$"# | ||
|
||
while loopFlag { | ||
print("가위(1), 바위(2), 보(3)! <종료 : 0> :", terminator: " ") | ||
|
||
let userInput = readLine() | ||
let playerInput = readLine() | ||
|
||
guard let safeUserInput = userInput else { | ||
guard let playerInputChoice = playerInput else { | ||
print("잘못된 입력입니다. 다시 시도하여 주세요.") | ||
loopFlag = false | ||
return | ||
} | ||
|
||
if (!isNumber(safeUserInput)) { | ||
if (!isValidInput(input: playerInputChoice, regex: regex)) { | ||
print("0-3까지의 수를 입력해주세요.") | ||
continue | ||
} | ||
|
||
if (safeUserInput == "0") { | ||
if (playerInputChoice == "0") { | ||
print("게임 종료") | ||
return | ||
} | ||
|
||
judgeGame(userChoice: safeUserInput) | ||
let computerChoice = makeComputerChoice() | ||
let gameResult = judgeRockScissorPaper(playerChoice: playerInputChoice, computerChoice: computerChoice) | ||
|
||
print("컴퓨터: \(computerChoice)") | ||
printResult(gameResult: gameResult) | ||
|
||
if gameResult == .draw { | ||
continue | ||
} else { | ||
loopFlag = false | ||
gameState = .mukChiba | ||
whoseTurn = gameResult == .win ? .player : .computer | ||
runGameSecondStep() | ||
} | ||
} | ||
} | ||
|
||
func judgeGame(userChoice: String) { | ||
let computerChoice = makeComputerChoice() | ||
func runGameSecondStep() { | ||
var loopFlag = true | ||
let regex = #"^[0-3]$"# | ||
|
||
switch (userChoice, computerChoice) { | ||
case (_, _) where userChoice == computerChoice: | ||
print("비겼습니다!") | ||
case ("1", "3"), ("2", "1"), ("3", "2"): | ||
print("이겼습니다!") | ||
default: | ||
print("졌습니다!") | ||
while loopFlag { | ||
print("[\(whoseTurn.rawValue)의 턴] 묵(1), 찌(2), 빠(3)! <종료 : 0> :", terminator: " ") | ||
|
||
let playerSecondInput = readLine() | ||
|
||
guard let playerSecondChoice = playerSecondInput else { | ||
print("잘못된 입력입니다. 다시 시도하여 주세요.") | ||
loopFlag = false | ||
return | ||
} | ||
|
||
if (!isValidInput(input: playerSecondChoice, regex: regex)) { | ||
print("0-3까지의 수를 입력해주세요.") | ||
continue | ||
} | ||
|
||
if (playerSecondChoice == "0") { | ||
print("게임 종료") | ||
return | ||
} | ||
|
||
let computerSecondChoice = makeComputerChoice() | ||
let gameResult = judgeMukChiBa(playerChoice: playerSecondChoice, computerChoice: computerSecondChoice) | ||
|
||
print("컴퓨터: \(computerSecondChoice)") | ||
|
||
switch gameResult { | ||
case .win: | ||
whoseTurn = .player | ||
print("\(whoseTurn.rawValue)의 턴입니다.") | ||
case .lose: | ||
whoseTurn = .computer | ||
print("\(whoseTurn.rawValue)의 턴입니다.") | ||
default: | ||
print("\(whoseTurn.rawValue)의 승리!") | ||
Comment on lines
+103
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enum의 rawValue를 활용하신 부분 너무 좋아요👍 |
||
loopFlag = false | ||
} | ||
} | ||
} | ||
|
||
func isValidInput(input: String, regex: String) -> Bool { | ||
return input.range(of: regex, options: .regularExpression) != nil | ||
} | ||
|
||
func makeComputerChoice() -> String { | ||
let computerChoice = String(Int.random(in: 1...3)) | ||
return String(computerChoice) | ||
} | ||
Comment on lines
118
to
121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 굳이? 라는 생각이 드는 부분이긴 한데 |
||
|
||
gameRun() | ||
func judgeRockScissorPaper(playerChoice: String, computerChoice: String) -> GameResult { | ||
switch (playerChoice, computerChoice) { | ||
case (_, _) where playerChoice == computerChoice: | ||
return .draw | ||
case ("1", "3"), ("2", "1"), ("3", "2") : | ||
return .win | ||
default: | ||
return .lose | ||
} | ||
} | ||
|
||
func judgeMukChiBa(playerChoice: String, computerChoice: String) -> GameResult { | ||
switch (playerChoice, computerChoice) { | ||
case (_, _) where playerChoice == computerChoice: | ||
return .draw | ||
case ("1", "3"), ("2", "1"), ("3", "2") : | ||
return .lose | ||
default: | ||
return .win | ||
} | ||
} | ||
Comment on lines
+123
to
+143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 숫자만으로는 어떤 것을 기준으로 승패가 나뉘는지 명확하게 이해하기가 어려울 수도 있어서 choice또한 enum으로 정의해준다면 더 쉽게 이해할 수 있을꺼 같아요🙂 |
||
|
||
func printResult(gameResult: GameResult) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. argument label을 추가해준다면 해당 함수를 사용하는 곳에서 가독성이 더 좋아질 수도 있을 것 같아요🙂 func printResult(of gameResult: GameResult) { } printResult(of: gameResult) |
||
switch gameResult { | ||
case .draw: | ||
print("비겼습니다!") | ||
case .win: | ||
print("이겼습니다!") | ||
case .lose: | ||
print("졌습니다!") | ||
} | ||
} | ||
|
||
runGameFirstStep() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금은 함수만으로 기능을 구현했지만 나중에 시간이 되신다면 struct/class나 protocol을 활용하여 가위바위보와 묵찌빠를 두 가지 타입으로 나누어 구현해보시면 좋을 것 같아요🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum을 활용하신 부분 너무 좋아요👍