본문 바로가기

코딩 테스트 풀이/프로그래머스

[LV1] 다트 게임

문제 설명

다트 게임

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

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

  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

입출력 예제

예제 dartResult answer 설명
1 1S2D*3T 37 11 * 2 + 22 * 2 + 33
2 1D2S#10S 9 12 + 21 * (-1) + 101
3 1D2S0T 3 12 + 21 + 03
4 1S*2T*3S 23 11 * 2 * 2 + 23 * 2 + 31
5 1D#2S*3S 5 12 * (-1) * 2 + 21 * 2 + 31
6 1T2D3D# -4 13 + 22 + 32 * (-1)
7 1D2S3T* 59 12 + 21 * 2 + 33 * 2

문제부터 엄청나게 길다. 간단히 요약하자면, 다트게임을 하는데 점수, 배수, 특수 효과로 나누어져 있고, 이를 순서대로 쓴 문자열로부터 점수를 알아내라는 것이었다.

문자 기능
숫자 점수(0~10)
S 점수 * 1
D 점수^2
T 점수^3
* 방금 얻은 점수와 직전 얻은 점수에 * 2
# 방금 얻은 점수에 * -1

 

처음에는 for문을 이용해 풀려고 했다. 나는 코딩 초고수가 아니기 때문에 간단하게 해결하기도 어려웠고, 조건도 많아 어쩔 수 없이 코드를 길게 작성할 수 밖에 없었다. 

  1. for문 작성(변수 i), 현재 점수를 담을 score 변수, 누적 점수를 담을 answer 변수 선언
  2. dartResult의 i번째 문자가 정수라면 score를 0으로 초기화한 후 dartResult의 i번째 문자를 score에 담음
  3. dartResult의 i번째 문자가 S, D, T라면 score에 각 문자에 맞는 연산 실행 후 answer에 score 더함
  4. dartResult의 i번째 문자가 *, #라면 answer에 각 문자에 맞는 연산 실행
  5. 최종 결과 = answer

이 방법을 사용했었는데, 계속 결과가 안나와서 조건을 다시 읽어보니 내가 문제를 잘못 이해하고 있었던 것이었다. 허

잘못 알았던건 *인데, *이 나오면 지금까지 얻은 점수에 2배를 하는건줄 알고 있었지만, 알고보니  이번에 얻은 점수와 바로 전에 얻은 점수 2개에만 2배를 해주는 것이었다.

그렇게 되면 N번째의 score와 N-1번째의 score을 구분할 수 있는 방법이 필요했는데, 이 방법으로는 방금 얻은 점수는 알 수 있지만 직전 얻은 점수는 알 수 없을 것 같았다. 따라서 다른 방법을 사용하기로 했다.

 

그 다음 생각한 방법은 문자열 dartResult을 [1S, 2D, *, 3T] 등의 배열로 나누어 각 배열의 요소별로 계산한 후 마지막에 *와 #을 적용해 주는 것이었다. 배열을 사용한다면 위에 발생한 문제인 N, N-1번째 점수도 구분 가능하고, 배열에 미리 점수들이 담겨 나오기 때문에 계산도 훨씬 용이할 것 같았다.

  1. 문자열을 S, D, T, *, # 기준으로 나누어 배열로 만듬
  2. 각 배열 요소를 연산하여 다트를 한번 던졌을 때의 점수 계산
  3. 배열에 *이 있다면 * 기준 N-1, N-2번째 요소에 2를 곱함
  4. #이 있다면 # 기준 N-1번째 요소에 -1을 곱함
  5. 연산이 완료된 요소들을 모두 더함

우선 S, D, T, *, # 를 기준으로 문자열을 나누어 주어야 했다. 

여기선 정규 표현식과 while문을 이용하기로 했다.

  1. dartResult의 0번째 문자부터 반복 시작
  2. 반복 횟수(요소 인덱스)를 나타내주는 변수 i와 문자열을 자르기 시작할 인덱스 n을 선언 후 모두 0으로 초기화
  3. 자른 문자열을 넣을 빈 배열 score 선언
  4. i번째 문자가 정수라면 통과. i는 1씩 증가
  5. 정규표현식을 통해 i번째 문자가 S, D, T, *, #라면 n부터 i까지 배열을 잘라 score에 push
  6. 다음에 자를 문자열은 현재 S, D, T, *, #가 나온 인덱스의 다음 인덱스부터이므로, n에 i+1을 저장
  7. i가 dartResult의 길이만큼 반복하면 종료
let i = 0
let n = 0
let arr = []
while (i < dartResult.length) {
	if (dartResult[i].match(/[DST*#]/)) {
    	score.push(dartResult.slice(n, i + 1))
      	n = i + 1
    }
    i++
}

"1S2D*3T"를 예로 들어보자. 이와 같은 과정을 거치면 배열 score는 ["1S", "2D", "*", "3T"]라는 배열이 된다. 

그 다음은 배열의 요소들을 연산해주면 된다. 

 

배열 요소 연산은 정수 부분과 문자 부분으로 나누어 문자의 종류에 따라 정수 부분에 연산을 해주면 된다. 

이때 정수는 0~10까지 존재할 수 있기 때문에 score[0][1]로 선언해주었다간 오류가 나게 된다. 

만약 score[0]이 "1S"라면 길이는 2, 정수 부분은 1이 되고,

score[0]이 "10S"라면 길이는 3, 정수 부분은 2가 되므로 0부터 score[0]의 길이 - 1까지 slice 해주면 정수 부분만 남게 된다.

이렇게 구한 정수 부분을 num이라는 변수에 저장해준다.

그리고 if-else if문과 includes를 통해 score[0]이 S를 포함한다면 score[0]을 num*1로, D를 포함한다면 Math.pow(num, 2), T를 포함한다면 Math.pow(num, 3)으로 바꾸어 주면 된다. 모든 배열 요소에 실행해야 하므로 for문을 사용한다.

for (let i = 0; i < score.length; i++) {
    let num = score[i].slice(0, score[i].length - 1)
    if (score[i].includes("S")) {
      score[i] = num * 1
    } else if (score[i].includes("D")) {
      score[i] = Math.pow(num, 2)
    } else if (score[i].includes("T")) {
      score[i] = Math.pow(num, 3)
    }
}

이 과정까지 끝나면 score 배열은 S, D, T 연산이 끝난 정수들과 *, #만 남게 된다.

*과 #의 연산은 배열의 N-1, N-2번째 요소들을 이용해 하는 것이기 때문에 for문을 사용하여 요소에 접근 후 연산을 실행하였다.

score의 길이만큼 for문 실행(변수 = i)

  1. i번째 요소가 *이라면 i-2, i-1번째 요소에 2를 곱함
  2. i번째 요소가 #이라면 i-1번째 요소에 -1을 곱함

그런데, 여기서 또 문제가 생겼다. 바로 i-2번째 요소가 정수가 아닌 경우!

"1S*2S*3S"를 예로 들어보면, 위 과정 직전까지 score = [1, "*", 2, "*", 3]인 상태일 것이다.

첫번째 *까지는 정상 작동 하지만, 두번째 *의 경우 i-1은 2지만 i-2는 *이 된다. 이 경우 1에 *이 적용되어야 하는데 *에 *이 적용되어버려 오류가 나게 된다.

따라서 i-2번째 요소까지 따져서 i-2번째 요소가 정수가 아니라면 i-3번째 요소에 2를 곱하도록 하였다.

#의 경우에는 # 앞에 #나 *이 나올 일이 없기 때문에 별다른 조치를 취해주지 않았다.

for (let i = 0; i < score.length; i++) {
    if (score[i] == "*") {
      if (!Number.isInteger(score[i - 2])) {
        score[i - 1] *= 2
        score[i - 3] *= 2
      }else {
        score[i - 1] *= 2
        score[i - 2] *= 2
      }
    } else if (score[i] == "#") {
      score[i - 1] *= -1
    }
}

마지막은 filter() 함수를 통해 score 배열에서 정수 요소들만 추린 뒤 reduce() 함수로 배열의 요소들을 모두 더해주었다.

 

function solution(dartResult) {
    var answer = 0;
    let i = 0
    let n = 0
    let score = []
    while (i < dartResult.length) {
      if (dartResult[i].match(/[DST*#]/)) {
        score.push(dartResult.slice(n, i + 1))
        n = i + 1
      }
      i++
    }
    for (let i = 0; i < score.length; i++) {
      let num = score[i].slice(0, score[i].length - 1)
      if (score[i].includes("S")) {
        score[i] = num * 1
      } else if (score[i].includes("D")) {
        score[i] = Math.pow(num, 2)
      } else if (score[i].includes("T")) {
        score[i] = Math.pow(num, 3)
      }
    }
    for (let i = 0; i < score.length; i++) {
      if (score[i] == "*") {
        if (!Number.isInteger(score[i - 2])) {
          score[i - 1] *= 2
          score[i - 3] *= 2
        } else {
          score[i - 1] *= 2
          score[i - 2] *= 2
        }
      } else if (score[i] == "#") {
        score[i - 1] *= -1
      }
    }
    answer = score.filter(a => Number.isInteger(a)).reduce((a, b) => a + b)
    return answer;
}

휴~ 끝!

이 문제가 카카오 코테였다는데......

역시 모두가 가고 싶은 기업은 다르구먼...

'코딩 테스트 풀이 > 프로그래머스' 카테고리의 다른 글

[LV1] 햄버거 만들기  (0) 2023.01.16
[LV1] 체육복  (0) 2023.01.14
[LV1] 소수 찾기  (0) 2023.01.11
[LV1] 문자열 내 마음대로 정렬하기  (0) 2023.01.09
[LV1] 행렬의 덧셈  (0) 2023.01.08