다트 게임

신연우·2021년 1월 18일
0

알고리즘

목록 보기
11/58
post-thumbnail

프로그래머스 - 다트 게임

문제 설명

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

카카오톡 게임별의 하반기 신규 서비스로 다트 게임을 출시하기로 했다. 다트 게임은 다트판에 다트를 세 차례 던져 그 점수의 합계로 실력을 겨루는 게임으로, 모두가 간단히 즐길 수 있다.

갓 입사한 무지는 코딩 실력을 인정받아 게임의 핵심 부분인 점수 계산 로직을 맡게 되었다. 다트 게임의 점수 계산 로직은 아래와 같다.

  1. 다트 게임은 총 3번의 기회로 구성된다.
  2. 각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.
  3. 점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱으로 계산된다.
  4. 옵션으로 스타상(*), 아차상(#)이 존재하며 스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 아차상(#) 당첨 시 해당 점수는 마이너스된다.
  5. 스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다.
  6. 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다.
  7. 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다.
  8. Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.
  9. 스타상(*), 아차상(#)은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있다.

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

입력 형식

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

  • 점수는 0에서 10 사이의 정수이다.
  • 보너스는 S, D, T 중 하나이다.
  • 옵션은 *이나 # 중 하나이며, 없을 수도 있다.

출력 형식

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

입출력 예제

dartResultanswer
1S2D*3T37
1D2S#10S9
1D2S0T3
1S*2T*3S23
1D#2S*3S5
1T2D3D#-4
1D2S3T*59

풀이

def solution(dartResult):
    score_list = [str(score) for score in range(11)]
    bonus_list = {"S": 1, "D": 2, "T": 3}
    chance = -1
    answer = [0 for _ in range(3)]

    for s in "SDT":
        dartResult = dartResult.replace(s, f" {s} ")
    for s in "*#":
        dartResult = dartResult.replace(s, f"{s} ")

    dartResult = dartResult.split(" ")

    for s in dartResult[:-1]:
        if s in score_list:
            chance += 1
            answer[chance] = int(s)
        elif s in bonus_list:
            answer[chance] = pow(answer[chance], bonus_list[s])
        elif s == "*":
            answer[chance] *= 2
            if chance:
                answer[chance - 1] *= 2
        else:
            answer[chance] *= -1

    return sum(answer)

해결 과정

  1. 문자열을 점수, 보너스, 옵션으로 각각 구분할 수 있도록 변형해야 한다.
    문자열 그 자체만으로 구분하는 것은 굉장히 어려운 일이다. 고로 문자열에서 점수, 보너스, 옵션을 구분할 수 있도록 문자열을 변형해 정보 추출에 용이하게 한다. 내가 선택한 방법은 공백 문자(' ')를 기준으로 구분하는 방법이다.

    보너스의 경우, 앞에 숫자는 반드시 있고, 뒤에 옵션은 있을 수도 있다. 양쪽에 공백을 추가한다. 만약 옵션이 있는 경우라면 옵션과 보너스 사이에 공백이 생기고, 없는 경우라면, 점수와 공백이 생기므로 결국 구분할 수 있는 기준이 된다.

    문제는, 옵션이다. 위의 내용만 진행할 경우 옵션이 있다면 숫자와 같이 붙어있게 된다. (예: *3) 이 경우를 방지하기 위해 옵션의 오른쪽에 공백 문자 하나를 추가했다.

    이후, 공백을 기준으로 split하면, 정보가 각각 담긴 배열이 만들어진다. 이때 주의할 점은 가장 마지막 요소에 '' 문자가 들어간다는 점이다. 문자열의 가장 마지막이 공백 문자이기 때문이다. 그러므로 반복문을 돌릴 때는 마지막 문자는 제외하고 돌려야 한다.

  2. 조건문을 통해 각 경우별로 처리
    정보를 가져올 수 있도록 변형했다면 남은 일은 조건문을 통해 각 경우에 맞는 조치를 취하는 것이다. 조건 검사를 용이하게 하기 위해 점수의 경우 점수의 범위(0 ~ 10)를 배열에 저장해 관리하고, 보너스의 경우 제곱을 하기 위해 딕셔너리의 형태로 관리한다.

다른 사람의 풀이

import re


def solution(dartResult):
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'' : 1, '*' : 2, '#' : -1}
    p = re.compile('(\d+)([SDT])([*#]?)')
    dart = p.findall(dartResult)
    for i in range(len(dart)):
        if dart[i][2] == '*' and i > 0:
            dart[i-1] *= 2
        dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]

    answer = sum(dart)
    return answer

정규식을 이용해 문제를 해결할 수 있다. 확실히 이 문제는 문자열이 정해진 형식대로 이루어져 있기 때문에 정규식을 사용하는 방법도 유추할 수 있다.

  • (\d+) : 0~9 사이의 문자로 시작하는 문자열을 하나의 문자로 인식
  • ([SDT]) : S, D, T 중 하나라면, 그것을 하나의 문자로 인식
  • ([*#]?) : *, # 문자가 있거나 없다면 그것을 하나의 문자로 인식

반복문을 돌 때 스타상의 중첩 예외 처리를 하고, 나머지는 점수^보너스 * 옵션의 식을 계산하여 각 기회별 점수를 구하는 방식이다.

profile
남들과 함께하기 위해서는 혼자 나아갈 수 있는 힘이 있어야 한다.

0개의 댓글