[프로그래머스] 다트 게임 | Javascript

Hailey Song·2021년 1월 12일
1

프로그래머스 다트게임 : https://programmers.co.kr/learn/courses/30/lessons/17682

풀이 과정보다는 자잘한 버그(배열 인덱스가 1부터 시작하는 코드 짠 거 실화냐..)에 더 많은 시간이 걸렸다..
함수를 분리하려고 했는데 가독성 면에서는 잘 모르겠다. 어떤 방식이 효율적인지는 좀 더 찾아봐야할 것 같다.
라운드가 단순히 3 라운드밖에 없어서 하드코딩을 한 점이 없지않아 있는데, 이게 확장될 경우의 수도 생각해야할 것 같다.

1. 수도코드

  1. solution 함수
    1-1. 각 라운드의 최종 점수를 담을 빈 배열을 선언한다
    1-2. 주어진 문자열을 순환하면서 점수와 보너스와 옵션을 구분한다
    1-2-1. 문자가 점수라면 temp에 임시보관(0부터 10점까지이므로 두 자리 수임을 염두)
    1-2-2. 문자가 보너스라면 getBasicScore을 호출해서 점수를 계산한 후 배열에 push
    1-2-3. 문자가 옵션이라면 applyOption을 호출해서 점수를 계산 (배열 자체를 수정)
    1-3. 모든 배열의 합을 더한 후 최종 리턴한다
  2. getBasicScore 함수
    2-1. 인자로 score와 bonus를 받는다
    2-2. score를 숫자로 변환한다
    2-3. bonus에 따라 라운드 점수를 계산한다
    2-4. 계산한 점수를 최종 리턴한다
  3. applyOption 함수 (함수명 고치고 싶다.. 영어실력의 한계..)
    3-1. 인자로 받은 라운드 배열의 길이를 구한다
    3-2. 옵션이 *라면 모든 배열의 인자에 2를 곱해준다. 단 배열의 길이가 3이고 해당 인덱스가 0일 때만 예외 (-> 이 단계가 하드코딩한 단계인데, 총 라운드가 3개라서 가능한 계산법이다. 확장성을 고려한다면 solution 함수에서 round를 변수를 선언해 라운드를 체크해서 curRound, preRound 인덱스를 구해 applyOption의 인자로 넣어줄 것 같다)
    3-3. 옵션이 #라면 배열의 마지막 요소에 -1을 곱해준다

2. 풀이

function solution(dartResult) {
    let roundScore = [];
    
    let temp = "";
    for (let letter of dartResult) {
        if (letter === "S" || letter ==="D" || letter === "T") {
            let basicScore = getBasicScore(temp, letter);
            roundScore.push(basicScore);
            temp = "";
        } else if (letter === "*" || letter === "#") {
            applyOption(roundScore, letter);
            temp = "";
        } else {
            temp += letter;
        }
    }
    
    return roundScore.reduce((a, c) => a + c);
}

// 보너스를 계산하는 함수
function getBasicScore(score, bonus) {
    let roundScore = 0;
    
    // score을 number로 변환
    score = +score;
    
    // 라운드 점수 계산
    if (bonus === "S") roundScore = Math.pow(score, 1);
    if (bonus === "D") roundScore = Math.pow(score, 2);
    if (bonus === "T") roundScore = Math.pow(score, 3);
    
    return roundScore;
}

// 옵션을 계산하는 함수
function applyOption(roundScore, option) {
    let length = roundScore.length;
    
    if (option === "*") {
        roundScore.forEach((score, index) => {
            if (!(length === 3 && index === 0)) {
                roundScore[index] *= 2;
            } 
        })
    }
    if (option === "#") {
        roundScore[length - 1] *= -1;
    }
}

3. 아쉬운 김에 수정해 봄

리팩토링하면서 변수명도 고치고 하드코딩한 부분의 확장성도 고쳤다. 이제 다른 사람들 코드 보러 가야지!

function solution(dartResult) {
    let roundScore = [];
    
    let score = "";
    for (let letter of dartResult) {
        if (letter === "S" || letter ==="D" || letter === "T") {
            let basicScore = applyBonus(score, letter);
            roundScore.push(basicScore);
            score = "";
        } 
        else if (letter === "*" || letter === "#") {
            let curRoundIndex = roundScore.length - 1;
            applyOption(roundScore, curRoundIndex, letter);
        } 
        else {
            score += letter;
        }
    }
    
    return roundScore.reduce((a, c) => a + c);
}

function applyBonus(score, bonus) {
    if (bonus === "S") return Math.pow(+score, 1);
    if (bonus === "D") return Math.pow(+score, 2);
    if (bonus === "T") return Math.pow(+score, 3);
}

function applyOption(roundScore, curRoundIndex, option) {
    let length = roundScore.length;
    
    if (option === "*") {
        roundScore[curRoundIndex] *= 2;
        if (curRoundIndex - 1 >= 0) roundScore[curRoundIndex - 1] *= 2
    }
    if (option === "#") {
        roundScore[length - 1] *= -1;
    }
}

4. 다른 사람들의 풀이

  1. 몇 가지 코드를 찾아봤는데 일단 다른 사람들은 함수를 분리하지 않고 for문 안에서 다 해결했던 것 같다. 나는 let of 반복문을 돌았는데 그냥 정석으로 i로 도는 반복문을 사용했다면 10점을 위한 예외처리가 쉬웠을 것 같다.
for (let i = 0; i < dartResult.length; i++) {
  // 점수가 10인 경우의 수
  if (dartResult[i + 1] === "0") {
    temp = dartResult[i] + dartResult[i + 1];
    i++
  }
}
  1. 그리고 반복문을 다 돌지 않고도 i+1이 보너스인지, i+2가 옵션인지 바로 확인해서 i++를 해주는 방법도 있었다. 이러면 반복문을 굳이 다 돌지 않아도 되니까 더 효율적인듯
for (let i = 0; i < dartResult.length; i++) {
  // 점수 추출하기
  
  // 보너스 추출하기
  if (dartResult[i + 1] === "S" || /* 기타 생략 */) {
      /* 대충 보너스 계산 */
      i++;
  }
  
  // 옵션 추출하기
  if (dartResult[i + 1] === "#" || /* 기타 생략 */) {
      /* 대충 옵션 계산 */
      i++;
  }
}
  
  1. 무려 정규표현식으로... 푼 사람도 있었다... 깔끔깔끔
var dr = dartResult.match(/\d{1,2}[SDT]{1}[*|#]?/g )

// 출처: https://jo-c.tistory.com/23 [조씨의 개발 블로그]
  1. 객체를 활용해서 정보를 담아두는 사람도 있었다. SDT와 #* 모두 객체에 키값으로 담아두면 나중에 확장성에도 좋고 수정할 때도 좋을 것 같다. 좋은 아이디어!
let score = {S : 1, D : 2, T : 3}; //영역에 따른 제곱 수

let data = dartResult.charAt(i);
stack.push(Math.pow(dartResult.slice(i - count, i), score[data]));
  
// 출처 : https://akh95123.blogspot.com/2019/11/javascript_12.html
  1. 아무 생각 없이 풀었는데 이 문제는 스택 자료구조 문제였구나.. 스택에 넣어서 보너스와 옵션 적용시 마지막 요소를 넣었다 뺐다 넣었다 뺐다 하면서 푸는 사람도 있었다.

0개의 댓글