틱택토 part2

.·2021년 8월 6일
0
post-thumbnail

2차원 배열 만들기

const ROW=3
const gameBoard=[]

for (let i = 0; i < ROW; i++) {
    const tr = document.createElement("tr");
    const row = [];
    for (let j = 0; j < ROW; j++) {
      const td = document.createElement("td");
      tr.append(td);
      row.push(td);
    }
    table.append(tr);
    gameBoard.push(row);
  }
  $gameContainer.insertBefore(table, $replayBtn);

화면에 보여주기

table 안에 td 3개 각 td안에 tr 3개를 넣어줘야 하기 때문에
table을 만들고 반복문을 2개 중첩해준다

자바스크립트 데이터 처리하기

const gameBoard=[[null,'x',null],[null,'x',null],[null,'x',null]] 이렇게 하는 것보다
이 게임에서는 태그를 직접 넣어주는 것이 좋다

왜???? 는 밑에서 설명하겠다

그래서 화면만드는 반복문 돌 때 같이 처리해줘야하는데
tr 만들때 row라는 배열을 만들어주고
td을 넣어준뒤 row를 gameBoard에 push해준다

승부판단

  1. 칸이 채워질 때마다 승부가 났는지(한줄빙고 되었는지) 판단을 해야한다
  2. 클릭한 부분의 가로,세로 그리고 대각선2개를 확인해야 한다
  3. 클릭한 부분의 가로 세로를 살펴볼려면 클릭한것의 행과 열을 찾아야 한다

처음에 생각했을 때는 클릭된 td의 행,열을 찾는 생각을 못해서 모든 가로와 세로를 확인하는 비효율적인일을 했다 또한
책에서는 클린된 td가 대각선에 겹치지 않아도 그냥 대각선 2개를 검사했다

그렇다면 클릭한부분의 행과 열을 어떻게 찾을까?????

첫번째 방법

  let cellIndex;
  gameBoard.forEach((row, ri) => {
    if (row.indexOf(td) > -1) {
      rowIndex = ri;
      cellIndex = row.indexOf(td);
    }

클릭이벤트가 발생했을때 클릭된 td를 전달해준다
그러면 만들어둔 자바스크립트 데이터(gameBoard)를 돌면서 행과 열을 찾아준다

  • 모든 td태그가 들어있기 때문에 클릭된 td와 비교 가능하다

!!이 방법을 쓰면 gameBoard안에 태그가 아닌 화면에서 보이는 것과 같이 null, ⭕️, ❌ 로 데이터를 구성했을때 클릭한 td의 행과 열을 구분할 수 없게 된다

또한 null, ⭕️, ❌ 로 데이터를 구성했을때 td의 textContent(화면)와 gameBoard데이터(자바스크립트 데이터)도 바꿔줘야 하기 떄문에 코드를 두번 작성해줘야 한다 만약 td를 gameBoard에 등록해줬을 때는 td.textContent만 바꿔주면(화면만 바꿔주면) gameBoard는 신경 쓸 필요가 없고 나중에 필요할 때 사용해 줄 수 있다

두번째 방법

const rowIndex=target.parentNode.rowIndex;
const cellIndex=target.cellIndex

event.target인 td의 부모태그가 tr이다 tr태그는 rowIndex라는 속성을 제공하고
td는 cellIndex라는 속성을 제공한다
이 속성을 사용하면 코드도 짧아지고 이해도 한번에 되고 let으로 변수를 선언할 필요가 없어서 좋다

target.parentNode.children 을 console.log로 출력해 보면 배열처럼 생긴 객체가 출력이 되는데 - > {0 : td, 1 : td, 2: td}이런 객체를 유사배열 객체라고 한다
하지만 배열에서 쓰는 내장함수(메서드)를 쓸 수 없다.
Array.from(target.parentNode.children) 을 사용하여 배열로 바꿔줄 수 있다

문자열도 배열로 바꿀수 있다
Array.from('123'); // ['1', '2', '3']

찾은 rowIndex와 cellIndex를 활용하여 가로줄, 세로줄을 확인해준다
가로줄 빙고인지 확인 할때는 rowIndex를 고정하고
세로줄 빙고인지 확인 할 때는 cellIndex를 고정해준다

const checkHasWinner = (td) => {
  const hasWinner = false;
  let rowIndex;
  let cellIndex;
  gameBoard.forEach((row, ri) => {
    if (row.indexOf(td) > -1) {
      rowIndex = ri;
      cellIndex = row.indexOf(td);
    }
  });
  //가로 확인
  if (
    gameBoard[rowIndex][0].textContent === mark[currentPlayer] &&
    gameBoard[rowIndex][1].textContent === mark[currentPlayer] &&
    gameBoard[rowIndex][2].textContent === mark[currentPlayer]
  ) {
    return true;
  }
  if (
    gameBoard[0][cellIndex].textContent === mark[currentPlayer] &&
    gameBoard[1][cellIndex].textContent === mark[currentPlayer] &&
    gameBoard[2][cellIndex].textContent === mark[currentPlayer]
  ) {
    return true;
  }

  if (
    gameBoard[0][0].textContent === mark[currentPlayer] &&
    gameBoard[1][1].textContent === mark[currentPlayer] &&
    gameBoard[2][2].textContent === mark[currentPlayer]
  ) {
    return true;
  }

  if (
    gameBoard[0][2].textContent === mark[currentPlayer] &&
    gameBoard[1][1].textContent === mark[currentPlayer] &&
    gameBoard[2][0].textContent === mark[currentPlayer]
  ) {
    return true;
  }

  return hasWinner;
};

대각선은 어디에 클릭하던지 상관없이 항상 확인해 주었다
checkHasWinner는 이긴사람이 있는지 없는지 true or false를 반환해주는 함수이다
함수 첫줄에 이긴사람이 없다고 가정하고 const hasWinner=false
만약에 해당 가로줄, 세로줄, 대각선에서 빙고가 나오면 true를 return 해 함수를 바로 종료시켜 주었다
처음에는 비긴 경우까지 판단해 주려고 했지만 함수가 대환장 파티가 일어나서 이긴사람이 있는지만 판단하도록하고 비긴경우는 따로 빼주었다

const handleTdClick = (td) => {
  if (!playing) return;

  if (td.textContent) return;

  td.textContent = mark[currentPlayer];

  const hasWinner = checkHasWinner(td);
  const isDraw = checkDraw();

  if (hasWinner) {
    finishGame();
    return;
  }
  if (isDraw) {
    finishGame(isDraw);
    return;
  }
  changeTurn();
};

td를 클릭했을 때 전체적인 로직이 담긴 함수이다
만약 클릭한곳에 이미 값이 있다면 함수를 바로 return해준다
그리고 currentPlayer의 mark("❌ 또는 "⭕️")를 td에 표시해준다

게임이 끝났을 때 책에서는 removeEventLister로 클릭이벤트를 방지해주었다 이 때 주의할 점은 addEventListener에 전달한 콜백함수와 같아야 한다는 것이다

나는 removeEventListener대신 playing이라는 변수를 사용해 주었다

  • false되는 경우 -- "", undefiend, 0 , false, null, NaN

출처 Let's get it 자바스크립트 프로그래밍

profile
Divde & Conquer

0개의 댓글