codingame winamax-battle

Maliethy·2021년 5월 6일
0

algorithm

목록 보기
3/5

0. 문제

https://www.codingame.com/ide/puzzle/winamax-battle

input값을 얻기 위한 코드

const n = parseInt(readline()); // the number of cards for player 1
let game = {p1:[], p2:[]}; 
for (let i = 0; i < n; i++) {
    const cardp1 = readline();
     game.p1.push(cardp1);// the n cards of player 1
}
const m = parseInt(readline()); // the number of cards for player 2
for (let i = 0; i < m; i++) {
    const cardp2 = readline();
     game.p2.push(cardp2); // the m cards of player 2
}

input값 예시
case) long game
const n = 26,
m = 26;
const game = {
p1: [
'AH',
'4H',
'5D',
'6D',
'QC',
'JS',
'8S',
'2D',
'7D',
'JD',
'JC',
'6C',
'KS',
'QS',
'9D',
'2C',
'5S',
'9S',
'6S',
'8H',
'AD',
'4D',
'2H',
'2S',
'7S',
'8C',
],
p2: [
'10H',
'4C',
'6H',
'3C',
'KC',
'JH',
'10C',
'AS',
'5H',
'KH',
'10S',
'9H',
'9C',
'8D',
'5C',
'AC',
'3H',
'4S',
'KD',
'7C',
'3S',
'QH',
'10D',
'3D',
'7H',
'QD',
],
};

결과값 
1 52

1. 문제해결을 위한 개념

데이터를 저장하는 자료 구조 중 큐와 스텍의 차이를 비교하면

큐(queue): 먼저 들어온 데이터가 먼저 빠져나간다(FIFO, First In First Out)

은행에서 업무를 보기 위해 번호표를 받고 줄을 서는 경우, 대기열의 맨 앞 고객이 제일 먼저 줄을 서고 업무도 먼저 처리된다면, 나중에 온 고객은 대기열의 맨 뒤에 서고 앞의 고객들이 처리되면 업무가 처리될 것이다.

  • queue의 기능에는 대표적으로 2 가지가 있다.
    enqueue: 큐의 맨 뒤에 새로운 데이터를 추가한다.
    dequeue: 큐의 맨 앞에 데이터를 반환(제거)한다.

스텍(stack): 나중에 들어온 데이터를 먼저 처리한다(LIFO, Last In First Out)

어떠 일을 수행하는 도중 새로운 업무를 해야 할 경우 잠시 하던 일을 멈추고 새로운 일을 수행할 때가 있다. 새로운 일을 마치면 다시 원래 하던 일로 돌아가 일을 다시 수행한다. 하던 일을 잠시 보류하고 나중에 돌아와 다시 처리하기 위한 자료구조라고 할 수 있다.

  • stack의 기능에는 대표적으로 2 가지가 있다.
    push: 스텍의 맨 뒤에 새로운 데이터를 추가한다.
    pop: 스텍의 맨 앞에 데이터를 반환(제거)한다.

2. 문제해결 코드

이 문제를 해결하기 위해서는 큐 자료구조를 이해할 필요가 있었다. 또한 큐 자료구조에 맞게 배열에 카드 값을 넣고 뺄 때 필요한 매서드 사용법을 다시 한 번 확인하게 되었다.
예를 들어, concat으로 p1, p2(각 플레이어의 카드가 담긴 배열)에 used_cards를 분명 넣었는데 p1, p2의 값이 바뀌지 않아 당황했다. push와 concat의 차이를 알고 있다고 생각했는데 막상 사용할 때 헷갈린 것이다.
인자로 주어진 값들이나 배열을 Array.prototype.push()는 기존 배열의 값을 바꾸어 반환하지만 Array.prototype.concat()은 기존 배열의 값을 바꾸어 새로운 배열로 반환한다.
js에는 배열에서 마지막 요소를 제거하고 그 요소를 반환하는 Array.prototype.pop() 메서드가 있다. 이 문제에서는 여러 요소를 제거하는데 편리한 Array.prototype.splice() 메소드를 사용했다. splice()는 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경할 수 있다.

처음 풀었던 코드 중 p1의 카드 값이 p2의 카드 값보다 큰 경우

if (card.indexOf(game.p1[i][0]) > card.indexOf(game.p2[j][0])) {
      if (war_turn) {
        save_used_card(i, j);
        game.p1.splice(0, war_turn * 4 + 1);
        game.p2.splice(0, war_turn * 4 + 1);
        game.p1.push(...used_cards);
        used_cards = [];
      } else {
        game.p1.push(game.p1[i], game.p2[j]);
        game.p1.splice(0, 1);
        game.p2.splice(0, 1);
      }
      count++;
      battle(0, 0, 0);

풀었던 코드를 다시 살펴보니 p1과 p2의 index는 똑같기 때문에 굳이 i, j로 나누지 않고 index 매개변수를 이용해 사용할 수 있었다. 또한 무승부일 경우 index를 4씩 증가시키면 되고 굳이 무승부일 경우와 바로 결투일 경우를 나누어 처리할 필요도 없었다. 무승부가 아닐 경우는 used_cards에 index가 0이 되므로 각 플레이어의 카드 배열에서 가장 첫번째 카드들이 담기게 될 것이다.

 else if (card.indexOf(game.p1[i][0]) === card.indexOf(game.p2[j][0])) {
      if (i + 4 < game.p1.length && j + 4 < game.p2.length) {
        war_turn++;
        battle(i + 4, j + 4, war_turn);
      } else {
        return (answer = 'PAT');
      }
    }

무승부일 경우에서 전쟁의 횟수(war_turn)를 매개변수로 넘겨줄 필요도 없다.

최종적으로 해결한 코드는 다음과 같다.

let game = {p1:[], p2:[]};
const card = ['2', '3', '4', '5', '6', '7', '8', '9', '1', 'J', 'Q', 'K', 'A'];
let count = 0,
  answer = '',
  used_cards = [];

function push_used_cards(index) {
  let a = 0,
    b = 0;

  while (a <= index) {
    used_cards.push(game.p1[a]);
    a++;
  }
  while (b <= index) {
    used_cards.push(game.p2[b]);
    b++;
  }

  // console.log('used_cards:', used_cards);
  return used_cards;
}

function battle(index, war_turn) {
  if (game.p1[index] !== undefined && game.p2[index] !== undefined) {
    if (card.indexOf(game.p1[index][0]) > card.indexOf(game.p2[index][0])) {
      if (war_turn) {
        push_used_cards(index);
        game.p1.splice(0, war_turn * 4 + 1);
        game.p2.splice(0, war_turn * 4 + 1);
        game.p1.push(...used_cards);
        used_cards = [];
      } else {
        game.p1.push(game.p1[index], game.p2[index]);
        game.p1.splice(0, 1);
        game.p2.splice(0, 1);
      }
      count++;
      battle(0, 0, 0);
    } else if (card.indexOf(game.p1[index][0]) < card.indexOf(game.p2[index][0])) {
      if (war_turn) {
        push_used_cards(index);
        game.p1.splice(0, war_turn * 4 + 1);
        game.p2.splice(0, war_turn * 4 + 1);
        game.p2.push(...used_cards);
        used_cards = [];
      } else {
        game.p2.push(game.p1[index], game.p2[index]);
        game.p1.splice(0, 1);
        game.p2.splice(0, 1);
      }
      count++;
      battle(0, 0, 0);
    } else if (card.indexOf(game.p1[index][0]) === card.indexOf(game.p2[index][0])) {
      if (index + 4 < game.p1.length && index + 4 < game.p2.length) {
        war_turn++;
        battle(index + 4, war_turn);
      } else {
        return (answer = 'PAT');
      }
    }
  } else {
    if (game.p1.length > game.p2.length) {
      return (answer = `1 ${count}`);
    } else if (game.p1.length < game.p2.length) {
      return (answer = `2 ${count}`);
    } else if (game.p1.length === game.p2.length) {
      return (answer = 'PAT');
    }
  }
}

battle(0, 0);
console.log(answer);

참고도서:
게임으로 익히는 코딩 알고리즘

profile
바꿀 수 있는 것에 주목하자

0개의 댓글