Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발 블로그

[Programmers/파이썬]프로그래머스(Lv.1) 다트게임 문제풀이 본문

Algorithm

[Programmers/파이썬]프로그래머스(Lv.1) 다트게임 문제풀이

토산인 2023. 2. 27. 18:15

문제 설명

다트 게임

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

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

  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설명
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

 

 


사고 흐름

어떻게 풀어야할지 고민이 좀 많았다 처음에는 for문으로 문자열을 돌면서 숫자, 영어, 기호일 때 케이스를 나눠서 계산을 하려고 했는데 코드가 너무 지저분할 것 같아서 다른 방법을 생각했다

-> 단계를 나눠 먼저 점수에 보너스를 계산하고, 옵션을 계산한다

-> 보너스를 계산하는 방법도 여러 방법이 있겠지만 단순하게 s,d,t 일때 제곱을 해줬다 그리고 옵션을 계산했다

 

문제 핵심과 알고리즘

문자열에서 숫자와 영어와 기호를 추출하는 문자열 처리 활용 문제

import math
import re

def solution(dartResult):
    answer = 0
    ans=[]

    nums=list(map(int, re.findall("\d+", dartResult)))
    sdt=re.findall("[A-Z]", dartResult)

    for (i, s) in enumerate(sdt) :
        if s=="S" :
            ans.append(nums[i])
        elif s=="D" :
            ans.append(int(math.pow(nums[i], 2)))
        else :
            ans.append(int(math.pow(nums[i], 3)))

    for (i, d) in enumerate(dartResult) :
        index=i//3
        if d=="*" :
            answer[index]=answer[index]*2
            if index!=0 :
                answer[index-1]=answer[index-1]*2
        if d=="#" :
            answer[index]=answer[index]*(-1)

    return sum(ans)

문제 풀면서 헤맸던 부분

쉬운 문젠데 어렵게 생각했던 것 같다. 각 기회를 계산하려고 하지 않고 한번에 모든 기회를 계산하려고 해서 그런 것 같다.

문제를 잘 보니 다항식 문제랑 비슷해서 각각 계산해야겠다고 생각해서 방법을 바꿨다. 

 

다른 사람 풀이 참고해 배운 점

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

이 풀이는 먼저 영어와 매칭되는 숫자들을 딕셔너리로 만들었다

그리고 re.compile을 사용해 점수,보너스,옵션 패턴을 저장했고, dartresult에서 패턴과 일치하는 문자열을 리스트로 리턴했. (와우)

딕셔너리를 사용해서 쉽게 매칭되는 보너스와 옵션을 계산했다. 

 

다른 풀이에서는 처음 내가 생각했던 방식으로 했다. 그때 난 10을 처리하지 못해 방법을 돌렸지만 그 풀이는 10을 k로 replace했고, 새로운 리스트를 만들어 k이면 10을 넣어 새로운 리스트를 돌면서 처리했다. 

항상 

 

 

<개념 정리>

1. re.compile()

정규표현식을 컴파일하는 함수이다. 즉 파이썬에게 전해주는 역할을 하는데, 찾고자 하는 패턴이 빈번한 경우에는 미리 컴파일하고 사용하면 속도와 편의성면에서 유리하다. 

 

https://jh2021.tistory.com/7

 

정규표현식(Regular Expression) with 파이썬 . ? + *기호 re.compile(), re.findall(), re.sub() (1)

정규표현식 정규식(正規式)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어 입니다. 특히 문자열을 처리할 때 정규표현식을 쉽게 활용할 수 있습니다. 정규표현식은 모

jh2021.tistory.com

 

 

(2023/03/15에 다시 풀어본 코드)

import re
def solution(dartResult):
    answer = 0
    dart=re.findall('[0-9]+[SDT][*#]?', dartResult)
    ans=[]
    
    for (i, d) in enumerate(dart) :
        digit=int(re.findall('[0-9]+', d)[0])
        if 'S' in d :
            if '#' in d :
                ans.append(-digit)
            elif '*' in d :
                if i!=0 :
                    ans[i-1]*=2
                ans.append(digit*2)
            else :
                ans.append(digit)
        elif 'D' in d :
            if '#' in d :
                ans.append(-digit*digit)
            elif '*' in d :
                if i!=0 :
                    ans[i-1]*=2
                ans.append(digit*digit*2)
            else :
                ans.append(digit*digit)
        elif 'T' in d :
            if '#' in d :
                ans.append(-digit*digit*digit)
            elif '*' in d :
                if i!=0 :
                    ans[i-1]*=2
                ans.append(digit*digit*digit*2)
            else :
                ans.append(digit*digit*digit)
    print(ans)
    return sum(ans)

실전이라서 빠르게 풀어야 한다면 이런 하드 코딩도 좋을 것 같다. 빠르게 풀 수 잇기 때문.. 하지만 코드를 좀 정리하자면

import re
import math
def solution(dartResult):
    answer = 0
    dart=re.findall('[0-9]+[SDT][*#]?', dartResult)
    ans=[]
    sdt={'S':1, 'D':2, 'T':3}
    
    for (i, d) in enumerate(dart) :
        temp=re.findall('[0-9]+|[SDT]|[*#?]', d)
        a=math.pow(int(temp[0]), sdt[temp[1]])
        if len(temp)==3 :
            if temp[2]=='*' :
                if i!=0 :
                    ans[i-1]*=2
                a*=2
            else :
                a*=(-1)
        ans.append(a)
        
    print(ans)
    return sum(ans)

딕셔너리를 사용해 s,d,t 판단하는 코드를 줄였고, *,#가 있으면 그에 대한 계산을 따로 처리했다. 

첫번째 한 풀이는 가장 직관적이지만,, 너무 없어 보인다. 그리고 중복된 코드들이 많기 때문에 그런 중복 코드를 줄이기 위해 어떻게 해야할까 고민해보자!