This commit is contained in:
Kagura 2024-11-17 13:04:47 +08:00
commit 93efc42517
15 changed files with 9959 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules

13
build/asset-manifest.json Normal file
View file

@ -0,0 +1,13 @@
{
"files": {
"main.css": "/static/css/main.f4938759.css",
"main.js": "/static/js/main.ee416b6e.js",
"index.html": "/index.html",
"main.f4938759.css.map": "/static/css/main.f4938759.css.map",
"main.ee416b6e.js.map": "/static/js/main.ee416b6e.js.map"
},
"entrypoints": [
"static/css/main.f4938759.css",
"static/js/main.ee416b6e.js"
]
}

1
build/index.html Normal file
View file

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Document</title><script defer="defer" src="/static/js/main.ee416b6e.js"></script><link href="/static/css/main.f4938759.css" rel="stylesheet"></head><body><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version":3,"file":"static/css/main.f4938759.css","mappings":"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","sources":["styles.css"],"sourcesContent":["@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n* {\n box-sizing: border-box;\n}\n\nbody {\n font-family: sans-serif;\n margin: 20px;\n padding: 0;\n}\n\nh1 {\n margin-top: 0;\n font-size: 22px;\n}\n\nh2 {\n margin-top: 0;\n font-size: 20px;\n}\n\nh3 {\n margin-top: 0;\n font-size: 18px;\n}\n\nh4 {\n margin-top: 0;\n font-size: 16px;\n}\n\nh5 {\n margin-top: 0;\n font-size: 14px;\n}\n\nh6 {\n margin-top: 0;\n font-size: 12px;\n}\n\ncode {\n font-size: 1.2em;\n}\n\nul {\n padding-inline-start: 20px;\n}\n\n* {\n box-sizing: border-box;\n}\n\nbody {\n font-family: sans-serif;\n margin: 20px;\n padding: 0;\n}\n\n.square {\n border: 1px solid #999;\n float: left;\n font-size: 24px;\n font-weight: bold;\n line-height: 34px;\n height: 34px;\n margin-right: -1px;\n margin-top: -1px;\n padding: 0;\n text-align: center;\n width: 34px;\n}\n\n.board-row:after {\n clear: both;\n content: '';\n display: table;\n}\n\n.status {\n margin-bottom: 10px;\n}\n.game {\n display: flex;\n flex-direction: row;\n}\n\n.game-info {\n margin-left: 20px;\n}\n\n.arrow {\n font-size: '20px';\n padding: '10px';\n cursor: 'pointer';\n}"],"names":[],"sourceRoot":""}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,39 @@
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

31
package.json Normal file
View file

@ -0,0 +1,31 @@
{
"name": "react.dev",
"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": {
"tailwindcss": "^3.4.15"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

11
public/index.html Normal file
View 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>

168
src/App.js Normal file
View file

@ -0,0 +1,168 @@
import { useState } from "react";
function Square({ row, column, value, clickHandler }) {
let buttonClass = ""
switch (value){
case "X":
buttonClass = "square bg-amber-200 "
break;
case "O":
buttonClass = "square bg-violet-300"
break;
default:
buttonClass = "square bg-white"
break;
}
return (
<button
className={buttonClass}
onClick={() => clickHandler(row, column)}
>
{value}
</button>
);
}
function SquareRow({ rowId, rows, clickHandler }) {
const items =
rows.map((ele, index) =>
<Square
key={index}
row={rowId}
column={index}
value={ele}
clickHandler={clickHandler}
/>
);
return <>{items}</>;
}
export default function Map({ mapSize }) {
// Config Here
const ticTacToeMaxMapSize = 6
if (mapSize == undefined) {
mapSize = 3;
}
const [map, setMap] = useState(
Array(mapSize).fill(null)
.map(() => Array(mapSize).fill(null))
);
const [currentMove, setCurrentMove] = useState(0);
const xIsNext = currentMove % 2 == 0;
const [winner, setWinner] = useState(null);
const [history, setHistory] = useState([Array(mapSize).fill(null)
.map(() => Array(mapSize).fill(null))])
function updateMap(nextMap) {
setHistory([...history, nextMap])
setMap(nextMap)
}
function handleClick(row, column) {
if (map[row][column] != null || winner != null) {
return
}
const nextMap = map.map(row => [...row]);
if (xIsNext) {
nextMap[row][column] = 'X'
} else {
nextMap[row][column] = 'O'
}
updateMap(nextMap)
setCurrentMove(currentMove + 1)
if (calculateWinner(row, column,nextMap)) {
setWinner(nextMap[row][column])
}
}
function calculateWinner(row, column,map) {
const who = map[row][column]; // 当前玩家标记
const winNeed = mapSize > ticTacToeMaxMapSize ? 5 : mapSize; // 棋盘大小(>5五子棋)
// 横向检查
let horizontal = 1;
for (let hLeft = column - 1; hLeft >= 0; hLeft--) {
if (map[row][hLeft] !== who) break;
horizontal++;
}
for (let hRight = column + 1; hRight < mapSize; hRight++) {
if (map[row][hRight] !== who) break;
horizontal++;
}
if (horizontal >= winNeed) return true;
// 纵向检查
let vertical = 1;
for (let vUp = row - 1; vUp >= 0; vUp--) {
if (map[vUp][column] !== who) break;
vertical++;
}
for (let vDown = row + 1; vDown < mapSize; vDown++) {
if (map[vDown][column] !== who) break;
vertical++;
}
if (vertical >= winNeed) return true;
// 主对角线 (左上到右下)
let mainDiagonal = 1;
for (let dUp = 1; row - dUp >= 0 && column - dUp >= 0; dUp++) {
if (map[row - dUp][column - dUp] !== who) break;
mainDiagonal++;
}
for (let dDown = 1; row + dDown < mapSize && column + dDown < mapSize; dDown++) {
if (map[row + dDown][column + dDown] !== who) break;
mainDiagonal++;
}
if (mainDiagonal >= winNeed) return true;
// 副对角线 (右上到左下)
let antiDiagonal = 1;
for (let dUp = 1; row - dUp >= 0 && column + dUp < mapSize; dUp++) {
if (map[row - dUp][column + dUp] !== who) break;
antiDiagonal++;
}
for (let dDown = 1; row + dDown < mapSize && column - dDown >= 0; dDown++) {
if (map[row + dDown][column - dDown] !== who) break;
antiDiagonal++;
}
if (antiDiagonal >= winNeed) return true;
// 如果没有满足条件,返回 false
return false;
}
const mapEle = map.map((ele, index) => (
<div key={`row-${index}`} className="board-row"> {/* Unique key for each row */}
<SquareRow rowId={index} rows={ele} clickHandler={handleClick} />
</div>
));
function handleLeftClick() {
if (currentMove > 0) {
setMap(history[currentMove - 1]);
setCurrentMove(currentMove - 1);
setHistory(history.slice(0, -1));
setWinner(null);
}
}
return (
<div className="rounded-xl bg-sky-50 shadow-xl table items-center space-y-2 container mx-auto content-center">
<p className="font-sans text-2xl text-center ">{mapSize > ticTacToeMaxMapSize ? "五子棋" : "井字棋"}模式</p>
<p className="text-xl text-center ">现在是 {xIsNext ? "X" : "O"} 移动</p>
<div className="flex flex-row justify-center items-center">
<div className="board" >
{mapEle}
</div>
</div>
<div className="flex flex-row justify-center items-center">
<button className="bg-cyan-50 shadow-md rounded-md text-lg" onClick={handleLeftClick}>
&#8592;回到上一个
</button>
</div>
<p className="text-xl text-center">{winner != null ? winner + "赢了" : ""} </p>
</div>
);
}

12
src/index.js Normal file
View 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 mapSize={8}/>
</StrictMode>
);

99
src/styles.css Normal file
View file

@ -0,0 +1,99 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
margin: 20px;
padding: 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;
margin: 20px;
padding: 0;
}
.square {
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;
}
.arrow {
font-size: '20px';
padding: '10px';
cursor: 'pointer';
}

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

9564
yarn.lock Normal file

File diff suppressed because it is too large Load diff