코딩테스트

프로그래머스 - 다트게임 (레벨1, swift)

momo_9 2020. 6. 28. 22:34

 

문제출처

programmers.co.kr/learn/courses/30/lessons/17682

 

문제설명 

카카오톡에 뜬 네 번째 별! 심심할 땐? 카카오톡 게임별~

 

카카오톡 게임별의 하반기 신규 서비스로 다트 게임을 출시하기로 했다. 다트 게임은 다트판에 다트를 세 차례 던져 그 점수의 합계로 실력을 겨루는 게임으로, 모두가 간단히 즐길 수 있다.
갓 입사한 무지는 코딩 실력을 인정받아 게임의 핵심 부분인 점수 계산 로직을 맡게 되었다. 다트 게임의 점수 계산 로직은 아래와 같다.

  1. 다트 게임은 총 3번의 기회로 구성된다.

  2. 각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.

  3. 점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산된다.

  4. 옵션으로 스타상(*) , 아차상(#)이 존재하며 스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 아차상(#) 당첨 시 해당 점수는 마이너스된다.

  5. 스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다. (예제 4번 참고)

  6. 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (예제 4번 참고)

  7. 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (예제 5번 참고)

  8. Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.

  9. 스타상(*), 아차상(#)은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있다. 

0~10의 정수와 문자 S, D, T, *, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.

 

입력형식

    - 점수|보너스|[옵션]으로 이루어진 문자열 3세트.
  예)  1S2D*3T

    - 점수는 0에서 10 사이의 정수이다.

    - 보너스는 S, D, T 중 하나이다.

    - 옵선은 *이나 # 중 하나이며, 없을 수도 있다.

 

출력형식

3번의 기회에서 얻은 점수 합계에 해당하는 정수값을 출력한다.
예) 37

 

입출력 예제

예제dartResultanswer설명
11S2D*3T371(1) * 2 + 2(2) * 2 + 3(3)
21D2S#10S91(2) + 2(1) * (-1) + 10(1)
31D2S0T31(2) + 2(1) + 0(3)
41S*2T*3S231(1) * 2 * 2 + 2(3) * 2 + 3(1)
51D#2S*3S51(2) * (-1) * 2 + 2(1) * 2 + 3(1)
61T2D3D#-41(3) + 2(2) + 3(2) * (-1)
71D2S3T*591(2) + 2(1) * 2 + 3(3) * 2

 

 

문제풀이

이거 진짜 어려웠음ㅠㅠ 

 

1) dartResult를 1세트씩 나눠주기

2) 각 세트별 점수 계산하기

3) 총 점수 return 해주기

 

이렇게 3가지만 해결해주면 되는 문제인데 일단 1번 해결하는데 엄청 오래걸렸다ㅠㅠ

 

1) dartResult를 1세트씩 나눠주기

한 세트 끝에 옵션이 붙어있는 경우도 있고 아닌 경우도 있어서 세트를 쪼개는 기준을 잡기가 굉장히 애매했다.

결국 dartResult를 반복문을 통해 한 글자씩 탐색하여 temp문자열에 붙여주다가 D,T,S를 만나면 해당 값을 1세트로 하여 값을 저장해주고 그 뒤에 *, #올 경우 앞의 값에 붙여주도록 해주었다.

 

dartResult 문자열을 탐색할 때, 해당 문자가 숫자인지 아닌지를 판별하기 위해 형변환을 이용하였다.

   Int(String(word)) == nil

해당 문자를 Int로 변환해 보고 nil이 나오면 형변환에 실패하였으므로 숫자가 아닌 문자(D,T,S,*,#)로 판단하면 된다.

 

 

이제 slpitResult에는 ["1D", "2S#", "10S"] 와 같은 식으로 값이 저장된다.

처음에는 이렇게 각 세트별로 쪼개지 않고 dartResult를 순차적으로 읽으며 바로바로 점수를 계산하도록 했으나, 옵션을 계산할 때 앞의 옵션도 다시 체크하여 점수를 계산해줘야 했기 때문에 세트별 구분의 필요성을 느꼈다.

 

2) 각 세트별 점수 계산하기

이제 각 세트별로 점수를 계산해주면 되는데, 여기서 주의할 점은 옵션계산이다.

옵션 계산을 정리하면 아래와 같다.

 

아차상

  [1] 해당 점수만 마이너스가 된다

스타상 

  [1]  해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 첫번째 기회에 나올 경우 첫번째 점수만 2배가 된다.

  [2] 스타상이 중첩 될 경우 중첩된 스타상 점수는 4배가 된다 -> 바로 전 세트에 스타상이 있다면 바로 전 세트의 점수는 4배, 해당 세트는 2배가 된다.

  [3] 스타상과 아차상이 중첩될 경우 중첩된 아차상은 -2배가 된다. -> 바로 전 세트에 아차상이 있다면 바로 전 세트의 점수는 -2배, 해당 세트는 2배가 된다.

 

스타상일 경우 바로 전 세트에 스타상 또는 아차상이 있는지 확인하고 점수 계산을 해줘야 하는데, 사실 해당 세트의 옵션이 스타상이라면 이전 세트의 옵션은 확인하지 않고 이전 세트의 점수를 무조건 2배 해주면 된다. 아래 자세히 살펴보자.

 

   1. 각 세트별 점수를 한 글자씩 읽어들이며 숫자인지 문자인지 체크한다. 숫자는 2자리 수일수도 있기 때문에 number 변수에 담을 때는 string 형태로 한 글자씩 담고 계산이 필요한 순간에 한번에 Int형으로 변환하도록 해주었다.

       -> if Int(String(j)) == nil

 

  2. 문자인 경우 switch문을 이용해서 S, D, T, *, # 에 대한 계산을 각각 해주었다. 먼저 S, D, T인 경우 각각의 규칙에 맞는 계산을 해주고 각 세트별 점수를 저장하는 score배열에 담아 준다. S인 경우 1제곱, D인 경우 2제곱, T인 경우 3제곱을 해주면 된다.

      ->  case "S": 

            scores.append(Int(number)! * 1)

           case "D":

            scores.append(Int(number)! * Int(number)!)

           case "T":

            scores.append(Int(number)! * Int(number)! * Int(number)!)

 

  3. *인 경우 해당 점수는 2배를 해주고, 해당 세트가 첫번째 세트가 아니라면 앞의 세트의 점수도 2배를 해주면 된다.

여기서 앞의 세트 옵션을 굳이 확인할 필요가 없이 무조건 2배만 해주면 되는데, 앞의 옵션이 "*"일 경우 이미 해당 점수에 2배 계산이 되어 있고, "#"일 경우 해당 점수에 이미 마이너스(-) 값이 저장되어 있기 때문에 이번 세트에서는 무조건 이전 세트의 점수를 2배만 해주면 규칙에 맞게 계산이 된다.

해당 세트가 첫번째 세트인 경우는 해당 세트만 계산하면 되기 때문에 if문을 이용해 첫번째 세트인지 확인하도록 한다.

    -> case "*":

           scores[scores.count-1] *= 2

            if i > 0 {

                scores[scores.count-2] *= 2

            }

 

 4. #인 경우 해당 점수에 -1를 곱해주어 점수를 마이너스로 바꿔준다.

   -> case "#":

          scores[scores.count-1] *= -1

 

5. score배열에 담긴 세트별 점수를 모두 더하여 반환한다

   -> scores.reduce(0) { $0 + $1 }