git init
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/.idea
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
17103
package-lock.json
generated
Normal file
17103
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "react.tictactoe",
|
||||
"version": "0.0.0",
|
||||
"main": "/src/index.js",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-scripts": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
11835
pnpm-lock.yaml
generated
Normal file
11835
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
public/index.html
Normal file
11
public/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
162
src/App.js
Normal file
162
src/App.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import {useMemo, useState} from "react";
|
||||
|
||||
const SQUARE_SIZE = 3;
|
||||
|
||||
function Square({ id, value, onSquareClick, winner, points }) {
|
||||
return <button className="square" onClick={onSquareClick} style={{
|
||||
color: winner && points?.includes(id) ? 'green' : 'black',
|
||||
}}>{value}</button>
|
||||
}
|
||||
|
||||
function Board({ xIsNext, squares, onPlay }) {
|
||||
const { winner, points } = calculateWinner(squares);
|
||||
|
||||
function handleClick(i) {
|
||||
if (squares[i] || winner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextSquares = squares.slice();
|
||||
|
||||
if (xIsNext) {
|
||||
nextSquares[i] = 'X';
|
||||
} else {
|
||||
nextSquares[i] = 'O';
|
||||
}
|
||||
|
||||
onPlay(nextSquares);
|
||||
}
|
||||
|
||||
let status;
|
||||
|
||||
const rendering = useMemo(() => {
|
||||
const result = [];
|
||||
|
||||
for (let i = 0; i < SQUARE_SIZE; i++) {
|
||||
result.push(
|
||||
<div key={i} className="board-row">
|
||||
{Array.from({length: SQUARE_SIZE}).map((_, j) => (
|
||||
<Square key={i * 3 + j}
|
||||
id={i * 3 + j}
|
||||
onSquareClick={() => handleClick(i * 3 + j)}
|
||||
value={squares[i * 3 + j]}
|
||||
winner={winner}
|
||||
points={points}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [squares, winner, points]);
|
||||
|
||||
if (winner) {
|
||||
status = '승자: ' + winner;
|
||||
} else {
|
||||
status = '다음 플레이어: ' + (xIsNext ? 'X' : 'O');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="status">{status}</div>
|
||||
{ rendering }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Game() {
|
||||
const [history, setHistory] = useState([Array(9).fill(null)]);
|
||||
const [currentMove, setCurrentMove] = useState(0);
|
||||
const currentSquares = history[currentMove];
|
||||
const [isAscending, setIsAscending] = useState(true);
|
||||
|
||||
const xIsNext = currentMove % 2 === 0;
|
||||
|
||||
function handlePlay(nextSquares) {
|
||||
const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
|
||||
setHistory(nextHistory);
|
||||
setCurrentMove(nextHistory.length - 1);
|
||||
}
|
||||
|
||||
function jumpTo(nextMove) {
|
||||
setCurrentMove(nextMove);
|
||||
}
|
||||
|
||||
const moves = history.map((squares, move) => {
|
||||
console.log('squares', squares);
|
||||
|
||||
let description;
|
||||
|
||||
const calculateUserPick = (move) => {
|
||||
let answer = null;
|
||||
const nowSquares = history[move];
|
||||
const prevSquares = history[move - 1];
|
||||
|
||||
for (let i = 0; i < squares.length; i++) {
|
||||
if (nowSquares[i] !== prevSquares[i]) {
|
||||
answer = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const row = Math.floor(answer / SQUARE_SIZE);
|
||||
const col = answer % SQUARE_SIZE;
|
||||
return [row + 1, col + 1];
|
||||
}
|
||||
|
||||
if (move > 0) {
|
||||
const [row, col] = calculateUserPick(move);
|
||||
description = '# ' + move + '로 이동하기 (' + row + ', ' + col + ')';
|
||||
} else {
|
||||
description = '시작지점으로 이동하기';
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={move}>
|
||||
<button onClick={() => jumpTo(move)}>{description}</button>
|
||||
</li>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="game">
|
||||
<div className="game-board">
|
||||
<div>당신은 {currentMove + 1}번째 순서에 있습니다...</div>
|
||||
<Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
|
||||
</div>
|
||||
<div className="game-info">
|
||||
<ol><button onClick={() => setIsAscending(!isAscending)}>순서변경</button></ol>
|
||||
<ol>{isAscending ? moves : moves.reverse() }</ol>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
function calculateWinner(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 i = 0; i < lines.length; i++) {
|
||||
const [a, b, c] = lines[i];
|
||||
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
|
||||
return {
|
||||
winner: squares[a],
|
||||
points: lines[i],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
winner: null,
|
||||
points: lines,
|
||||
};
|
||||
}
|
||||
12
src/index.js
Normal file
12
src/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React, { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./styles.css";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
const root = createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
93
src/styles.css
Normal file
93
src/styles.css
Normal file
@@ -0,0 +1,93 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.square {
|
||||
background: #fff;
|
||||
border: 1px solid #999;
|
||||
float: left;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 34px;
|
||||
height: 34px;
|
||||
margin-right: -1px;
|
||||
margin-top: -1px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 34px;
|
||||
}
|
||||
|
||||
.board-row:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.game {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.game-info {
|
||||
margin-left: 20px;
|
||||
}
|
||||
Reference in New Issue
Block a user