틱택토 게임 화면 | 이미지 출처 출처 - 본인 프로젝트

틱택토 게임 화면 | 이미지 출처 출처 - 본인 프로젝트

틱택토 소개


틱택토는 2명의 플레이어가 자신의 기호(X, O) 3개를 가로, 세로, 대각선으로 연속해서 놓이도록 하는 보드 게임이다. 게임은 주로 3x3 격자 보드에서 진행된다. 플레이어는 번갈아가면서 자신의 기호를 놓고, 한 칸엔 한 개의 기호만 놓을 수 있다. 모든 칸에 기호를 놓았지만 어느 한쪽도 연속적인 세 개의 기호를 배열하지 못하면 게임은 무승부로 끝난다(무승부가 많은 게임).

승리 조건 체크


틱택토는 행렬로 이뤄진 2차원 보드에서 진행하지만, 1차원 배열로 관리하면 각 칸을 단일 인덱스로 접근할 수 있기 때문에 데이터를 더 수월하게 관리할 수 있다. 게임 로직을 구현할 때도 인덱스 계산을 단순화시켜 코드의 복잡성을 줄이는 데 도움이 된다. 또한, 1차원 배열은 그리드 스타일을 이용해 2차원 보드로 렌더링 할 수 있다.

const board = [null, 'O', 'X', null, null, 'O', ...];

/*
  [null, 'O', 'X']
  [null, null, 'O']    
  [null, null, null]
*/

Basic

3x3 보드 각 격자에 인덱스를 표기한 이미지

3x3 보드 각 격자에 인덱스를 표기한 이미지

승리 조건을 검사하는 가장 간단한 방법은 승리할 수 있는 경우의 수(인덱스)를 2차원 배열에 저장해두고, 현재 게임 보드와 비교하는 방식이다. 3x3 보드에선 가로, 세로, 대각선 총 8가지 방법($size \times 2 +2$)으로 승리할 수 있다. React 공식 문서에 있는 Tic-Tac-Toe 튜토리얼도 이 방법을 사용한다.

function calculateWinner(board: number[]) {
  const lines = [
    [0, 1, 2], // row 1
    [3, 4, 5], // row 2
    [6, 7, 8], // row 3
    [0, 3, 6], // column 1
    [1, 4, 7], // column 2
    [2, 5, 8], // column 3
    [0, 4, 8], // diagonal 1
    [2, 4, 6], // diagonal 2
  ];

  for (const [a, b, c] of lines) {
    const isLineMatch = board?.[a] === board?.[b] && board?.[a] === board?.[c];
    if (isLineMatch) return board[a]; // 'X' | 'O'
  }

  return null;
}

하지만 이 방법은 고정된 보드 크기와 미리 정의한 승리 조건에만 최적화 되어 있기 때문에, 승리 조건이나 보드 크기를 변경할 수 있는 상황에선 적합하지 않다. 다른 보드 크기의 모든 가능한 승리 조합을 수동으로 추가하는건 비효율적이므로 좀 더 유연한 방법이 필요하다.

Advanced



마지막 놓았던 격자 위치를 기준으로 가로, 세로, 대각선 방향으로 승리 조건 수 만큼 격자를 검사하면 보드 크기나 승리 조건이 동적인 경우에도 승리 여부를 검사할 수 있다. 예를들어 4×4 보드에서 10번 인덱스에 놓았다면 해당 인덱스를 기준으로 가로, 세로, 대각선 방향으로 승리 조건에 만족하는지 검사하면 된다.