# 다트 게임

## 1. 개요

* 프로그래머스
* Lv.1
* 2018 KAKAO BLIND RECRUITMENT
* [문제 바로가기](https://school.programmers.co.kr/learn/courses/30/lessons/17682)

***

## 2. 문제 설명

카카오톡에 뜬 네 번째 별! 심심할 땐? 카카오톡 게임별\~

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

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, \*, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.

***

### 2-1. 문제 설명 - 입력 형식

"점수|보너스|\[옵션]"으로 이루어진 문자열 3세트.

예) 1S2D\*3T

* 점수는 0에서 10 사이의 정수이다.
* 보너스는 S, D, T 중 하나이다.
* 옵선은 \*이나 # 중 하나이며, 없을 수도 있다.

***

### 2-2. 문제 설명 - 출력 형식

3번의 기회에서 얻은 점수 합계에 해당하는 정수값을 출력한다.\
예) 37

***

### 2-3. 문제 설명 - 입출력 예제

| 예제 | dartResult | answer | 설명                             |
| -- | ---------- | ------ | ------------------------------ |
| 1  | 1S2D\*3T   | 37     | 11 \_ 2 + 22 \_ 2 + 33         |
| 2  | 1D2S#10S   | 9      | 12 + 21 \* (-1) + 101          |
| 3  | 1D2S0T     | 3      | 12 + 21 + 03                   |
| 4  | 1S*2T*3S   | 23     | 11 \_ 2 \_ 2 + 23 \* 2 + 31    |
| 5  | 1D#2S\*3S  | 5      | 12 \_ (-1) \_ 2 + 21 \* 2 + 31 |
| 6  | 1T2D3D#    | -4     | 13 + 22 + 32 \* (-1)           |
| 7  | 1D2S3T\*   | 59     | 12 + 21 \_ 2 + 33 \_ 2         |

***

## 3. 문제 풀이

```javascript
function solution(dartResult) {
  // 1) 점수 부분 가져오기
  const score = dartResult.match(/[0-9]{1,2}/g);

  // 2) 점수 부분을 제외한 부분 가져오기
  const option = dartResult
    .replace(/[0-9]{1,2}/g, ",")
    .split(",")
    .slice(1);

  // 3) [점수, 보너스, 옵션] 형태의 요소를 가지는 배열 만들기
  const result = score.map((item, index) => [item, ...option[index]]);

  // 4) 맵 객체를 사용하여 점수 관리하기
  const map = new Map();
  result.forEach(([score, option1, option2], index) => {
    // 5) 보너스의 종류에 따라 초기 점수 설정하기
    const bonus1 = option1 === "S" ? 1 : option1 === "D" ? 2 : 3;
    map.set(index, Number(score) ** bonus1);

    // 6) 옵션이 "*"라면 해당 점수와 이전 점수에 *2 하기
    if (option2 === "*") {
      map.set(index, map.get(index) * 2);
      map.set(index - 1, map.get(index - 1) * 2);
    }

    // 7) 옵션이 "#"이라면 해당 점수에 *-1 하기
    if (option2 === "#") {
      map.set(index, map.get(index) * -1);
    }
  });

  // 8) 첫 번째, 두 번째, 세 번째 점수를 더한 값을 반환하기
  return map.get(0) + map.get(1) + map.get(2);
}
```

***

### 1) 점수 부분 가져오기

```javascript
const score = dartResult.match(/[0-9]{1,2}/g);
```

`dartResult` 문자열을 검사하여 정규식에 일치하는 문자열을 가져온다.

* \[0-9]: 0이상 9이하의 숫자를 포함한 문자열과 매칭
* {1,2}: 1개 이상 2개 이하
* \[0-9]{1,2}: 1개 이상 2개 이상의 숫자(0\~9)를 포함한 문자열과 매칭

***

### 2) 점수 부분을 제외한 부분 가져오기

```javascript
const option = dartResult
  .replace(/[0-9]{1,2}/g, ",")
  .split(",")
  .slice(1);
```

`String.replace()` 메서드를 사용하여 숫자부분은 ","으로 바꾼 후 "," 기준으로 배열을 만든다. 이때 첫 번째 요소는 비어있기 때문에 첫 번째 인덱스 이후의 요소만 배열에 저장한다.

***

### 3) \[점수, 보너스, 옵션] 형태의 요소를 가지는 배열 만들기

```javascript
const result = score.map((item, index) => [item, ...option[index]]);
```

`result` 배열에 각 \[라운드의 점수, 보너스, 옵션] 을 요소로 저장한다.

***

### 4) 맵 객체를 사용하여 점수 관리하기

```javascript
const map = new Map();
```

맵 객체를 사용하여 각 라운드의 점수를 관리할 것이다. 맵 객체를 사용하는 까닭은 중복된 키가 저장되지 않기 때문이다. 이때 라운드가 키, 점수가 값이 된다.

***

### 5) 보너스의 종류에 따라 초기 점수 설정하기

```javascript
const bonus1 = option1 === "S" ? 1 : option1 === "D" ? 2 : 3;
map.set(index, Number(score) ** bonus1);
```

두 번째 인자인 `option1`은 영역에 따라 점수에 1제곱, 2제곱, 3제곱을 할지 정한다.

`option1`의 값에 따라 스타상, 아차상이 계산되지 않는 점수를 구한 후 맵 객체에 저장한다.

***

### 6) 옵션이 "\*"라면 해당 점수와 이전 점수에 \*2 하기

```javascript
if (option2 === "*") {
  map.set(index, map.get(index) * 2);
  map.set(index - 1, map.get(index - 1) * 2);
}
```

`option2`를 통해 스타상인지 아차상인지 알 수 있다.(없을 수도 있다.)

만약 `option2`가 "\*"이라면 스타상이므로 현재의 점수와 이전 점수에 각각 \*2를 한 점수를 맵 객체에 저장한다.

***

### 7) 옵션이 "#"이라면 해당 점수에 \*-1 하기

```javascript
if (option2 === "#") {
  map.set(index, map.get(index) * -1);
}
```

만약 `option2`가 "#"이라면 아차상이므로 현재의 점수에만 \*-1를 한 점수를 맵 객체에 저장한다.

***

### 8) 첫 번째, 두 번째, 세 번째 점수를 더한 값을 반환하기

```javascript
return map.get(0) + map.get(1) + map.get(2);
```

반복문을 통해 점수를 구할 수도 있었지만 일단 많은 라운드가 없기 때문에 위와 같이 코드를 작성하였고 반복문을 작성하게 되면 키가 `-1`인 경우가 있을 수 있으므로 코드가 더 복잡해 질 수 있다고 생각하였다.

`-1`키가 생성되는 이유는 라운드 1(키가 0일 때)에서 스타상을 받을 수 있기 때문이다.

***

### 결과

![programmers\_dart\_game\_result1](/files/IX2NzgZcjVlTRVoymeBb)

***

## 4. Conclusion

> 이번 문제를 풀면서 많이 어렵지는 않았지만 문자열을 조건에 따라 나누는 것에 대해 버벅임이 있었다. 정규식에 대한 공부한 필요한 듯 하다. 계속 미루지 말고 조만간 주말에 시간을 내서 정리를 하자.

***

📅 2022-09-13


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://noah-dev.gitbook.io/til/coding-test/programmers/level1/programmers_dart_game.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
