스터디 기록 13

유아현·2022년 12월 5일
0

Study

목록 보기
14/27
post-thumbnail

오늘의 스터디 문제 목록

크래인 인형뽑기 게임

게임개발자인 "죠르디"는 크레인 인형뽑기 기계를 모바일 게임으로 만들려고 합니다.
"죠르디"는 게임의 재미를 높이기 위해 화면 구성과 규칙을 다음과 같이 게임 로직에 반영하려고 합니다.

게임 화면은 "1 x 1" 크기의 칸들로 이루어진 "N x N" 크기의 정사각 격자이며 위쪽에는 크레인이 있고 오른쪽에는 바구니가 있습니다. (위 그림은 "5 x 5" 크기의 예시입니다). 각 격자 칸에는 다양한 인형이 들어 있으며 인형이 없는 칸은 빈칸입니다. 모든 인형은 "1 x 1" 크기의 격자 한 칸을 차지하며 격자의 가장 아래 칸부터 차곡차곡 쌓여 있습니다. 게임 사용자는 크레인을 좌우로 움직여서 멈춘 위치에서 가장 위에 있는 인형을 집어 올릴 수 있습니다. 집어 올린 인형은 바구니에 쌓이게 되는 데, 이때 바구니의 가장 아래 칸부터 인형이 순서대로 쌓이게 됩니다. 다음 그림은 [1번, 5번, 3번] 위치에서 순서대로 인형을 집어 올려 바구니에 담은 모습입니다.

만약 같은 모양의 인형 두 개가 바구니에 연속해서 쌓이게 되면 두 인형은 터뜨려지면서 바구니에서 사라지게 됩니다. 위 상태에서 이어서 [5번] 위치에서 인형을 집어 바구니에 쌓으면 같은 모양 인형 두 개가 없어집니다.

크레인 작동 시 인형이 집어지지 않는 경우는 없으나 만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 또한 바구니는 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정합니다. (그림에서는 화면표시 제약으로 5칸만으로 표현하였음)
게임 화면의 격자의 상태가 담긴 2차원 배열 board와 인형을 집기 위해 크레인을 작동시킨 위치가 담긴 배열 moves가 매개변수로 주어질 때, 크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수를 return 하도록 solution 함수를 완성해주세요.

function solution(board, moves) {
  let basket = [];
  let arr = [];
  let newboard = [];
  let bang = 0;

  // 크레인 작동할 board 만들어 주기
  for (let i = 0; i < board.length; i++) {
    for (let j = 0; j < board.length; j++) {
      arr.push(board[j][i]);
    }
    newboard.push(arr);
    arr = [];
  }

  moves.forEach((ele) => {
    const bring = game(newboard, ele - 1);

    // 가져온 게 0보다 크면 인형을 가져 왔다는 것
    if (bring > 0) {
      //  바구니에서 제일 최근에 들어간 인형과 지금 가지고 온 인형이 같을 경우
      if (basket[basket.length - 1] === bring) {
        // 두 개 터짐
        bang += 2;
        // 최근 들어간 인형 바구니에서 꺼냄
        // 지금 가지고 온 인형을 push 후, 팝팝해도 되지만 귀찮으니까 바구니에 넣기 전에 최근 넣었던 인형과 같으면 하나만 팝해 줌

        basket.pop();
      } else {
        basket.push(bring); // 인형을 바구니에 담기
      }
    }
  });

  return bang;
}
// ele은 뽑아야 하는 줄
function game(newboard, ele) {
  let result = 0;
  for (let i = 0; i < newboard[ele].length; i++) {
    if (newboard[ele][i] !== 0) {
      result = newboard[ele][i];
      newboard[ele][i] = 0;
      return result;
    }
  }
  return 0;
}

solution(
  [
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, 3],
    [0, 2, 5, 0, 1],
    [4, 2, 4, 4, 2],
    [3, 5, 1, 3, 1],
  ],
  [1, 5, 3, 5, 1, 2, 1, 4]
);

// [
//   [0, 0, 0, 4, 3],
//   [0, 0, 2, 2, 5],
//   [0, 1, 5, 4, 1],
//   [0, 0, 0, 4, 3],
//   [0, 3, 1, 2, 1],
// ]

주어진 board를 그대로 사용할 수 있었지만 열 기준으로 인형을 뽑았어야 됐으므로 헷갈리니까 그냥 열을 기준으로 해서 배열을 새로 만들어서 풀었다. 크레인 작동하는 board를 새로 만들어 주고 moves를 하나씩 돌면서 moves의 각 요소들은 뽑아야 하는 열을 뜻하므로 인형을 뽑는 함수를 따로 정의해 줬다. 해당 열을 for문으로 돌려 각 요소가 0이 아니라면 인형을 뽑는 것이므로 result에 할당 뒤 그 자리를 0으로 바꿔 주고 리턴 시켜 주었고, 계속 0만 있는 경우에는 해당 줄에는 인형이 없는 것이므로 그대로 0을 리턴해 줬다. 그렇게 아무것도 가지지 않고 온 경우(0)과 인형을 뽑은 경우(0 이상인 경우)가 있다. 인형을 뽑은 경우에는 전에 넣었던 인형과 같은 인형이라면 bang! 두 번 터지고 같은 요소는 삭제해 주는 식으로 풀었다. 인덱스 주는 건 역시 아직 헷갈린다... 더 이해를 하기 위해서는 그림을 그려서 하는 방법도 좋을 것 같다.

function solution(board, moves) {
    // 같은 모양 2개면 사라짐
    // 인덱스와 같은 key 값을 갖는 객체를 만든 후
    // 해당 key 값에 따라 --를 해준다.
    // 그리고 basket 넣어주고
    // 연속된 값이 오면 처리
    let answer = 0;
    const basket = [];
    const obj = {};
    // 원할이 pop을 하기 위해 순서를 역전
    // board.reverse();
    for(let i = board.length - 1; i >= 0; i--){
        board[i].forEach((value, index)=>{   
        if(obj[index+1]){
            // value가 0인 경우를 결러줌
            if(value)obj[index+1].push(value);
        } else{
            if(value)obj[index+1] = [value];
        } 

        })
    }
//     for(arr of board){
//         // arr.reverse();
//         arr.forEach((value, index)=>{   
//         if(obj[index+1]){
//             // value가 0인 경우를 결러줌
//             if(value)obj[index+1].push(value);
//         } else{
//             if(value)obj[index+1] = [value];
//         } 

//         })

//     }
    // console.log(obj);
    for(value of moves){
        if(obj[String(value)].length){
            basket.push(obj[value].pop());
            // console.log(basket);
            if(basket[basket.length - 1] === basket[basket.length - 2]){
            basket.pop();
            basket.pop();
            answer += 2;
        }
        }
        
    }
    
    return answer;
}

효근님의 코드! 애초에 크레인을 작동시킬 보드에 인형만 들어가게 0이 되는 것들은 걸러서 만들어 주고 해당 인덱스를 뽑아서 할 수 있게 작성하셨다.

옹알이 (2)

머쓱이는 태어난 지 11개월 된 조카를 돌보고 있습니다. 조카는 아직 "aya", "ye", "woo", "ma" 네 가지 발음과 네 가지 발음을 조합해서 만들 수 있는 발음밖에 하지 못하고 연속해서 같은 발음을 하는 것을 어려워합니다. 문자열 배열 babbling이 매개변수로 주어질 때, 머쓱이의 조카가 발음할 수 있는 단어의 개수를 return하도록 solution 함수를 완성해주세요.

function solution(babbling) {
  //! 할 수 있는 발음 ["aya", "ye", "woo", "ma"]
  // 연속으로 안 되는 발음
  const continuous = ["ayaaya", "yeye", "woowoo", "mama"];

  let result = 0;
  // ["ayaye", "uuu", "yeye", "yemawoo", "ayaayaa"]
  for (let i = 0; i < babbling.length; i++) {
    //! 연속으로 발음되는 문자열이 포함됐다면, 다음으로 넘어가야 됨.
    for (let talk of continuous) {
      if (babbling[i].includes(talk)) {
        // 현재 순회 중인 요소를 발음할 수 없는 상태인 false로 바꿈
        babbling[i] = false;
        break;
      }
    }

    // 요소가 할 수 없는 발음(false)인 경우 다음 요소로 넘어감
    if (!babbling[i]) continue;

    //! 할 수 있는 발음을 숫자로 바꾸어 준다.
    //? aya = 1 , ye = 2, woo = 3, ma = 4
    babbling[i] = babbling[i]
      .replaceAll("aya", "1")
      .replaceAll("ye", "2")
      .replaceAll("woo", "3")
      .replaceAll("ma", "4");

    //! 숫자를 공백으로 바꿔 준다.
    babbling[i] = babbling[i].replaceAll(/[1-4]/g, "");

    if (!babbling[i].length) result++;
    // if(babbling[i].length === 0) 0은 false니까 한 번 부정
  }

  console.log(result);
  return result;
}

solution(["ayaye", "uuu", "yeye", "yemawoo", "ayaayaa", "wooyemawooye"]);

간단한 줄 알았는데 생각보다 많이... 어려웠던 문제다 처음에는 replace만 썼다가 정규식 쓸 생각을 아예 못했다 연속되는 발음 처리도 어떻게 할까 고민을 정말 많이 했던 문제 고민하다 나온 결과 코드이다! 특히 이번에 옹알이 문제를 풀면서 if 조건에 불리언 타입을 넣어 하는 방법을 많이 익힐 수 있었으며 정규식에 대한 이해가 좀 많이 됐다!

https://curryyou.tistory.com/234
위 링크를 통해 정말,,,, 많은 도움을 받았다 감사합내다 선생님

function solution(babbling) {
    const regx1 = /(aya|ye|woo|ma)\1/ //ayaaya , yeye , woowoo , mama 처럼 중복인지 찾는 조건
    const regx2 = /^(aya|ye|woo|ma)+$/ // "aya", "ye", "woo", "ma" 이외에 다른 문자가 없는 조건
    let count = 0

    babbling.forEach(value => {
        if(!regx1.test(value) && regx2.test(value)) count++ // (중복)이 아니면서 (다른 문자)가 없는 조건
    })

    return count

정규식 마스터 하신 지원님 코드 정규식이 좋긴 좋구나야,, 지원님이 정규식 코드 작성은
https://abangpa1ace.tistory.com/233?category=910462 이 블로그름 참고하셨다고 한다. 이 블로그의 도움도 받아야지!

function solution(babbling) {
  const canPronounce = ["aya", "ye", "woo", "ma"];
  let result = 0;
  for (const word of babbling) {
    let letterIdx = 0;
    let formerCpIdx = -1;
    while (letterIdx < word.length) {
      let flag = false;
      for (let i = 0; i < canPronounce.length; i++) {
        if (i === formerCpIdx) continue;
        if (
          word.slice(letterIdx, letterIdx + canPronounce[i].length) ===
          canPronounce[i]
        ) {
          flag = true;
          formerCpIdx = i;
          letterIdx += canPronounce[i].length;
          break;
        }
      }
      if (!flag) break;
    }
    if (letterIdx === word.length) result++;
  }
  return result;
}

그리고 대박대박대박소리밖에 안 나왔던 민혁님 코드 나도 처음에는 정규식 사용 없이 이렇게 풀려고 했으니 머리가 따라주지 않아서 포기했었는데... 답은 silce였군,,, 하하 babbling의 각 요소에서 요소들의 각 인덱스마다 설정을 해 줘서 푸셨다........ 이 코드는 디버깅하면서 다시 이해하는 걸로!

if (dup.some(v=>b.includes(v))) continue;
        // 가능한 발음을 모두 숫자로 바꾸어

동원님의 코드에서 가능한 발음들은 숫자로 바꾸는 코드만 뜯어 왔다. some이라는 게 생소해서 한번 짚고 넘어가고 싶었기 때문이다! 저번에 시간 복잡도를 알아보면서 어렴풋이 봤던 기억이 있어서 확실하게 보면 좋을 것 같다.

Array.prototype.some()

  • 배열 안의 어떤 요소라도 주어진 판별 함수를 통과하는지 테스트한다.
    some() 메서드는 빈 배열에서 호출하면 무조건 false를 반환한다고 한다.
    배열 중에서 배열 요소 중에서 조건이 하나라도 부합한다면 true로 반환한다는 것이다.

명예의 전당 (1)

"명예의 전당"이라는 TV 프로그램에서는 매일 1명의 가수가 노래를 부르고, 시청자들의 문자 투표수로 가수에게 점수를 부여합니다. 매일 출연한 가수의 점수가 지금까지 출연 가수들의 점수 중 상위 k번째 이내이면 해당 가수의 점수를 명예의 전당이라는 목록에 올려 기념합니다. 즉 프로그램 시작 이후 초기에 k일까지는 모든 출연 가수의 점수가 명예의 전당에 오르게 됩니다. k일 다음부터는 출연 가수의 점수가 기존의 명예의 전당 목록의 k번째 순위의 가수 점수보다 더 높으면, 출연 가수의 점수가 명예의 전당에 오르게 되고 기존의 k번째 순위의 점수는 명예의 전당에서 내려오게 됩니다. 이 프로그램에서는 매일 "명예의 전당"의 최하위 점수를 발표합니다. 예를 들어, k = 3이고, 7일 동안 진행된 가수의 점수가 [10, 100, 20, 150, 1, 100, 200]이라면, 명예의 전당에서 발표된 점수는 아래의 그림과 같이 [10, 10, 10, 20, 20, 100, 100]입니다.

명예의 전당 목록의 점수의 개수 k, 1일부터 마지막 날까지 출연한 가수들의 점수인 score가 주어졌을 때, 매일 발표된 명예의 전당의 최하위 점수를 return하는 solution 함수를 완성해주세요.

function solution(k, score) {
  let arr = [];
  let best = [];
  let worst = [];
  for (let i = 0; i < score.length; i++) {
    // 출연 한 명씩 함.
    arr.push(score[i]);

    //! 점수 높은 순으로 정렬
    arr.sort((a, b) => Number(b) - Number(a));

    //! 명예의 전당
    best = [];
    for (let j = 0; j < k; j++) {
      best.push(arr[j]);
    }
    // 출연진이 K 미만일 경우에 undefined가 들어감
    //! undefined 들어갈 때
    for (let x = k - 1; x >= 0; x--) {
      if (best[x] === undefined) best.pop();
    }

    //! 명예의 전당에서 최하위 뽑기
    worst.push(best[best.length - 1]);
  }

  return worst;
}

solution(3, [10, 100, 20, 150, 1, 100, 200]);

나는 출연진의 점수를 받은 배열들을 하나씩 push 해 주면서 for문을 돌게 했다. 한 번의 for문이 돌아갈 때마다 정렬-k번까지(1등부터 k까지) 거르기 - 최하위 뽑아서 새 배열에 넣기 순서로 끝까지 돌게 되면 최하위의 점수만 들어있는 배열을 리턴하게 했다. 여기에서 명예의 전당의 최하위 순위를 뽑게 되는 코드를 worst.push(best[best.length - 1]); 라고 해 주었다. 맨 마지막의 인덱스의 위치는 -1로 알고 있었는데 best[-1]로 하면 결과가 나오지 않아 의문점을 가졌었는데 승연님이 말끔하게 해결해 주셨다! ㅎㅎㅎㅎ

lowest.push(honor.at(-1)); 승연님이쓰신 마지막 인덱스를 뽑아오는 코드이다. .at()을 사용하여 구구절절 .length-1을 쓰지 않아도 된다...!! 오늘 처음 알게 된 사실이다 ㅎㅎ

Array.prototype.at()

  • 정수 값을 받아, 배열에서 해당 값에 해당하는 인덱스 요소를 반환한다.
    at에 내가 알고자하는 인덱스의 요소를 알 수 있다는 것이다. 배열에서 arr[-1]가 작동하지 않아 나오게 된 매서드라고 한다. arr.at(-1)로 지정해 줄 시, 인덱스의 마지막 요소에 접근할 수 있는 매우 유용한 정보,,!

0개의 댓글