Home PS) 다트 게임
Post
Cancel

PS) 다트 게임

출처
: Programmers

문제

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

(생략)

나의 풀이

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import re

""" 
3개로 나눠진 문자열 각각에 대해
리스트로 반환
e.g. - [1, 'S', '*']
"""
def check_parts(splits):
    # 스코어는 반드시 존재하므로
    # if문 필요없음
    for_score = re.compile('\d{1,2}')
    
    # 보너스도 반드시 존재
    for_bonus = re.compile('[SDT]')

    # 옵션은 존재할수도, 안 할수도
    for_option = re.compile('[*#]')
    
    option_val = for_option.search(splits)
    return_list = [for_score.search(splits).group(),
                   for_bonus.search(splits).group(),
    (lambda option_val: option_val.group() if bool(option_val) is True else '')(option_val)]
    return return_list
    """ 
    - score
        same value

    - bonus
        S => power 1
        D => power 2
        T => power 3

    - option
        *(스타상) 
        => 첫 번째에 있으면
            첫 번째 점수에만 적용
           두 번째는
            첫 번째 있는 것과 중복적용 가능
           세 번째는
            두 번째와 중복적용
            
        
        #(아차상)
        => 아차상 나온 점수는 해당 점수만 (-1) 곱함
     """

""" 
3개의 리스트가 담겨있는
1개의 리스트를 인자로 받음
그걸 각각 계산한 후
더하기

val_sharp_star_co
    : '*''#'가 같이 있는지 
    체크 여부 받음
 """
def calcul_all(three_lists):

    # 합계를 위한 리스트
    for_sum = []
    idx = 0
    for i in three_lists:
        """
        현재 형태
        셋 다 str 타입임
        score는 숫자로 변환해야 편함 
        """
        score = int(i[0])
        bonus = i[1]
        option = i[2]

        
        # pow() 함수 인자 대응
        val_pow = [1, 2, 3]
        bonus_dict = {_:__ for _, __ in zip(['S', 'D', 'T'], [0, 1, 2])}
        
        for_sum.append(pow(score, val_pow[bonus_dict[bonus]]))

        if (option == '#'):
            for_sum[idx] = for_sum[idx] * (-1)
        elif (option == '*'):
            if (idx != 0):
                for_sum[idx - 1] = for_sum[idx - 1] * 2 
            for_sum[idx] = for_sum[idx] * 2

        idx += 1
        
    result = sum(for_sum)
    return result

def solution(dartResult):
    p = re.compile('\d{1,2}[SDT][*#]?')
    str_for_re = dartResult
    splits = p.finditer(str_for_re)

    all_lists = []
    for i in splits:
        temp = i.group()
        all_lists.append(check_parts(temp))

    answer = calcul_all(all_lists)
    return answer


정말 길게도 푼 것 같다.
이번 문제를 통해서 정규식에 대하여
아주 조금 학습하는 기회가 됐다.

다른 사람들의 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

이 풀이도 정말 아름답다…
정규식 부분에 대해 하나씩 뜯어보고
아이디어를 배워가야겠다.

정규식 활용

1
p = re.compile('(\d+)([SDT])([*#]?)')

일단, 정규표현식의 기초사항은 다음과 같다.

  • +
     : 최소 1번 이상 반복되는 것 대상
  • \d  : 숫자인 값이 대상
  • []
     : 이 대괄호 안에 있는 문자들 중에
      하나의 문자와 매칭되는 것이 대상
  • ?
     : ? 앞의 문자가 있을 수도 있고, 없을 수도 있음
  • [*], [#]
     : 이건 *이나 #과는 다르다.
      마치 \(백슬래시)같은 걸 별도로 출력하는 맥락.

위의 사항들을 종합하여
정규표현식을 해석하면 다음과 같다.

숫자는 한 번 넘게 반복될 수도 있고,
그 다음에는 S, D, T 중 하나가 오고,
그 다음으로 *나 # 중 하나가 올 수도 있고, 안 올 수도 있다.


if 대신 리스트 쓰기

1
2
3
4
5
6
7
bonus = {'S' : 1, 'D' : 2, 'T' : 3}
option = {'' : 1, '*' : 2, '#' : -1}
(중략)
    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]]

이런 코드를 작성해서 이용하면
if문 사용을 줄일 수 있다.

1
2
val_pow = [1, 2, 3]
bonus_dict = {_:__ for _, __ in zip(['S', 'D', 'T'], [0, 1, 2])}

나도 이런 식의 코드를 쓰긴 썼는데,
위의 고수 코드만큼은 제대로 활용하지 못했다.

1
2
3
4
5
return_list = [for_score.search(splits).group(),
                for_bonus.search(splits).group(),
(lambda option_val: option_val.group() 
    if bool(option_val) is True else '')(option_val)
            ]

이 부분은
옵션(*, #)에 대한 값이 없는 경우에
None이 되므로 이 경우를 처리해야 하는데,
if 붙이자니 뭔가 그래서 람다식을 써보았다.
근데 오히려 더 지저분해진 것 같다.

Contents