TIL(Today I Learned)

1월 24일 TIL - 프로그래머스 다트 게임🎯

Hyerin P. 2024. 1. 24. 21:33
# https://school.programmers.co.kr/learn/courses/30/lessons/17682
# [1차] 다트 게임
# 문제 설명
# 다트 게임은 총 3번의 기회로 구성된다.
# 각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.
# 점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산된다.
# 옵션으로 스타상(*) , 아차상(#)이 존재하며 스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 아차상(#) 당첨 시 해당 점수는 마이너스된다.
# 스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다. (예제 4번 참고)
# 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (예제 4번 참고)
# 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (예제 5번 참고)
# Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.
# 스타상(*), 아차상(#)은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있다.
# 0~10의 정수와 문자 S, D, T, *, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.
# 입력 형식
# "점수|보너스|[옵션]"으로 이루어진 문자열 3세트.
# 예) 1S2D*3T
# 제한사항
# 점수는 0에서 10 사이의 정수이다.
# 보너스는 S, D, T 중 하나이다.
# 옵선은 *이나 # 중 하나이며, 없을 수도 있다.

🧐  문제 정리

1. 숫자를 기준으로 문자열 3세트를 분리한다.

2. 세개의 점수 리스트를 만든 뒤 총 합을 구한다.

🤯  해결 과정에서의 어려움

 처음에 문자열에서 숫자들만 따로 떼어내거나 주어진 문자열 세트를 어떻게 분리해야할 지 막막했다. 정규표현식을 생각하긴 했지만 아직 제대로 사용할 줄 모르기에 일단 다른 방법을 모색하였다. 그래서 for문으로 문자열을 하나씩 돌려 숫자인지 판단한 후 점수를 계산하기로 하였다. 하지만 이렇게 되면 10이란 숫자는 '1','0'으로 판별하게 되는 부분을 놓쳐 초반에 애를 먹었다.

🤩  배운점

이 코드에선 .isdigit()을 이용하여 숫자를 판별한다. 문자가 무엇인지 판별하는덴 여러가지 코드가 있다.

# 문자열이 숫자인지 판별하기
a = '123'
a.isdigit() # True

b = '1 2,3a' # 숫자 외의 기호, 공백, 문자가 포함되어 있을 시
b.isdigit()  # False 

# 문자열이 문자로만 이루어졌는지 판별하기
c = 'ABC안녕'
c.isalpha() # True

d = 'ab12_ c' # 문자 외의 기호, 공백, 숫자가 포함되어 있을 시 
d.isdigit()   # False

# 문자 또는 숫자로만 이루어졌는지 판별하기
e = 'abc123'
e.isalnum() # True

f = 'ab12_ c' # 문자 또는 숫자 외의 기호, 공백이 포함되어 있을 시 
f.isalnum()   # False

 

다음에는 정규표현식을 좀 더 공부해오겠다!

😎  해결 하기

1. 숫자를 기준으로 문자열 3세트를 분리한다.

# 처음 코드, 이때는 10을 기록하지 못했다.
for n in dartResult:
    # n이 숫자일시 스코어에 기록하기, 기회 체크
    if n.isdigit():
        score.append(int(n))
        heart += 1

# 수정 후 코드
for i, n in enumerate(dartResult):
        if n.isdigit():
        	#10은 숫자의 연속이기에 그 부분으로 10인지 확인하였다.
            if dartResult[i-1].isdigit():
                score[-1] = 10
            else:
                score.append(int(n))

 

2. 보너스, 옵션 확인해서 점수를 계산한다.

# 숫자를 기준으로 각각의 기회를 구분하기
heart = 0
score = []
bonus = {'S': 1, 'D': 2, 'T': 3}

for i, n in enumerate(dartResult):
    # n이 숫자일시 스코어에 기록하기, 기회 체크
    if n.isdigit():
        # 만약 바로 앞의 문자가 숫자면 10이므로 확인 후 기록하기
        if dartResult[i-1].isdigit():
                score[-1] = 10
            else:
                score.append(int(n))
        heart += 1
    # 숫자가 아닐 경우 보너스인지, 옵션인지 구분하기
    else:
        # 보너스이면 해당하는 값을 찾아 계산하기
        if n in bonus:
            score[-1] **= bonus[n]
        # 옵션일때 스타상, 아차상에 따라 계산해주기
        else:
            # 스타상일 때 점수 계산
            if n == '*':
                # 해당점수 2배
                score[heart-1] *= 2
                # 이전 인덱스 존재한다면 2배
                if len(score) > 1:
                    score[heart-2] *= 2
            else:
                score[heart-1] *= -1
                
# 총합 구하기
sum(score)

 

위 코드의 변수 중 heart는 지금 현재 기회를 나타내기 위해 사용했는데 코드를 고쳐나가다 보니 없어도 될 변수였다. score의 인덱스가 곧 기회이기 때문에 따로 변수를 지정해줄 필요는 없었다.

또한 bonus를 딕셔너리로 정의할 때 option도 함께 딕셔너리로 정의하여 코드를 짜고 싶었는데 구상이 안나와서 일단 없이 구조를 짰다. 다 작성하고 보니 옵션 계산 부분을 수정할 수 있을 것 같아 최종 함수에선 스타상(*)일 때 앞에 인덱스가 있을때만 조건문으로 작성해주고 option 딕셔너리를 이용하여 점수를 계산하게 작성하였다. 그러니 좀 더 깔끔한 코드가 된 것 같다.

📝   최종 함수

def solution(dartResult):
    score = []
    bonus = {'S': 1, 'D': 2, 'T': 3}
    option = {'*': 2, '#': -1}
    for i, n in enumerate(dartResult):
        if n.isdigit():
            if dartResult[i-1].isdigit():
                score[-1] = 10
            else:
                score.append(int(n))
        else:
            if n in bonus:
                score[-1] **= bonus[n]

            else:
                if n == '*' and len(score) > 1:
                    score[-2] *= 2
                score[-1] *= option[n]
    return sum(score)