JS/VanillaJS

[VanillaJS] 바닐라js로 틱택토를 만들어보자 (2)

kth990303 2021. 10. 22. 22:24
반응형

es2021 강좌의 틱택토 구현 요구사항대로 만들어보고 있는데,

이번 포스팅에서 얘기할 내용은 강의와 다르게 알고리즘 및 코드를 짜다보니 강의코드와 완전 다른 코드가 되어버렸다...

그래도 시간복잡도 및 코드길이가 충분히 좋기 때문에 걱정할 필요는 없어보인다.

(다만, 알고리즘은 강의코드보다 아주약간 더 어려울수도?)

 

지난 포스팅은 아래와 같다.

https://kth990303.tistory.com/188

 

[VanillaJS] 바닐라js로 틱택토를 만들어보자

바닐라js로 틱택토를 만들면서 기본기를 다져보자. 틱택토는 오목과 룰이 동일하나, 가로, 세로, 대각선 중 한 방향으로 5개를 자기 것으로 칠해야하는 오목과 달리, 3개만 자기 것으로 칠해도 되

kth990303.tistory.com


컴퓨터의 턴을 추가해주자.

이번 포스팅은 사실 그렇게 길지 않다.

컴퓨터의 턴만 추가하면 끝나기 때문이다.

 

컴퓨터의 턴은 X, 나의 턴은 O로 진행할 것이다.

그러기 위해선, 컴퓨터의 턴이 될 때, 나의 click 이벤트를 기다리지 않고 자기가 스스로 X를 칠해주게 해야 한다.

그렇기 때문에 칸 선택함수 코드의 마지막 부분에서 turn==='O' ? turn='X' : turn='O'; 부분을 아래 코드로 변경하였다.

// 턴 체인지
turn='X';
setTimeout(()=>{
	computerChoice();
}, 500);

computerChoice() 함수에서 컴퓨터가 어떻게 X를 칠해주는지 볼 것이다.

아, 참고로 setTimeOut 동안 내가 칸을 선택할 수 없게 함수 맨 위에 아래 코드도 추가하였다.

// 이미 게임이 끝났거나 컴퓨터의 턴이라면
if(finished || turn === 'X') return;

기존의 finished 에다가 || 연산자와 turn ==='X' 로 조건을 추가한 것이다.

 

 

computerChoice() 함수는 아래와 같다.

// 컴퓨터의 턴
const computerChoice=()=>{
  const emptyCells=rows.flat().filter(v=>!v.textContent);
  const randomIdx=Math.floor(Math.random()*(emptyCells.length));
  const randomCell=emptyCells[randomIdx];
  randomCell.textContent=turn;
  if(checkWinner(randomCell.parentNode.rowIndex, randomCell.cellIndex)){
    $result.textContent=`${turn}님의 승리!`;
    finished=true;
    return;
  }
  turn='O';
}

filter 함수를 이용해 빈칸 중 랜덤하게 X로 칠해주는 코드이다.

X로 칠해주고 컴퓨터가 승리했는지 안했는지를 판단하기 위해 randomCell의 rowIndex, cellIndex를 파라미터로 checkWinner 함수에 넘겨준다. checkWinner가 true라면 컴퓨터의 승리이므로 리턴해준다.

 

사실 처음에는 randomCell.textContent=turn; 코드를 checkWinner 함수 아래에 넣었었다.

근데 결과가 원하는대로 나오지 않아 디버깅을 해보았다.

디버깅해보니 이미 X가 선택한 칸에 X로 칠해주지 않아 의도하지 않은 결과가 나온 것이었다.

debugger를 하는 모습

js는 c++, java와 다르게 debugger; 라고 써주면 디버깅이 되는 신기한 모습을 보여준다!

아무튼 여기까지 하면 컴퓨터의 턴에 자기가 스스로 X를 칠해주는 똑똑한 틱택토가 완성된다.


전체코드

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>틱택토</title>
  <style>
    table{
      border-collapse: collapse;
    }
    td{
      border: 1px solid black;
      width: 40px;
      height: 40px;
      text-align: center;
    }
  </style>
</head>
<body>
  <script>
    const { body }=document;

    const $table=document.createElement('table');
    const $result=document.createElement('div');
    const rows=[];
    let turn='O', count=0, finished=false;
    // 승부결정 파악 함수
    const checkWinner=(rowIndex,colIndex)=>{
      let garo=true, sero=true, diagonal=true, revDiagonal=true;
      for(let i=0;i<3;i++){
        if(rows[i][colIndex].textContent!==turn)
          sero=false;
        if(rows[rowIndex][i].textContent!==turn)
          garo=false;
        if(rows[i][i].textContent!==turn)
          diagonal=false;
        if(rows[i][2-i].textContent!==turn)
          revDiagonal=false;
      }
      return garo||sero||diagonal||revDiagonal;
    }
    // 컴퓨터의 턴
    const computerChoice=()=>{
      const emptyCells=rows.flat().filter(v=>!v.textContent);
      const randomIdx=Math.floor(Math.random()*(emptyCells.length));
      const randomCell=emptyCells[randomIdx];
      randomCell.textContent=turn;
      if(checkWinner(randomCell.parentNode.rowIndex, randomCell.cellIndex)){
        $result.textContent=`${turn}님의 승리!`;
        finished=true;
        return;
      }
      turn='O';
    }
    // 칸 선택함수
    const selectCol=(i, j)=>(e)=>{
      // 이미 게임이 끝났거나 컴퓨터의 턴이라면
      if(finished || turn === 'X') return;
      // 이미 선택했던 칸이라면
      if(e.target.textContent) return;
      e.target.textContent=turn;
      count++;
      console.log(e.target.textContent);
      // 승부가 났는가?
      if(checkWinner(i, j)){
        $result.textContent=`${turn}님의 승리!`;
        finished=true;
        return;
      }
      // 9칸(모든 칸)을 선택했는가?
      else if(count==9){
        $result.textContent=`무승부!`;
        finished=true;
        return;
      }
      // 턴 체인지
      turn='X';
      setTimeout(()=>{
        computerChoice();
      }, 500);
    }

    for(let i=0;i<3;i++){
      const $tr=document.createElement('tr');
      const cells=[];
      for(let j=0;j<3;j++){
        const $td=document.createElement('td');
        cells.push($td);
        // 클릭 이벤트
        $td.addEventListener('click', selectCol(i, j));
        $tr.append($td);
      }
      rows.push(cells);
      $table.append($tr);
    }
    body.append($table);
    body.append($result);
  </script>
</body>
</html>

강의와 다르게 코드를 짜봤는데 꿀잼이다.

시간복잡도도 괜찮고 코드길이는 더 짧고, 내가 스스로 만든 코드라 더욱 애착이 가는 이번 틱택토.

(그렇지만 html, css는 조현영님이 만들어놓은 코드 그대로...)

 

이상하게 css는 정이 잘 안간단말이지 ㅠ

아무튼 js도 나름 꿀잼언어같다.

css도 언제한번 날잡고 다시 쫙 훑어봐야겠다. 내가 디자인을 워낙 안따져서 그게 언제가 될지는 모르겠지만...

반응형