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

김멉덥·2023년 7월 7일
0

알고리즘 공부

목록 보기
20/171
post-thumbnail

문제

프로그래머스 2018 KAKAO BLIND RECRUITMENT

문제 설명

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

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

  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설명
11S2D*3T3711  2 + 22  2 + 33
21D2S#10S912 + 21 * (-1) + 101
31D2S0T312 + 21 + 03
41S2T3S2311  2 2 + 23 * 2 + 31
51D#2S*3S512  (-1) 2 + 21 * 2 + 31
61T2D3D#-413 + 22 + 32 * (-1)
71D2S3T*5912 + 21  2 + 33  2

코드 구현

import math

def solution(dartResult):
    answer = 0

    dart_split = []         # input으로 들어온 문자열 dartResult를 쪼개서 담을 리스트
    score = []              # 아래 for문에서 각 점수를 쪼깨어 부분 리스트로 넣을 리스트

    # 문자열 쪼개서 담기 작업
    for d in dartResult:
        if(d.isdigit()):            # 만약 숫자면
            score.append(int(d))    # int로 변형해서 부분 리스트 score에 넣어두기
        else:                       # 숫자가 아니면 (ex. 'S', 'D', 'T', '*', '#')
            score.append(d)         # 그냥 string 형식 그대로 부분 리스트 score에 넣어두기

        # 부분 리스트인 score 의 길이가 3 이상이라면 -> 점수가 10이라는 소리
        # (10은 2자리라서 score에 이렇게 1과 0으로 쪼개어져 들어가있게됨 ex. [1, 0, 'S'])
        if (len(score) >= 3):
            update = str(score[0]) + str(score[1])      # 맨앞에 두 정수를 붙여서 새로운 정수로 만들어주기 (ex. '1' + '0' -> '10)
            score[0] = int(update)      # 0번째 자리에 새로 만든 2자리 정수를 넣고 (ex. [10, 0 'S'])
            score.pop(1)                # 1번 인덱스에 있는건 빼버리기 (ex. [10, 'S'])

        # 만약 옵션인 *이나 #이 있다면 -> 따로 분리해서 넣어주기
        if (d == '*' or d == '#'):
            dart_split.append(score)    # 부분 리스트 score를 dart_split 리스트에 넣기
            score = []                  # 초기화

        # 보너스를 만나면 -> 끊어주기
        if(d == 'S' or d == 'D' or d == 'T'):       # 부분 리스트 score를 dart_split 리스트에 넣기
            dart_split.append(score)                # 초기화
            score = []

    print(dart_split)
    # dart_split 출력 예시 (ex. [[1, 'S'], [2, 'D'], ['*'], [3, 'T']])

    calculate_score = []        # 각 점수를 계산할 리스트

    for element_index, element in enumerate(dart_split):        # 위의 예시를 토대로 하면 element : [1, 'S'], element_index : 0

        # S, D, T를 기반으로 제곱 계산하기
        if(element[-1] == 'S'):
            calculate_score.append(math.pow(element[0], 1))
        if (element[-1] == 'D'):
            calculate_score.append(math.pow(element[0], 2))
        if (element[-1] == 'T'):
            calculate_score.append(math.pow(element[0], 3))

        # 만약 옵션 *이 있으면
        if(element[-1] == '*'):
            index = dart_split.index(element, element_index)    # 옵션 *의 인덱스 파악

            # 옵션 *이 첫번째 점수에서 나온게 아닐 때
            if(index >= 2):
                current_index = len(calculate_score) - 1        # 가장 최근 calculate_score에 계산되어 들어온 값의 인덱스
                calculate_score[current_index] *= 2             # *을 가진 해당 점수 2배하기
                calculate_score[current_index - 1] *= 2         # 해당 점수 바로 전에 얻은 점수 2배하기

            # 옵션 *이 첫번째 점수에서 나온거일 때
            else:
                calculate_score[index - 1] *= 2     # 현재 첫번째 점수만 2배

        # 만약 옵션 #이 있으면
        if (element[-1] == '#'):
            current_index = len(calculate_score) - 1        # 가장 최근 calculate_score에 계산되어 들어온 값의 인덱스
            calculate_score[current_index] *= (-1)          # #을 가진 해당 점수 마이너스화

    print(calculate_score)
    # calculate_score 출력 예시 (ex. [2.0, 8.0, 27.0])

    # 총 합 계산하기
    for score in calculate_score:
        answer += score

    return answer

풀이

  • 처음에는 아래와 같이 일부 테스트케이스를 통과하지 못하였다.

→ 뭐가 틀렸는지 확인하고 싶어서 여러 테스트케이스를 혼자 돌려보다가 "1T*2T#3T*" 이 케이스에서 오류가 나는걸 발견했다!

  • 틀렸던 이유
    # 만약 옵션 #이 있으면
    if (element[-1] == '#'):
        current_index = len(calculate_score) - 1        # 가장 최근 calculate_score에 계산되어 들어온 값의 인덱스
        calculate_score[current_index] *= (-1)          # #을 가진 해당 점수 마이너스화

이 코드 부분에서 원래는 current_index를 사용하지 않고, index = dart_split.index(element, element_index) 이런식으로 dart_split에 들어있는 인덱스로 추정하여 #을 가진 점수 마이너스화를 진행하였다.

    # 만약 옵션 #이 있으면
    if (element[-1] == '#'):
        **index** = dart_split.index(element, element_index)
        calculate_score[**index - 1**] *= (-1)

이렇게하면 만약 #이 인덱스 3에 존재할 때, index = 3 이 되어 calculate_score[3 - 1] ⇒ calculate_score[2] 의 값을 마이너스화하게 되는데,

#이 인덱스 3인 경우는 위 테스트케이스로 예시를 들자면, 올바르게 마이너스화 되어야 하는건 인덱스 2에 있는 2T 점수이다.

그러나 calculate_score[2] 의 값을 마이너스화 하려고 하면, 인덱스 오류가 난다.

왜냐하면 현재 calculate_score에는 [1T 계산한값, 2T 계산한값] 까지밖에 안들어있기 때문이다.

즉, 인덱스 1을 마이너스화 해야하기 때문에 #의 인덱스를 얻을 수 있는 dart_split의 인덱스를 기준으로 생각하지 말고, 계산한 값들이 들어있는 calculate_score의 인덱스를 기준으로 바꾸어줘야 한다.

  • 이걸 고치고나니 모두 통과!

What I learned

▶️ isdigit() : 문자열에 숫자가 들어있는지 판별하기

참고 : https://cotak.tistory.com/137

초반에 문자열을 자르는 부분에서 하나하나 비교하는 식으로 짰다가 너무 별로라서 위 메서드를 찾아 수정하였다.

if(d == '0' or d == '1' or ... d == '9'):

-> if(d.isdigit()):

▶️ index() : 리스트 요소를 가지고 인덱스를 찾을 때 특정 요소가 맨 처음으로 위치하고 있는 인덱스만 찾아줌

참고 : https://ooyoung.tistory.com/78

예를 들어, “1S*2S*3S*" 와 같이 *이 여러개가 껴있어서 중간 * 들의 인덱스를 찾아내지 못하고 계속 맨 처음으로 있는 * 의 인덱스만 찾아낼 때, 시작점을 지정해주어 해당 위치부터 처음 뜨는 *을 찾아내게 할 수 있다.

# 문자열중 2번째 위치부터 처음 'x'가 위치한 자리
>>> 'oxoxoxoxox'.index('x', 2)
3

이걸 사용한게 아래의 이 코드 부분이다.

index = dart_split.index(element, element_index)    # 옵션 *의 인덱스 파악

▶️ 2번을 위해서 현재 위치를 같이 전달해줘야하니까 enumerate()를 써주었다. (쓸때마다 헷갈리는 놈)

참고 : https://www.daleseo.com/python-enumerate/

>>> for i, letter in enumerate(['A', 'B', 'C']):
...     print(i, letter)
...
0 A
1 B
2 C

아무튼 이런 형식

for 요소의_인덱스, 요소 in enumerate(리스트):

▶️ 좋은 아이디어

replace()를 사용한 점이 인상깊었다.

def solution(dartResult):
    point = []
    answer = []
    dartResult = dartResult.replace('10','k')
    point = ['10' if i == 'k' else i for i in dartResult]
    print(point)

    i = -1
    sdt = ['S', 'D', 'T']
    for j in point:
        if j in sdt :
            answer[i] = answer[i] ** (sdt.index(j)+1)
        elif j == '*':
            answer[i] = answer[i] * 2
            if i != 0 :
                answer[i - 1] = answer[i - 1] * 2
        elif j == '#':
            answer[i] = answer[i] * (-1)
        else:
            answer.append(int(j))
            i += 1
    return sum(answer)

애초에 이렇게 하나하나 다 쪼개서 하는게 나았을지도?!


profile
데굴데굴 뚝딱뚝딱 개발기록

0개의 댓글