[level 1] [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, *, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.
문제 풀이 접근법1
1) dartResult에서 숫자만 뽑아 리스트로 정의한다.
2) *과 #의 위치를 position딕셔너리로 정의한다.(key: * or # , value: * or #의 위치)
3) dartResult의 길이 만큼 반복한다. 만약 dartResult[i]가 숫자라면 제곱한 만큼을 answer에 더한다.
4) dartResult[i]가 * 이나 #이라면 answer에서 *이나 #이 있는 곳의 점수를 뺀 후, 다시 계산한 값을 더한다.
dartResult = "1S2D*3T"
answer = 0
dart_number = []
#dart_string = { 'S': 1, 'D': 2, 'T':3, '*':2, '#':-1}
position = {}
for i in dartResult:
if i.isdigit():
dart_number.append(int(i))
if i == '*' or i == '#':
position[i] = (dartResult.index(i) // 2)
for i in range(len(dartResult)):
if dartResult[i].isdigit():
#print()
answer += int(dartResult[i])**(int(dartResult[i]))
#print(answer)
if dartResult[i] == '*':
pos = position['*'] -1
if pos != 0:
answer -= ((dart_number[pos-1]**dart_number[pos-1]) +(dart_number[pos]**dart_number[pos]))
dart_number[pos] = (dart_number[pos]**dart_number[pos]) * 2
dart_number[pos-1] = (dart_number[pos-1]**dart_number[pos-1]) *2
answer += (dart_number[pos] + dart_number[pos-1] )
#print(answer)
else:
answer -= dart_number[0]**dart_number[0]
dart_number[0] = (dart_number[0]**dart_number[0]) *2
answer += dart_number[0]
elif dartResult[i] == '#':
pos = position['#'] -1
answer -= dart_number[pos]**dart_number[pos]
dart_number[pos] = (dart_number[pos]**dart_number[pos]) *-1
answer += dart_number[pos]
print(answer)
코드의 문제점
① 점수가 10일 경우 1, 0으로 판별 되어짐
② *와 #을 key로 해당 값들의 위치를 (몇번째 판에 나왔는지를) value로 다루고 있었는데 *와 #는 중복이 가능함 → 딕셔너리 사용 불가
③ 왜때문인지 모르겠지만...내가 차례대로 점수에 제곱하는 수가 커지는 거라고 생각해서...이것도 해결해야 했다. ㅋ
문제 해결 접근법2
1) dartResult에 10이 있다면 A로 교체한다.
2) dartResult에서 숫자만 뽑아 리스트로 정의한다. 만약 A가 있다면, 숫자 리스트에 10을 추가한다.
3) dartResult에서 알파벳만 뽑아 따로 리스트로 정의한다.
4) answer에 각 라운드의 점수에 해당하는 제곱수를 곱하여 더한다.
5) *이나 #이 있다면 원래 점수를 빼고 다시 계산한 점수를 더한다.
dartResult = "1D#2S*3S"
answer = 0
dart_number = []
dart_alpha = []
dart_string = { 'S': 1, 'D': 2, 'T':3, '*':2, '#':-1}
position = {}
dartResult= dartResult.replace("10", "A")
for i in dartResult:
if i == 'A':
dart_number.append(10)
elif i.isdigit():
dart_number.append(int(i))
elif i.isalpha():
dart_alpha.append(i)
for i in range(3):
answer +=(dart_number[i] ** dart_string[dart_alpha[i]])
print('*#x:', answer)
for i in range(len(dartResult)):
if dartResult[i] == '*':
pos = (i // 2 )- 1
if pos != 0:
answer -= ((dart_number[pos-1]**dart_string[dart_alpha[pos-1]]) +(dart_number[pos]**dart_string[dart_alpha[pos]]))
dart_number[pos] = (dart_number[pos]**dart_string[dart_alpha[pos]]) * 2
dart_number[pos-1] = (dart_number[pos-1]**dart_string[dart_alpha[pos-1]]) *2
answer += (dart_number[pos] + dart_number[pos-1] )
else:
answer -= dart_number[0]**dart_alpha[0]
dart_number[0] = (dart_number[0]**dart_alpha[0]) *2
answer += dart_number[0]
elif dartResult[i] == '#':
pos = (i // 2) - 1
answer -= dart_number[pos]**dart_string[dart_alpha[pos]]
dart_number[pos] = (dart_number[pos]**dart_string[dart_alpha[pos]]) *-1
answer += dart_number[pos]
print(answer)
샘플 테스트 케이스 5번에서 오류가 났다.
-2가 빠져야 하는데(첫번째 점수가 1 * -1 * 2가 되어서) 여기서는 -1빠지고 오히려 +1이 더 되어서 5가 아닌 7이 나왔다.
문제 해결 접근법3
1) dartResult에 10이 있다면 A로 교체한다.
2) dartResult에서 숫자만 뽑아 리스트로 정의한다. 만약 A가 있다면, 숫자 리스트에 10을 추가한다.
3) dartResult에서 알파벳만 뽑아 따로 리스트로 정의한다.
4) dart_number리스트의 원소들을 각 영역 만큼의 제곱수로 변환한다.
5) dartResult의 길이 만큼 반복문을 돌리며 #이나 *이 나오면 조건에 맞추어 dart_number의 원소에 2를 곱해주거나 -1을 곱해준다.
6) sum(dart_number)를 반환한다.
def solution(dartResult):
answer = 0
dart_number = []
dart_alpha = []
dart_string = { 'S': 1, 'D': 2, 'T':3, '*':2, '#':-1}
position = {}
dartResult= dartResult.replace("10", "A")
for i in dartResult:
if i == 'A':
dart_number.append(10)
elif i.isdigit():
dart_number.append(int(i))
elif i.isalpha():
dart_alpha.append(i)
for i in range(3):
dart_number[i] = (dart_number[i] ** dart_string[dart_alpha[i]])
for i in range(len(dartResult)):
if dartResult[i] == '*':
pos = ( i // 2 ) -1
if pos != 0:
dart_number[pos] *= 2
dart_number[pos - 1] *= 2
else:
dart_number[0] *= 2
if dartResult[i] == '#':
pos = (i//2)-1
dart_number[pos] *= -1
answer = sum(dart_number)
return answer
테스트 케이스 18, 19, 20, 21에서 런타임 에러가 발생했다.
열심히 해당 케이스를 찾아보니까 "10S*10S*10S*" 일 때, pos가 제대로된 위치를 가리키고 있지 않는 것을 확인할 수 있었다.
문제 해결 접근법4(성공!!)
생각해보니까 나누기 2가 아니라 오히려 3을 나누어서 위치를 찾아주는 것이(왜냐면 *이나 #은 늘 3의 배수로 위치하게 되니까) 더 정확하고 오류도 나지 않을 것 같아서 바꿔주었다.
def solution(dartResult):
answer = 0
dart_number = []
dart_alpha = []
dart_string = { 'S': 1, 'D': 2, 'T':3}
dartResult= dartResult.replace("10", "A")
for i in dartResult:
if i == 'A':
dart_number.append(10)
elif i.isdigit():
dart_number.append(int(i))
elif i.isalpha():
dart_alpha.append(i)
for i in range(len(dart_number)):
dart_number[i] = (dart_number[i] ** dart_string[dart_alpha[i]])
for i in range(len(dartResult)):
if dartResult[i] == '*':
pos = ( i // 3)
dart_number[pos] *= 2
if pos != 0:
dart_number[pos-1] *= 2
elif dartResult[i] == '#':
pos = (i//3)
dart_number[pos] *= -1
answer = sum(dart_number)
return answer
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
파이썬 정규 표현식을 사용해서 문제를 풀 수도 있다니..!
너무 멋있다. 사실 정규표현식 자체를 처음 알았음(...)
나중에 꼭 복습해보기..
https://namhandong.tistory.com/65
파이썬 | 정규표현식(re) re.compile 사용을 위한 표현법
1. 정규 표현식 (RE : Regular Expression) 정규 표현식은 특정한 규칙을 가진 문자열 패턴을 사용하는 방식이다. 데이터 전처리에 있어서 텍스트에서 특정 문자열을 검색, 치환, 제거할 때 사용한다. 예
namhandong.tistory.com
참고:
[Python] 파이썬 문자열에서 숫자만 추출하기
안녕하세요. 오늘은 파이썬으로 문자열에서 숫자만 추출하는 방법 2가지에 대해 알아보겠습니다. 파이썬 문자열에서 숫자만 추출하기 ▣ 예시 문자열 "2022년 모두 수고하셨습니다. 2023년도 화이
codingspooning.tistory.com