프로그래머스 - [1차] 다트 게임

Lellow_Mellow·2023년 6월 3일
1
post-thumbnail

✨ Lv. 1 - [1차] 다트 게임

문제 링크 : https://school.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


풀이 코드 + 설명

조건이 많습니다. 우선 조건을 하나씩 간단하게 정리해보겠습니다.

  • 한 번의 다트 게임은 점수 | 보너스 | 옵션 으로 이루짐.
  • 숫자는 0 - 10 의 값을 가짐.
  • 보너스는 항상 존재하며, S, D, T로 각각 1제곱, 2제곱, 3제곱을 의미.
  • 옵션은 *, # 이며, 존재하지 않을수도, 중복으로 존재할 수도 있음.
  • *은 이전 점수와 현재 점수에 x 2.
  • #는 현재 점수에 x -1.
  • 다트 게임 점수의 총합을 구하여라.

이를 바탕으로 아래와 같은 순서로 코드를 작성하였습니다.

  • 각 게임의 점수를 담을 result 배열 선언
  • 점수를 담을 current 변수 선언
  • dartResult를 순회하며 아래를 반복
    - 숫자면 current에 더하기
    - *인 경우, result의 가장 뒤에 있는 2개의 값에 2를 곱하기
    - #인 경우, result의 가장 뒤에 있는 값에 -1 곱하기
    - S, D, T인 경우, current의 1, 2, 3제곱을 resultpush, current 초기화

이를 바탕으로 작성한 코드는 아래와 같습니다.

function solution(dartResult) {
    let result = [];
    
    let current = '';
    [...dartResult].forEach((v) => {
        if('0' <= v && v <= '9') current += v;
        else if(v === '*') {
            if(result.length >= 2) result[result.length - 2] *= 2;
            result[result.length - 1] *= 2;
        }
        else if(v === '#') {
            result[result.length - 1] *= -1;
        }
        else {
            if(v === 'S') result.push(Number(current));
            if(v === 'D') result.push(Math.pow(Number(current), 2));
            if(v === 'T') result.push(Math.pow(Number(current), 3));
            current = '';
        }
    });
    
    return result.reduce((s, v) => s + v);
}


코드의 가독성을 높이고, 보다 명확하게 하기 위해서 정규표현식과 객체를 활용하여 다른 방식으로 풀이가 가능합니다. 우선 매번 비교하여 보너스와 옵션에 따라 코드를 작성하지 않기 위하여 객체 형태로 이를 작성합니다.

이후, 정규표현식을 이용하여 점수 | 보너스 | 옵션 형태를 기준으로 match를 활용하여 배열 형태로 저장합니다.

/\d\d?.?\D/g

  • \d : 숫자를 나타냄. 최대 10까지 올 수 있으므로, 2번 사용.
  • ? : 존재하지 않을수도 있음. 한 자리인 경우도 있으므로 \d 뒤에 사용.
  • .? : 임의의 한 문자를 의미하며, . 는 해당 문자열이 뒤에 오는 문자로 끝남을 의미.
  • \D : 숫자가 아닌 문자를 의미.
  • g : global, 대상 문자열 내의 모든 패턴을 검색함을 의미.

이후, 배열을 순회하며 각 경우를 정규표현식을 활용하여 숫자와 보너스, 옵션으로 나누어 계산합니다.

/(^\d{1,})(S|D|T)(\*|#)?/

  • (\d{1, }) : 숫자를 나타냄. {1,}\d가 1개 이상 위치함을 의미.
  • (S|D|T) : S, D, T 중 하나가 위치함을 의미.
  • (\*|#)? : *, # 둘 중 하나가 위치함을 의미하며, 존재하지 않을 수도 있음.
  • () : 하나의 그룹을 표현함.

이를 이용하면 index 0에는 매치된 문자열이, 그 뒤로는 각 그룹에 매치된 문자열이 저장됩니다. 이를 이용하여 문제를 풀이할 수 있습니다. 위 내용을 활용한 코드는 아래와 같습니다.

function solution(dartResult) {
    const bonus = { 'S': 1, 'D': 2, 'T': 3 };
    const option = { '*': 2, '#': -1, undefined: 1 };

    let games = dartResult.match(/\d\d?.?\D/g);
    games.forEach((v, i) => {
        let current = v.match(/(^\d{1,})(S|D|T)(\*|#)?/);
        let score = Math.pow(current[1], bonus[current[2]]) * option[current[3]];
        if(i >= 1 && current[3] === '*') games[i - 1] *= option['*'];
        games[i] = score;
    })

    return games.reduce((s, v) => s + v);
}

profile
잔잔한 물결에서 파도로, 도약을 위한 도전. 함께하는 성장

0개의 댓글