[프로그래머스] [1차] 다트 게임
예전에 풀다가 포기한 문제였는데 이 문제 뺀 나머지 Level 1문제 다 풀고 다시 풀어보니까 풀리더라고요😆
이렇게 보니까 문제가 엄청 길긴 하네요;;;
이 문제에 대한 팁에 대해 설명하자면
입력 형식에서 "점수는 0에서 10 사이의 정수이다."라는 말이 있습니다.
이 말은 0 ~ 10까지 존재한다는 말입니다.
저는 이번 문제를 풀면서 저 10이라는 두 자리 숫자 때문에 정확성 테스트에서 애를 먹었습니다.
이때 이 아래 있는 테스트 케이스에 추가하시고 푸시면 쉽게 풀 수 있습니다.
dartResult : 10D4S10D
answer : 204
해결방안
function solution(dartResult) {
// dartResult문자열을 문자하나하나로 나누어서 배열로 만들기
let spArr = let spArr = [...dartResult];
// '1', '0'이면 '10'으로 바꾸어주기
let arr = [];
for(let i=0; i<spArr.length; i++){
if(spArr[i] === '1' && spArr[i+1] === '0'){
arr.push('10');
i+=2;
}
arr.push(spArr[i]);
}
// 문자열 3세트 각각 점수구하기
let arr1 = [];
for(let i=0; i<arr.length; i++){
if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
let num = 0;
if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);
if(arr[i+1] === '#'){
if(arr[i+4] === '*') num *= -2;
else num *= -1;
}
if(arr[i+1] === '*'){
if(arr[i+4] === '*') num *= 4;
else num *= 2;
}else if(arr[i+3] === '*') num *= 2;
arr1.push(num);
num = 0;
}
}
// 문자열 3세트 점수 합산하기
return arr1.reduce((acc, cur) => acc+cur);
}
해결방안 순서
- dartResult문자열을 문자 하나하나로 나누어서 배열로 만들기
- '1', '0'이면 '10'으로 바꾸어주기
- 문자열 3세트 각각 점수 구하기
- 문자열 3세트 점수 합산하기
1단계. dartResult문자열을 문자 하나하나로 나누어서 배열로 만들기
// 1단계. dartResult문자열을 문자하나하나로 나누어서 배열로 만들기
let spArr = [...dartResult];
점수를 구하기 위해서는 dartResult문자열을 배열로 쪼개는 게 구하게 쉽다는 생각이 들었습니다.
해봤자 뒤에서 for문을 쓸 예정이니까 미리 배열로 만들면 편하겠죠?
문자열을 배열로 만드는 건 원래라면 split()메서드를 사용했지만
이번에는 조금 다른 방법으로 바꾸어 볼까 합니다.
spread operator문법을 사용해서 바꾸었는데요.
spread operator문법은 쉽게 설명하면 괄호를 제거해주는 연산자입니다.
spread는 배열뿐만 아니라 문자, 객체에도 쓸 수 있습니다.
문자열에다가 spread를 붙이면 문자열.split('')와 비슷한 효과를 만들 수 있습니다.
let 문자 = 'hello';
console.log(...문자); // 'h', 'e', 'l', 'l', 'o'
console.log([...문자]; // ['h', 'e', 'l', 'l', 'o']
테스트 1 (dartResult = 1S2D*3T)
let spArr = [...dartResult]; -> ['1', 'S', '2', 'D', '*', '3', 'T']
테스트 2 (dartResult = 1D2S#10S)
let spArr = [...dartResult]; -> ['1', 'D', '2', 'S', '#', '1', '0', 'S']
.
.
.
2단계. '1', '0'이면 '10'으로 바꾸어주기
// 2단계. '1', '0'이면 '10'으로 바꾸어주기
let arr = [];
for(let i=0; i<spArr.length; i++){
if(spArr[i] === '1' && spArr[i+1] === '0'){
arr.push('10');
i+=2;
}
arr.push(spArr[i]);
}
이 문제에서는 숫자가 1~9까지 존재하는 게 아니라 10까지 존재합니다.
그래서 '1', '0'을 '10'으로 만들어주는 작업을 해야 됩니다.
spArr에 배열 원소가 '1'인데 다음번째 원소가 '0'이면 1,0이 아닌 10이겠죠?
for문을 사용해서 spArr[i]가 '1'이고 spArr[i+1]가 '0' 이면 '10'을 만들어 주었습니다.
- for(let i=0; i<spArr.length; i++) : 0부터 spArr.length-1 만큼 반복
- if(spArr[i] === '1' && spArr[i+1] === '0') : spArr[i]가 '1'이고 spArr[i+1]가 '0' 이면
- arr.push('10'); : '1', '0'이면 '10'으로 넣어주기
- i+=2; : '1', '0'이 맞으면 2개를 합쳐서 '10'으로 쓴 거기 때문에 i번째 다음 꺼는 할 필요가 없으므로 i+2를 해주었습니다.
- arr.push(spArr[i]); : spArr[i]가 1이 아니면 나머지는 그대로 넣어주었습니다. (else 넣어서 하시면 안돼요)
테스트 2 (dartResult = '1D2S#10S';)
1단계 : let spArr = [...dartResult]; -> ['1', 'D', '2', 'S', '#', '1', '0', 'S']
2단계 : arr = ['1', 'D', '2', 'S', '#', '10', 'S']
테스트 8 (dartResult = '10D4S10D') // 따로 만든 거
1단계 : let spArr = [...dartResult]; -> [ '1', '0', 'D', '4', 'S', '1', '0', 'D']
2단계 : arr = [ '10', 'D', '4', 'S', '10', 'D' ]
3단계. 문자열 3세트 각각 점수 구하기
// 3단계. 문자열 3세트 각각 점수구하기
let arr1 = [];
for(let i=0; i<arr.length; i++){
if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
let num = 0;
if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);
if(arr[i+1] === '#'){
if(arr[i+4] === '*') num *= -2;
else num *= -1;
}
if(arr[i+1] === '*'){
if(arr[i+4] === '*') num *= 4;
else num *= 2;
}else if(arr[i+3] === '*') num *= 2;
arr1.push(num);
num = 0;
}
}
Single(S), Double(D), Triple(T) 영역이 존재
- 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산
옵션으로 스타상(*) , 아차상(#)이 존재(점수가 ABC 라면 해당점수는 B로 예시)
- 스타상(*) 당첨 시 해당 점수(B)와 바로 전에 얻은 점수(A)를 각 2배로 만든다. -> A B* C이면 A*2 B*2 C
- 아차상(#) 당첨 시 해당 점수(B)는 마이너스된다. -> A B# C이면 A B(-1) C
- 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (테스트 4 참고) -> A* B*C이면 A*4 B* C
- 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (테스트 5번 참고) -> A# B* C이면 A(-1)*2 B*2 C
이 위에 있는걸 다 적용되게 만들어야 됩니다.
2단계에서 만든 arr배열을 보시면
- 테스트 1 arr = ['1', 'S', '2', 'D', '*', '3', 'T']
- 테스트 2 arr = ['1', 'D', '2', 'S', '#', '10', 'S']
- 테스트 3 arr = [ '1', 'D', '2', 'S', '0', 'T' ]
'S', 'D', 'T'앞에는 무조건 숫자이고, 뒤는 '*', '#'이거나 없을 수 있습니다.
그래서 'S', 'D', 'T'을 기준으로 점수를 계산했습니다.
'S', 'D', 'T'의 앞에 자리가 무조건 숫자인으로 숫자와 'S', 'D', 'T'를 사용해서 1제곱, 2제곱, 3제곱을 만들어주면 되는데
'*', '#'은 상황에 따라 달라집니다.
그래서 어떻게 생각했다면
'*'은 현재 자리+1부터 '*'이 몇 개인은 지를 확인하면 되고
-> 내 파트, 내 다음 파트에 '*'이 있네?,
->내 파트 말고 다음 파트에 '*'이 있네?
-> 내 파트, 내 다음 파트 둘 다 '*'이 있네
'#'은 현재 자리+1에 있는지 확인하고 그다음에 *이 있는지 확인하면 됩니다.
-> 내 파트에 '#'이 있네?
-> 내파트에 '#'이 있는데 다음 파트에 '*'이 있네
'*' 스타상일 때 ('S', 'D', 'T'위치가 i)
- i+1에 '*' 있다면 -> 내 파트에 '*'이 있네
- i+1, i+4 두 곳에 '*'이 있다면 -> 내 파트와 내 다음 파트에 '*'이 있네
- i+1에는 '*' 없고 i+3에 '*'이 있다면 -> 내 다음 파트에만 '*'이 있네
이렇게 3가지의 경우로 나누어집니다.
코드로 작성할 때 예시
- i+1에 '*' 있다면
- i+4에 '*' 있다면
- true : num*4
- false : num*2
- i+4에 '*' 있다면
- i+1에는 '*' 없고 i+3에 '*'이 있다면
- true : num*2해주기
'#' 아차상일 때 ('S', 'D', 'T'위치가 i)
- i+1에 '#'에만 있다면
- i+1에 '#'이 있는데 i+4에 '*'이 있다면
이렇게 2가지 경우로 나누어집니다.
코드로 작성할 때 예시
- i+1에 '#' 있다면
- i+4에 '*' 있다면
- true : num*(-2)
- false : num*(-1)
- i+4에 '*' 있다면
이 '*'스타상 '#'아차상일 때 코드로 작성할 때 예시를 코드로 작성하면 됩니다.
코드가 길어서 복잡해 보이지만 순서를 간단하게 설명하자면
3단계 코드 순서
- 0부터 arr배열의 길이만큼 for문으로 반복해준다.
- arr배열(2단계에서 만든 배열)에서 'S', 'D', 'T'를 찾기
- 'S', 'D', 'T'의 앞에 자리(i-1), arr[i-1]의 1제곱, 2제곱, 3제곱을 만들어서 num에다가 담기
- 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '#'이 있다면에 대한 코드넣기(위에 '#' 아차상일 때 내용)
- 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '*'이 있다면에 대한 코드넣기(위에 '*' 스타상일 때 내용)
- arr1에 2~5에서 해준 num의 값을 넣고 num초기화해주기
3단계_1. 0부터 arr배열의 길이만큼 for문으로 반복해준다.
for(let i=0; i<arr.length; i++)
3단계_2. arr배열(2단계에서 만든 배열)에서 'S', 'D', 'T'를 찾기
if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
let num = 0;
}
'S', 'D', 'T'에서만 num을 계산해 줄 거기 때문에 arr[i]가 'S', 'D', 'T'에서만 작동하도록 했습니다.
각 파트의 합산을 구하기 위해 num=0;을 넣어주었고
num이 arr[i]가 'S', 'D', 'T'에서만 계산할 수 있도록 해주었습니다.
3단계_3. 'S', 'D', 'T'의 앞에 자리(i-1), arr[i-1]의 1제곱, 2제곱, 3제곱을 만들어서 num에다가 담기
if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);
문제에서 S이면 1제곱, D이면 2제곱, T이면 3제곱을 하라 했습니다.
?제곱을 하기 위해 Math.pow()함수를 썼습니다.
MDN Web Docs
Math.pow()함수는 base^exponent처럼 base에 exponent를 제곱한 값을 반환합니다.
문법 : Math.pow(base, exponent)
- base : 밑 값.
- exponent : 밑 을 제곱하기 위해 사용하는 지수.
// Math.pow()함수 예제
// 간단한 예
Math.pow(7, 2); // 49
Math.pow(7, 3); // 343
Math.pow(2, 10); // 1024
Math.pow()함수를 사용해서 각각 문자마다 알맞은 제곱을 해주었습니다.
이때 arr[i-1]/1을 해준 이유는 arr배열을 보시면 숫자까지 다 ''로 감싸져 있습니다. 즉 문자라는 말입니다.
문자를 숫자로 바꾸어야 1제곱, 2제곱, 3제곱을 해줄 수 있습니다.
문자를 숫자로 바꾸는 방법으로는 전에도 말했지만
"문자열과 숫자열의 사칙연산"으로 구해주면 쉽게 구할 수 있습니다.
문자열과 숫자열의 사칙연산은 숫자가 나오게 됩니다.
- '5'/1 = 5
3단계_4. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '#'이 있다면에 대한 코드 넣기(위에'#' 아차상일 때 내용)
if(arr[i+1] === '#'){
if(arr[i+4] === '*') num *= -2;
else num *= -1;
}
- i+1에 '#' 있다면
- i+4에 '*' 있다면
- true : num*(-2)
- false : num*(-1)
- i+4에 '*' 있다면
이 내용을 코드로 작성했습니다.
3단계_5. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '*'이 있다면에 대한 코드 넣기(위에'*' 스타상일 때 내용)
if(arr[i+1] === '*'){
if(arr[i+4] === '*') num *= 4;
else num *= 2;
}else if(arr[i+3] === '*') num *= 2;
- i+1에 '*' 있다면
- i+4에 '*' 있다면
- true : num*4
- false : num*2
- i+4에 '*' 있다면
- i+1에는 '*' 없고 i+3에 '*'이 있다면
- true : num*2해주기
이 내용을 코드로 작성했습니다.
3단계_6. arr1에 2~5에서 해준 num의 값을 넣고 num초기화해주기
arr1.push(num);
num = 0;
1~5에서 만든 num값을 arr1에 담아주고, num을 초기화해주었습니다.
num은 다음번 arr[i]에서 'S', 'D', 'T'이 나오면 그 위치에서 num의 값을 구해야 되기 때문에 다시 초기화해주었습니다.
3단계 코드 순서
테스트 1 arr = ['1', 'S', '2', 'D', '*', '3', 'T']
i = 1, 3, 6일 때 'S', 'D', 'T'가 있음
2. i=1일 때 'S'가 있음
3. arr[i-1] = arr[1] ='1' -> 1에 1제곱은 1 -> num = 1
5. arr[i+3] = arr[4] = '*' -> num *= 2 -> num = 1*2 = 2
6. arr1 = [num] -> arr1=[2]; num=0;
2. i=3일 때 'D'가 있음
3. arr[i-1] = arr[2] ='2' -> 2에 2제곱은 4 -> num = 4
5. arr[i+1] = arr[4] = '*' -> num *= 2 -> num = 4*2 = 8
6. arr1 = [num] -> arr1=[2, 8]; num=0;
2. i=6일 때 'T'가 있음
3. arr[i-1] = arr[5] ='3' -> 3에 3제곱은 27 -> num = 27
6. arr1 = [num] -> arr1=[2, 8, 27]; num=0;
arr1 = [2, 8, 27]
4단계. 문자열 3세트 점수 합산하기
// 4단계. 문자열 3세트 점수 합산하기
return arr1.reduce((acc, cur) => acc+cur);
마지막으로 arr1에 원소 값을 합산하면 되는데요. 배열 안 원소의 합을 구할 때는 reduce()메서드를 사용하면 쉽게 구할 수 있습니다.
MDN Web Docs
reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
reduce() 함수는 네 개의 인자를 가집니다.
1. 누산기 (acc)
2. 현재 값 (cur)
3. 현재 인덱스 (idx)
4. 원본 배열 (src)
구문 : 배열.reduce((callback)=>처리할 현재 요소, 기본값)
- callback : 배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수(acc, cur, idx, src)를 받습니다.
- 기본값 : 넣어도 되고 안 넣어도 됨
// reduce() 메서드 예제
const array1 = [1, 2, 3, 4];
const reducer = (previousValue, currentValue) => previousValue + currentValue; // 1 + 2 + 3 + 4
console.log(array1.reduce(reducer)); // 10
3단계에서 구한 테스트 1 arr1 = [ 1, 8, 27 ]
return arr1.reduce((acc, cur) => acc+cur); -> 1+ 8 + 27 = 37
여기까지 프로그래머스 [1차] 다트 게임 해결방안에 대해 설명해보았습니다.