안녕하세요 희새입니다!
일이 있어서 (?) 리액트로 틱택토 게임을 구현해야 하는데, 헷갈려서 정리하고자 + 지피티에게 물어본 것들을 더 잘 이해하기 위해서 글을 씁니다 :)
제가 구현하고자 하는 틱택토 게임은 기존 리액트 공식문서 틱택토 게임에서
1. 컴퓨터와 대결
2. 승, 패에 따른 점수 기록
3. 게임 리셋
기능이 추가 된 틱택토를 구현하려고 한다.
이 기능을 위해 내가 해야하는 것을 정리해보자면
(한 칸을 square 라고 했을 때) square 클릭함수,
컴퓨터가 수 놓는 함수,
승패 구별 함수,
게임 리셋 함수,
score 업데이트
이렇게 인 것 같다.
게임 보드판, 현재 차례, 점수를 상태로 관리해 줄 것이다.
현재 차례는 사용자가 O, 컴퓨터가 X
square 클릭 함수인 handleClick 함수이다.
// 전체 코드
const handleClick = (i) => {
if (squares[i] || referee(squares) || currentPlayer !== 'O') {
return;
}
const nextSquares = squares.slice();
nextSquares[i] = 'O';
setSquares(nextSquares);
setCurrentPlayer('X');
}
const handleClick = (i) => {
if (squares[i] || referee(squares) || currentPlayer !== 'O') {
return;
}
}
- squares[i] : 클릭한 square 의 인덱스 값
- referee(squares) : referee 는 심판이라는 뜻으로 승패결정 함수이다.
- currentPlayer !== 'O' : 현재 플레이어가 사용자 = O 가 아닐 때
즉, 1. 클릭한 square 의 인덱스 값이 null 이 아닐 경우,
2. 승패 결정 함수가 값을 반환하는 경우 (=게임이 끝났을 때)
3. 사용자 차례가 아닐 경우에는
return 을 반환하여 함수를 종료시켜준다.
<< 현 시점에서 referee 함수를 작성하지 않았으니 2는 빼야 에러없이 실행된다! >>
const handleClick = (i) => {
if (squares[i] || referee(squares)) {
return;
}
const nextSquares = squares.slice();
}
- slice() : 배열의 일부 혹은 전체를 복사하여 새 배열로 반환, 복사본을 수정하여 결과를 반영할 때 사용됨
리액트에서는 상태 직접 변경을 피하기 때문에 이러한 방법을 사용해준다.
const handleClick = (i) => {
if (squares[i] || referee(squares)) {
return;
}
const nextSquares = squares.slice();
nextSquares[i] = 'O';
}
}
O 를 할당
const handleClick = (i) => {
if (squares[i] || referee(squares)) {
return;
}
const nextSquares = squares.slice();
nextSquares[i] = 'O';
setSquares(nextSquares);
setCurrentPlayer('X');
}
SetSquares(nextSquares)
- 업데이트 된 squares 배열을 상태로 설정
SetCurrentPlayer(true);
- 차례 변경
지금의 경우 player 다음은 항상 computer 이기 때문에 항상 true = X 가 다음 플레이어로 할당되게 해줌
만약 사용자가 두 플레이어를 다 하고 싶다면 !currentPlayer 로 설정해주면 됨
Computer 가 패 놓기 함수인 computerMove 함수이다.
// 컴퓨터 차례
const computerMove = () => {
// 빈 칸 찾기
const emptySquare = squares.map((square,i) => square === null ? i : null).filter(i => i !== null);
// 무작위로 한 칸 선택
const randomSquare = emptySquare[Math.floor(Math.random() * emptySquare.length)];
// 선택된 위치에 말 놓기 (X)
const newSquares = squares.slice();
newSquares[randomSquare] = 'X';
// 보드 상태 업데이트
setSquares(newSquares);
// 차례 변경
setCurrentPlayer('O');
}
알고리즘을 사용하지 않고 그냥 무작위 빈칸에 패를 놓게 한다.
빈 칸 찾기 -> 무작위로 한 칸 선택 -> 선택한 위치에 X 말 놓기 -> 보드 상태 업데이트 -> 차례 변경 순으로 작동한다.
useEffect(() => {
if (currentPlayer === 'X') {
const timer = setTimeout(computerMove, 1000);
return () => clearTimeout(timer);
}
}, [currentPlayer, squares]);
useeffect 를 사용하여 currentPlayer 가 X = 컴퓨터 면 1초 뒤에 computerMove 함수를 실행하게 해주었다.
return 문 작성
return(
<>
<div className='text-center'>
<h3>Score : {score}</h3>
<h2>current player : {currentPlayer}</h2>
</div>
<section>
<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>
</section>
</>
)
score 는 나중에 점수를 보여주기 위해서 작성해두었다.
div 세 개로 칸을 나눈 이유는 그냥 3 * 3 편하게 하기 위해서라고 한다. ( 지피티가 말해줌 )

여기까지 하면이렇게..! 나랑 컴퓨터랑 번갈아가면서 패를 놓는 틱택토 게임이 되었다.
승/패 구별 함수가 없어서 컴퓨터가 한 줄을 완성시켰는데도 계속 게임이 진행된다.
2편에서는 승 / 패를 구별하고 그에 따라 score 값을 내리거나 올리고, 게임이 종료된 후 리셋까지 완성시켜보도록 하겠습니당
글 읽어주셔서 감사합니다 (--) (__)

만드는 과정 중에 생긴.. 컴퓨터만 패를 쌈@뽕하게 놓는 그런.. 코딩 :) 폭주기관차 컴퓨터 푸하하
'React' 카테고리의 다른 글
리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 3편, 전체코드공유 (0) | 2024.05.10 |
---|---|
리액트로 사용자 vs 컴퓨터 Tic Tac Toe 틱택토 게임 구현하기 2편 (0) | 2024.05.09 |
포켓베이스 pocketbase 설치하는 방법 (0) | 2023.10.28 |
React 컴포넌트 (0) | 2023.08.20 |
useEffect 와 useEffect 를 사용해 서버에 데이터 가져오기 요청하기 (0) | 2023.08.18 |