Skip to content

Quokkaaa/ios-rock-paper-scissors

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 

Repository files navigation

iOS 커리어 스타터 캠프

묵찌빠 프로젝트 저장소

가위바위보 & 묵찌빠 게임 프로젝트

🗓 기간 : 2022.02.14 ~ 2022.02.18
👨🏻‍💻 팀원 : 쿼카, 민성
✍️ 리뷰어: 제리

타임 라인

월 - 팀 그라운드 룰 정하기 및 개인공부(공식문서 읽기) 및 학습활동 화 - STEP1 순서도 그리기 및 STEP1 PR 수 - STEP1 피드백 읽기 및 리펙토링 목 - STEP2 순서도 그리기 및 STEP2 구현 및 에러해결 금 - STEP2 PR 및 READ ME 작성 및 재귀함수 장.단점 학습

순서도

[STEP1]

STEP1

[STEP2]

FlowChart

학습 목표

  • Swift의 Optional 안전하게 처리하기
  • if와 switch 조건문의 차이와 장단점 비교해보기
  • 순환함수(재귀함수)와 반복문의 장단점 비교해보기
  • 함수가 한 가지 일만 하도록 기능 분리하기
  • guard 구문의 이해와 활용
  • 동료와 협업자세 고민하기

기능 구현

[타입]

  • private HandGame : Hand Game의 필요한 기능과 특성들을 담은 값
  • private Kind : 게임의 종류를 담은 값
  • private Hand : HandGame에 필요한 패를 담은 값
  • private Player : 사용자와 컴퓨터를 담은 값
  • private Guide : 게임 실행시 출력되는 메세지를 담은 값
  • private Result : 게임결과시 출력 메세지를 담은 값
  • private Erorr : 잘 못된 입력시 출력되는 메세지를 담은 값

[프로퍼티]

  • private hasUserWin : 묵찌빠의 공격자 턴을 구별 해주기 위한 프로퍼티

[함수]

  • play() : 가위바위보 및 묵찌빠 게임을 시작
  • private printInputGuidanceMessage() : 게임에 필요한 입력가이드를 제시
  • private receiveUserInputHand() : 사용자 패를 입력 받는다
  • private isValueZero() : 입력된 패가 "0"(게임종료)인지 확인
  • private convertedUserHand() : 사용자가 입력한 패를 게임에 맞게 변환
  • private generatedRandomHand() : 컴퓨터의 패 랜덤으로 생성
  • private judgeUserGameResult() : 가위바위보 결과를 판정
  • private playMukjipa() : 묵찌빠 게임을 시작
  • private printRockPaperScissorsWinnerTurn() : 가위바위보 승리자 턴 출력
  • private isWrongInputted : 정상적인 입력 외에 값인지 검사
  • private printGameResult() : 게임결과 메세지를 출력
  • private switchTurn() : 턴을 변경
  • private printMukjipaWinnerTurn() : 묵찌빠게임 현재턴을 출력
  • private printMukjipaWinner() : 묵찌빠게임 승자를 출력
  • private judgeMukjipaWinner() : 묵찌빠게임 결과를 판정

문제 해결 & 어려웠던점 & 고민했던점 & 궁금한점

[STEP1]

문제 해결

  • 스위치문으로 1,2,3,0 의 사용자 입력 숫자가 있어 스위치문에 제격이라는 판단이 들었다. 그래서 사용자 입력을 그냥 받는 함수와 검증을 하는 함수를 구분하였다. 검증을 하는 함수에서 123을 선택시 입력받은 값대로 바로 리턴하게 처리하였고, 디펄트값에는 다시입력하라는 메세지와함께 재귀함수 형태로 진행하였는데 0이 굉장히 골치아팠다. 스위치문의 장점은 선택지를 한정하여 그외 값은 디펄트로 처리하는 깔끔한 기능이라고 생각했으나 return 처리를 해줘야하는 작업이 들어가서 순수하게 값을 반환하는게 아니라 함수내부에서 0에대한 작업을 처리해주거나 미리 0을 처리해주는 함수를 빼서 진행해주어야 했다. 모두다 return값을 가져갈 값들이 아니라면 if문을 고려해보는 것도 좋으며 반드시 선택지를 제공할 수 있다고해서 switch문을 고집하는건 주의하자.
  • zero를 넣었더니 generatedRandomHand임의의수 생성할때에도 같이 섞여서 오류가 났다. generatedRandomNumber생성하는 함수내 filter를 이용하여 0이 아닌 수들만 저장하는 식으로 처리했다.

어려웠던점

  • 네비게이터와 드라이버를 30분단위로 체인지하고 함수는 2개, 사용자정의 타입 등 짧지않는 선에서 커밋을 하기로했으나 지켜지지않았다. 암묵적으로 리펙토링을 하면서 서로 코드 교정하고 계속 작성하다보니 그렇게 3,4시간이 흘렀다. 체력적으로도 힘들었고 쉬는시간도 적절히 갖지 못한것같다. TIL을 쓰기 직전에 녹초가되어버렸다. 아무튼 네비게이터와 드라이버를 적절히 분리하여 짝프를해야 안정적이고 규칙적으로 코드를 구현할 수 있는것같다.
  • printGameResult()에서 비기고 play()로 재귀 playMukjipa가 진행되고 결과가 나왔을 때 스택구조로 돌아가면서 printGameResult() 끝났을 때 다음 문장인 playMukjipa를 바로 실행해버림 이걸 방지하기 위해 플레이 묵찌빠에 조건문을 달아줬다.

고민했던점

  • 네이밍을 HandShape로 할지 Hand로 할지에 대해서 고민을 많이했지만 Hand로도 패라는 의미를 표현할 수 있다고 판단되어 수정
  • 어려운 난관에 부딪혔을때 해결하기에 앞서 문제를 파악하고 미리 메모해두는것이 시간이 지났을때 잊혀지기때문에 매우 중요하다는 것을 알았다. 내가 어떤 문제에 부딪혔고 그걸 해결하기위해서는 재귀함수 형태로 진행하거나 함수기능을 분리하여 코드 개선을 한 부분을 생각이났을때 즉각적으로 메모하는게 좋음
  • Rock-Paper-Scissors 타입내 enum을 만들어 코드읽기 유연하게 표현하려고 했으나 우려되는점이 코드가 너무 길어질것같다는 점이 있었습니다.

[STEP2]

문제 해결

  • 묵찌빠게임에서 convertedUserHand 함수로 패를 반환하는데 묵찌빠에서도 재활용이 가능해보였다. 재활용을 하게될시 두가지 문제점이 발생합니다. 첫번째는 패만 반환되는것이아니라 0 대한 값도 반환해주어야한다는 점이고 두번째는 가위바위보 게임에서는 사용자입력이 1번이 가위이고 2번이 바위이지만 묵찌빠에서는 1번이 바위이고 2번이 가위이다.

0에대한 해결방법) 먼저 사용자입력을 받고나서 바로 0인지에대해 검증하는 함수로 연결을 해주는것이다. 0은 게임을 종료하는 로직이기때문에 가장 먼저 처리해주는것이 적절하기도하다. 그리고 난 후에 Handcase end를 제거해주었다. 이방법은 묵찌빠에서도 적용된다.

가위바위보게임시 입력 번호와 묵찌빠게임 입력번호 1,2의 차이 해결방법) gamd Kind라는 열거형을 만들어 가위바위보와 묵찌빠게임에 대한 case를 만들고, convertedUserHand함수 매개변수에 gamekind타입을 추가해주었다. 그래서 가위바위보게임일때는 1번이 가위, 2번이 바위로 처리가되지만 묵찌빠일때는 if문으로 gamekind == .mukjipa일때는 1,2번을 바꾸어 Hand를 return하는 방식을 선택하였다. 그렇게 두줄의 코드를 추가함으로써 convertedUserHand 함수를 재사용할 수 있다.

어려웠던점

  • 묵찌빠게임할때에도 convertedUserHand함수를 재활용 할 수 있겠다는 판단이 들었다.묵찌빠게임은 잘 못 입력했을때 사용자입력을 다시 받는 재귀함수 패턴이 아니라 사용자 턴이 바뀌고 입력을 받게된다. convertedUserHand함수에서 사용자턴으로 바뀔때 실행될필요없는 default값 때문에 초기에는 if문을 사용하여 default를 빼주는것을 고민해보았으나 잘 못 입력된것에대해서 지속적으로 메세지를 표해주고 재귀함수필요없이 값을 받아주는게 switch문의 편리한점때문에 냅두었다. 그런데 재귀가 리턴하는 과정에서 로직에러가 계속발생하고 에러파악하기로 어려워져 불필요한 재귀는 최소화하는 방향으로 if문으로 수정하였다.

고민했던점

  • enum Type Naming스위프트 랭귀지 가이드 문서 를 참고하여 단수로 지정하였다.
  • 묵찌빠 게임에서 사용자입력을 받을 함수를 재활용할까고민했는데 재사용성에 대해서 고민해보는것도 좋지만 이를 해결하기위해 만들어지는 함수가 오히려 더 많아지는 역설적인 상황이 생기기도 한다. 이번기회에 재사용성에대해서 고민해보고 삽질을 할 수 있는 좋은기회였던것같다.
  • 턴 체인지에 대한 방법고민을 많이했던것같습니다. 그중에서 가장 유연하고 편리한 방법으로 var hadUserTurn 프로퍼티를 만들어 턴을 구별해주는 방식으로 처리하였다.
  • 재사용성 코드에대해서 고민을 해보았습니다.

궁금한점

  • 사용자입력받는 함수 실행전에 lldb에 초기화된 값이 찍히는게 궁금합니다. Case첫번째값이 default로 찍히는데 이는 왜그런건가요 ?? => 프로그램 실행시 HandGame 타입으로 인스턴스를 초기화하면 그 내부에있는 함수와 타입 그리고 함수 내부에 있는 프로퍼티들까지 메모리 주소를 할당받는다. 이때 함수내부에 있는 프로퍼티가 진짜 값을 할당받기 이전까지 기본값을 가지고 있고 실제로 실행된 이후에는 할당받은 값으로 변경이 된다.

  • 네이밍문서(https://www.swift.org/documentation/api-design-guidelines/)에 보면 사이드 이펙트가 없는 함수에는 함수명을 명사로 짓는다걸 확인했어요. 저의가 작성한 함수드들은 대부분 사이드이펙드가 없다고 판단되는데 제리에 생각은 어떠한가요?

공부한 내용

  • Nested Type 중첩타입을 사용할때 struct나 class내에 enum을 사용하게되면 꾀나 유용할 수 있다고 나온다. 추측해보면 예씨문데 struct문 안에 enum이 있는데 클래스가 아닌 이유는 둘다 Value Type이라서 그럴 수 있을까 ? 라는 의문이든다.

중첩타입은 외부에서 인스턴스를 생성할때 변수명을 접두사를 붙여 생성해준다고 랭귀지 가이드에서 확인 해 볼 수 있다. 접두사는 앞에 접미사는 뒤에 덧붙이는 방식인데 예시단어를 봐보자 접두사 - 시퍼렇다, 맨손, 빨간얼굴, 멍든팔 접미사 - 선생님, 쿼카야, 지우개, 먹히다 - 히

재귀함수와 반복문의 특징 및 차이점

재귀함수와 반복문의 특징

[재귀함수]

  • 재귀함수란 함수가 구문내에서 자기자신을 호출하는 것

[장점]

  • 재귀는 가독성을 증가시키고 코드를 작성하고 디버그하는데 필요한 시간을 줄여준다. 만약에 함수에 입력하는값이 작다는걸 알고있고 깔끔하게 만들고싶다면 재귀는 확실히 좋은 선택일 수 있지만.. 반면 입력값이 크다면 속도와 메모리를 희생할만큼 매력적이진않고 성능에 좋지않다..

  • 트리 순회에서 좋다.
특정 리프(또는 노드)를 찾을 때 이러한 트리를 탐색하는 더 효율적인 방법 중 하나는 찾고 있는 값을 찾을 때까지 해당 분기가 끝날 때까지 단일 분기를 재귀적으로 추적하는 것입니다. 위 방식은 선주문 트리탐색에 사용할때 유용하고함.

[단점]

  • 예상치 못한 무한 재귀가 발생할 수 있으며 요즘은 메모리용량이 커 해당사항이 거의 없지만 stack메모리가 많이쌓여 stackOverFlow에 가능성이 나올 수 있다. 될때까지 값을 유지해야하기때문에 메모리할당은 반복함수의 메모리 할당보다 크다.

  • 속도가 느리고 메모리를 더 차지한다. 반복문 보다 느리다. 속도와 메모리적인 부분에서 재귀함수를 반복문보다 더 효율적이게 구현하기는 어렵다. 재귀가 느린 이유는 새로운 스택 프레임의 할당이 필요하기 때문입니다. 왜냐하면 전에 실행됬던 함수의 메모리를 유지한 상태에서 새로운 메모리를 할당받아야 하기때문이다.

[반복문]

  • 반복문이란 조건이 거짓이될때까지 반복적으로 실행되는 것이다.

[장점]

  • stack을 사용하지않으므로 재귀보다 속도가 빠르다.
  • 메모리 소모를 덜한다.

[단점]

  • 코드가 길어 질 수 있다.
  • 조건이 거짓이되지않을시 무한 루프에 빠질 수 있다.
  • 루프 조건이 실패하면 반복이 종료된다.

재귀함수와 반복문의 차이

  • 재귀함수가 반복문보다 더 예기치 않은 무한루프에 빠질 위험이 높다. 반복문은 특정 조건이 실패하지않는 경우 재귀는 함수가 기본 케이스에 수렴하는 방식으로 입력을 줄이지 않을 때 발생합니다. 수렴하는 예시 코드)
func factorial(num: Int) -> Int {
    if num == 1 {
        return 1
    }
    return (num * factorial(num: num - 1))
}
factorial(num: 2) 

로직순서) 2 * ? -> return 1 -> ? = 1 -> 2 * 1 -> result : 2 return 1이 나왔을때 stack 메모리에서는 pop이 발생하여 역순으로 값을 되돌림

  • 재귀함수의 형태는 답정너같은 원리로 원하는 값이 나올떄까지 자신의 함수를 호출하기때문에 추가적인 코드를 구현할 필요가 없지만, 반복문은 특정 값이 나올때까지 반복문을 추가로 구현해줘어야 한다
  • 반복문은 stack구조를 사용하지않아 메모리가 쌓이지않는다. 그래서 속도가 재귀보다 빠르다.

TWL (This week We Learned)

잘한 점

-기능구현에만 신경쓰지 않았고 에러처리와 네이밍등 가독성에 신경을 많이 썼다

  • 5시간에 가까운 긴시간동안 재귀함수 무한루프를 에러를 팀원과 같이 lldb를 활용하여 해결하려고 노력하였고 팀원덕에 해결 해볼 수 있었다.
  • 재귀함수 장,단점 파악 및 재귀함수 로직 이해를 하기위해 팀원이랑 공부하고 학습해보았다.
  • 함수의 기능을 재활용할 수 있는 코드를 구현해보았고 함수가 한가지 기능을 할 수 있도록 작성 및 피드백을 받아보았다.
  • 모르는 것이 있거나 이해가되지않을때 팀원에게 적극적으리 질문하였다.
  • 우리가 어려움웠던 점과 고민했던점 궁금했던점이 생겼을때 그 즉시 바로 메모장에 메모를 하려고 노력하였다.

개선할 점

  • 객체지향적인 프로그래밍을 할 수 있도록 노력 해야겠다.
  • commit단위를 정한 부분을 더 잘 지킬 수 있었으면 좋았을것같다. 한개의 함수기능 또는 사용자 정의 타입 구현 또는 시간 15분
  • READ 작성과 타임라인 등을 어떻게 정하고 작성해나아갈지 미리얘기했으면 더 좋았을 것 같다.
  • 궁금했던점이 생겼을때 그 즉시 바로 메모장에 메모를 하려고 노력해보자.

학습 키워드

  • CaseIterable Protocol
  • Equatable Protocol
  • switch, if, guard
  • enum
  • private
  • computed property

팀원 칭찬하기

Minseong이 쿼카에게 - 다양한 시도를 할수 있게 이끌어 주었고 처음 보는 문법을 이해할 수 있게 잘 알려 줘서 감사해요~ 블로그 작성 및 내가 느꼈던 점이나 공부할 점 고민한 점에 대해 정리하는 법을 어깨너머로 배울 수 있는 시간을 제공해 준 것에 대해 감사합니다. 😄 쿼카가 Minseong에게 - 의견을 적극적으로 말한것에대해서 잘 수용해주려는 모습이 멋집니다. 그리고 무한루프에 빠진걸 해결하는데 체력적으로 지쳤지만 민성은 더 끈기를 가지고 해결하려는 모습이 배울점인것같아요 ㅎㅎ 또한 이해가 잘되지않거나 잘못이해하고있는것을 확인받을 수 있어서 든든함도 느껴졌어요. 그리고 공감대가 잘 맞아서 중간중간 시시콜콜한 토크토크를 하는게 너무 즐거웠습니다. 😁

About

iOS 가위, 바위, 보 게임 시작 저장소

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%