React

리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 3편, 전체코드공유

히새 2024. 5. 10. 17:53

https://h2s0.tistory.com/97

 

리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 1편

안녕하세요 희새입니다!일이 있어서 (?) 리액트로 틱택토 게임을 구현해야 하는데, 헷갈려서 정리하고자 + 지피티에게 물어본 것들을 더 잘 이해하기 위해서 글을 씁니다 :) 제가 구현하고자 하

h2s0.tistory.com

https://h2s0.tistory.com/98

 

리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 2편

https://h2s0.tistory.com/97 리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 1편안녕하세요 희새입니다!일이 있어서 (?) 리액트로 틱택토 게임을 구현해야 하는데, 헷갈려서 정리하고자 + 지

h2s0.tistory.com

 

리액트로 사용자 vs 컴퓨터 틱택토 만들기 1편, 2편을 보고 오시면 이해가 빠릅니다!

 

이번 3편에서 해볼 것은

1. 컴퓨터 : 사용자 점수 기록하기

2. 게임이 끝난 뒤, Play again 글씨 회색 -> 검정색으로 바꿔주기

입니다!


게임 끝난 뒤 Play Again 색깔 바꾸기

 

Play Again 버튼의 클릭 가능 / 불가능 여부를 시각적으로 보여주고 싶어서 추가하게 된 기능!

const [textColor, setTextColor] = useState('text-gray-300');

먼저 색깔을 상태로 관리해주도록 하겠따! 기본값은 회색으로 설정해주었다

 

// 승,패 구별
const referee = (squares) => {
  const lines = [
    [0, 1, 2], [3, 4, 5], [6, 7, 8],
    [0, 3, 6], [1, 4, 7], [2, 5, 8],
    [0, 4, 8], [2, 4, 6]
  ];
  for ( let line of lines ) {
    // 배열 구조분해
    const [a,b,c] = line;
    // 값이 있는지, 세 칸이 모두 같은 값을 가지고 있는지 확인
    if ( squares[a]
        && squares[a] === squares[b] && squares[a] === squares[c]
    ) {
      setWinner(squares[a]);
      // 추가 된 부분
      setTextColor('text-black');
      return squares[a];
    }
  }
  const isFull = squares.every(square => square !== null);
  if (isFull ) {
    setWinner('Draw');
    // 추가 된 부분
    setTextColor('text-black');
  }
  return null;
}

그리고 referee 함수의 게임이 끝나는 부분이 있는  setTextColor('text-black') 을 추가해주었다.

 

// 다음 게임 버튼
const nextGameClick = () => {
  if (!winner) {
    return;
  }
  setWinner('');
  setSquares(Array(9).fill(null));
  setCurrentPlayer('X');
  // 추가 된 부분
  setTextColor('text-gray-300');
};

다음 게임을 클릭하면 초기값인 회색으로 변경도 해주었다

실험해보면 처음에 내가 이길 때에도 버튼이 검정색으로 변하고, 두번째에 컴퓨터가 이길 때에도 버튼이 검정색으로 잘 변하는 것을 볼 수 있다!

 

 


기존의 score 을 1점씩 높히고 낮추는 것 말고,

사용자 vs 컴퓨터의 점수를 띄워보도록 합시다!

 

// 점수 상태 관리
const [computerScore, setComputerScore] = useState(0);
const [playerScore, setPlayerScore] = useState(0);

 

기존 score 로만 관리해줬던 상태를 컴퓨터 점수, 사용자 점수 두개의 상태로 나눠줍니당

 

// 게임 종료 후 점수 올리고 내리기
useEffect(() => {
  if (winner === 'O') {
    setPlayerScore(prevScore => prevScore + 1);
  } else if (winner === 'X') {
    setComputerScore(prevScore => prevScore + 1);
  } else if (winner === 'Draw') {
    setPlayerScore(prevScore => prevScore);
    setComputerScore(prevScore => prevScore);
  }
}, [winner])

그리고 게임 종료 후 점수를 올리고 내려주었던 useEffect 의 코드를 수정해줍니다

score 을 +1, -1 을 해주는 대신, O 가 이기면 플레이어 점수 +1, X 가 이기면 컴퓨터 점수 +1 을 해줍니다

비겼을 때에는 그냥 기존 점수를 들고가도록 합니다.

 

<h5>{playerScore} : {computerScore}</h5>

그리고 이렇게 return 문 안에서도 {} 를 써서 화면에 보여줍니다

 

 

이렇게 사용자가 이기면 왼쪽 사용자 점수가, 컴퓨터가 이기면 오른쪽 컴퓨터 점수가 올라가는 기능이 완성되었습니다!

무승부를 만들려고 노력해보았는데 잘 안되네요 ㅎ

 


 

이렇게 해서 제가 구현하려고 했던 기능이 다 들어간 틱택토 게임을 완성시켜보았습니다!

 

프로젝트 구조는 이러하구요, 아래는 전체코드입니다.

import { useState, useEffect } from 'react';
import Square from './Square';

function Board() {

  // 게임 보드판 상태 관리
  const [squares, setSquares] = useState(Array(9).fill(null));
  // 현재 차례 상태 관리
  const [currentPlayer, setCurrentPlayer] = useState('X');
  // 승리자 상태 관리
  const [winner, setWinner] = useState('');
  // 점수 상태 관리
  const [computerScore, setComputerScore] = useState(0);
  const [playerScore, setPlayerScore] = useState(0);
  // 버튼 글자 색깔 상태 관리
  const [textColor, setTextColor] = useState('text-gray-300');

  // 컴퓨터가 맨 처음 수를 놓게 함
  useEffect(() => {
    if (referee(squares)) {
      setWinner(referee(squares));
    }
    if (currentPlayer === 'X') {
      const timer = setTimeout(computerMove, 1000);
      return () => clearTimeout(timer);
    }
  }, [currentPlayer, squares]);

  // 사용자 차례
  const handleClick = (i) => {
    if (squares[i] || referee(squares) || currentPlayer !== 'O') {
      return;
    }
    const nextSquares = squares.slice();
    nextSquares[i] = 'O';
    setSquares(nextSquares);
    setCurrentPlayer('X');
  }

  // 컴퓨터 차례
  const computerMove = () => {
    if (referee(squares) || currentPlayer !== 'X') {
      return;
    }
    const emptySquare = squares.map((square,i) => square === null ? i : null).filter(i => i !== null);
    if (emptySquare.length === 0) return;
    const randomSquare = emptySquare[Math.floor(Math.random() * emptySquare.length)];
    const newSquares = squares.slice();
    newSquares[randomSquare] = 'X';
    setSquares(newSquares);
    setCurrentPlayer('O');
  }

  // 승,패 구별
  const referee = (squares) => {
    const lines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];
    for ( let line of lines ) {
      const [a,b,c] = line;
      if ( squares[a]
          && squares[a] === squares[b] && squares[a] === squares[c]
      ) {
        setWinner(squares[a]);
        setTextColor('text-black');
        return squares[a];
      }
    }
    const isFull = squares.every(square => square !== null);
    if (isFull ) {
      setWinner('Draw');
      setTextColor('text-black');
    }
    return null;
  }

  // 게임 종료 후 점수 올리고 내리기
  useEffect(() => {
    if (winner === 'O') {
      setPlayerScore(prevScore => prevScore + 1);
    } else if (winner === 'X') {
      setComputerScore(prevScore => prevScore + 1);
    } else if (winner === 'Draw') {
      setPlayerScore(prevScore => prevScore);
      setComputerScore(prevScore => prevScore);
    }
  }, [winner])

  // 다음 게임 버튼
  const nextGameClick = () => {
    if (!winner) {
      return;
    }
    setWinner('');
    setSquares(Array(9).fill(null));
    setCurrentPlayer('X');
    setTextColor('text-gray-300');
  };

  return(
    <div className='flex flex-col gap-2 items-center w-72 bg-orange-200 rounded-3xl p-5'>
      <h5>{playerScore} : {computerScore}</h5>
      {playerScore === 9999 && <h2>Congratulations!</h2>}
      <h5>current player : {currentPlayer}</h5>
      <div>
        <div className='board-row'> {[0,1,2].map( i => <Square key={i} value={squares[i]} onClick={() => handleClick(i)} />)} </div>
        <div className='board-row'> {[3,4,5].map( i => <Square key={i} value={squares[i]} onClick={() => handleClick(i)} />)} </div>
        <div className='board-row'> {[6,7,8].map( i => <Square key={i} value={squares[i]} onClick={() => handleClick(i)} />)} </div>
      </div>
      <h5>winner : {winner}</h5>
      <button className={`bg-white px-4 py-2 rounded-lg ${textColor}`} onClick={nextGameClick}>Play Again</button>
    </div>
  )
}

export default Board
function Square({ value, onClick }) {
  
  return(
    <button className="square" onClick={onClick}>
      {value}
    </button>
  )
}

export default Square

 

혹시 필요하실까봐 전체 코드 올려놓습니다

이 외에 다른 파일 코드가 필요하시거나 질문이 있으시다면 편하게 댓글 달아주세요!

 

감사합니다 (--)(__)