글 작성자: 택시 운전사
반응형

🐶 리액트(React)를 이용해 이모지 빙고게임 만들기 🐶

노마드 코더 슬랙을 둘러보던 중, 빙고 게임을 과제로 만드시던 분이 빙고 검증에 대한 구현을 질문하셨다. 처음에는 Codesandbox에 돌아가는 코드를 만들어서 보여드리면 어떨까 생각하다가, "내 사이드 프로젝트로 좋을 것 같은데?"라는 생각에 바로 당일 빙고게임 사이드 프로젝트 작업을 시작했다.

컴포턴트(Component)화

시작하기에 앞서 생각났던 프로젝트가 바로 리액트 공식 문서에서 제공하는 튜토리얼인 틱택토 게임이었다. BoardSqurare로 컴포넌트화 되어있던게 빙고에 적용하기 딱이였고, 똑같이 BoardSquare라는 컴포넌트를 만들어서 진행했다.

함수구현

빙고에서 구현해야할 함수는 다음과 같다.

1. 특정 칸 클릭시 해당 칸의 상태가 변하는 함수
2. 전체 판을 확인하여 빙고의 갯수를 체크하는 함수

그리고 나는 매 시행마다 판의 구성이 변하기를 원했기 때문에 판에 들어가는 요소들을 섞는 셔플 함수까지 구현했다.

1. 특정 칸 클릭시 해당 칸의 상태 변화 함수 / handleOnClickSquare

handleOnClickSquare = (row: number, col: number) => {
  // 현재 matrix의 데이터 가공하기
  const prevMatrix = this.state.matrix;
  prevMatrix[row].splice(col, 1, {
    ...prevMatrix[row][col],
    check: !prevMatrix[row][col].check
  });
  this.setState({ matrix: prevMatrix });
};

의외로 초반부터 애를 먹었던 부분이다. prevMatrix[row][col].check = !prevMatrix[row][col].check이런식으로 될 줄 알았지만, 전혀 아니였다. 배열 안에서의 데이터 조작은 반드시 splice를 이용해야한다. splice의 매개변수는 배열의 변경을 시작하는 인덱스인 start, 제거할 요소의 수를 결정짓는 deleteCount, 대체할 요소에 해당되는 item으로 이루어져 있다.

2. 전체 판을 확인하여 빙고의 갯수를 체크하는 함수 / checkBingo

checkBingo = () => {
    const { size } = this.props;
    const { matrix } = this.state;
    let totalBingo = 0;
    // row
    for (let i = 0; i < size; i++) {
      if (matrix[i].reduce((bingo, square) => bingo && square.check, true)) totalBingo++;
    }
    // column
    for (let i = 0; i < size; i++) {
        let bingo = true;
        for (let j = 0; j < size; j++) {
            bingo = bingo && matrix[j][i].check;
        }
        if (bingo) totalBingo++;
    }
    // diagnal
    let diagnalBingoOne = true;
    let diagnalBingoTwo = true;
    for (let i = 0; i < size; i++) {
        diagnalBingoOne = diagnalBingoOne && matrix[i][size - i - 1].check;
        diagnalBingoTwo = diagnalBingoTwo && matrix[i][i].check;
    }
    if (diagnalBingoOne) totalBingo++;
    if (diagnalBingoTwo) totalBingo++;
    return totalBingo;
};

빙고는 3가지 방향으로 생길 수 있다. 가로, 세로, 그리고 대각선. 해당 함수에서는 각각을 확인하여 갯수를 파악한다. 가로 데이터의 빙고 여부의 확인을 위해서는 배열의 메소드인 reduce를 사용했다. reduce는 배열의 처음부터 끝까지 훑으면서 각 데이터들을 이용해 점진적 연산을 하는 메소드이다. 여기에선 가로 한 줄에서 칸마다의 check 값을 and 연산해가면서, 최종 값이 true일 시 빙고 갯수를 늘리는 방향으로 코드를 구현했다. 세로 데이터의 빙고여부는, 좀 더 복잡하기 때문에 일반적인 for-loop를 두 번 써서 구현했다. 다른 방법이 있다면 해당 행렬을 90도 회전한 행렬을 구해서 확인하는 방법이 있겠지만, 시간도 더 많이 걸리고 그다지 마음에 들지 않는 방법이라 기존 방식대로 짰다. 대각선은 빙고판에서 2가지가 있기 때문에 그에 대한 함수도 구현했다.

3. 셔플 함수 / shuffle

셔플 함수는 과거 <더 많이 더 적게>의 키워드를 셔플하는 함수를 재사용했다.

shuffle = (a: any) => {
    for (let i = a.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() \* (i + 1));
      \[a\[i\], a\[j\]\] = \[a\[j\], a\[i\]\];
    }
    return a;
};

그다지 어렵지 않게, 굉장히 짧게 구현할 수 있다.

출처: https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array

그리고... 이모지😎

숫자는 너무 식상하고, 칸 내부 내용을 사용자가 자유롭게 작성 할 수 있게 하려했는데 구현 시간이 너무 길기도 하고 완성물이 이쁘지도 않을 것 같아서 간단하고 이쁜 게 뭐가 있을까 생각하다가 이모지를 넣어봤다! 결과는 대만족, 방법은 좀 무식하다. 이모지 사이트에서 마음에 드는 이모지들을 한 배열에 저장하여, 셔플 후 뿌려주는 방식이다. 하지만 잘 돌아가니 상관없다 🤣

cf. 이모지의 경우 OS 별로 OS가 같아도 버전별로 보이는 방식이 천차만별이다. 어디선 보이던게 어디선 안 보이고, PC에선 이뻤는 데, 모바일로 보니 이상해지고하는 경우가 있다. 이모지를 사용하는 프로젝트를 할 경우 고려해야 할 사항이다.

그래서 만들어진 결과물이 바로 이모지 빙고(Emoji Bingo)이다. 사이즈를 변수로 둬서 이모지의 최대 갯수에 맞춰 12X12 크기까지 가능하게 만들었다. 추가적으로 구현시 약간의 꼼수를 사용했는데, 모든 데이터 배열 과정이 Board의 constructor에서 하게 만들었기 때문에 그냥 사이즈만 변환하게되면, 사이즈는 바뀌지만 그 안의 데이터는 변하지 않게 된다. 이를 타파하기 위해 Board에 key = size + hash 으로 해서 size가 변할 때마다, 그리고 셔플을 누르면 새로운 해쉬를 반환하게 하여 셔플했을 때에도 새로운 key 값에 의해 constructor가 다시 실행하게 만들었다.

이모지 빙고 GitHub 주소

반응형