init basic tictactoe game
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
.env
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# React Router
|
||||||
|
.react-router
|
||||||
|
build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
.idea
|
||||||
41
README.md
Normal file
41
README.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Material UI - React Router example in TypeScript
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
Download the example [or clone the repo](https://github.com/mui/material-ui):
|
||||||
|
|
||||||
|
<!-- #target-branch-reference -->
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-react-router-ts
|
||||||
|
cd material-ui-react-router-ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Install it and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
<!-- #target-branch-reference -->
|
||||||
|
|
||||||
|
[](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
||||||
|
|
||||||
|
[](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
||||||
|
|
||||||
|
## The idea behind the example
|
||||||
|
|
||||||
|
<!-- #host-reference -->
|
||||||
|
|
||||||
|
This example demonstrates how you can use Material UI with [React Router](https://reactrouter.com/) in [TypeScript](https://github.com/Microsoft/TypeScript).
|
||||||
|
It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI.
|
||||||
|
|
||||||
|
## What's next?
|
||||||
|
|
||||||
|
<!-- #host-reference -->
|
||||||
|
|
||||||
|
You now have a working example project.
|
||||||
|
You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section.
|
||||||
18
index.html
Normal file
18
index.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
||||||
|
/>
|
||||||
|
<title>Blog - Aetoskia</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3974
package-lock.json
generated
Normal file
3974
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "tictactoe-vite",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"serve": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "latest",
|
||||||
|
"@emotion/styled": "latest",
|
||||||
|
"@mui/material": "latest",
|
||||||
|
"@mui/icons-material": "latest",
|
||||||
|
"react": "latest",
|
||||||
|
"react-dom": "latest",
|
||||||
|
"react-markdown": "latest",
|
||||||
|
"markdown-to-jsx": "latest",
|
||||||
|
"remark-gfm": "latest",
|
||||||
|
"marked": "latest",
|
||||||
|
"axios": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "latest",
|
||||||
|
"vite": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main.jsx
Normal file
14
src/main.jsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import TicTacToe from './tictactoe/TicTacToe';
|
||||||
|
// import { TicTacToeProvider } from './tictactoe/providers/TicTacToeProvider';
|
||||||
|
import "./tictactoe/styles.css";
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
const root = createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
// <TicTacToeProvider>
|
||||||
|
<TicTacToe />
|
||||||
|
// </TicTacToeProvider>,
|
||||||
|
);
|
||||||
11
src/tictactoe/Board.tsx
Normal file
11
src/tictactoe/Board.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import Square from "./Square";
|
||||||
|
|
||||||
|
export default function Board({ squares, onClick }) {
|
||||||
|
return (
|
||||||
|
<div className="board">
|
||||||
|
{squares.map((value, i) => (
|
||||||
|
<Square key={i} value={value} onClick={() => onClick(i)} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
src/tictactoe/Square.tsx
Normal file
7
src/tictactoe/Square.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default function Square({ value, onClick }) {
|
||||||
|
return (
|
||||||
|
<button className="square" onClick={onClick}>
|
||||||
|
{value}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
52
src/tictactoe/TicTacToe.tsx
Normal file
52
src/tictactoe/TicTacToe.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import Board from "./Board";
|
||||||
|
|
||||||
|
export default function TicTacToe() {
|
||||||
|
const [board, setBoard] = useState(Array(9).fill(null));
|
||||||
|
const [xIsNext, setXIsNext] = useState(true);
|
||||||
|
|
||||||
|
const winner = calculateWinner(board);
|
||||||
|
|
||||||
|
function handleSquareClick(i: number) {
|
||||||
|
if (board[i] || winner) return;
|
||||||
|
|
||||||
|
const newBoard = board.slice();
|
||||||
|
newBoard[i] = xIsNext ? "X" : "O";
|
||||||
|
|
||||||
|
setBoard(newBoard);
|
||||||
|
setXIsNext(!xIsNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
setBoard(Array(9).fill(null));
|
||||||
|
setXIsNext(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = winner
|
||||||
|
? `Winner: ${winner}`
|
||||||
|
: `Next player: ${xIsNext ? "X" : "O"}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="game-container">
|
||||||
|
<h1>Tic Tac Toe</h1>
|
||||||
|
<div className="status">{status}</div>
|
||||||
|
<Board squares={board} onClick={handleSquareClick} />
|
||||||
|
<button className="reset-btn" onClick={reset}>Reset</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- winner logic ---
|
||||||
|
function calculateWinner(sq: any[]) {
|
||||||
|
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 [a,b,c] of lines) {
|
||||||
|
if (sq[a] && sq[a] === sq[b] && sq[a] === sq[c]) {
|
||||||
|
return sq[a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
53
src/tictactoe/styles.css
Normal file
53
src/tictactoe/styles.css
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #f4f4f4;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 50px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 6px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square {
|
||||||
|
background: white;
|
||||||
|
border: 2px solid #333;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-btn:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
9
src/vite-env.d.ts
vendored
Normal file
9
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_WS_BASE_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// 👇 This line tells TypeScript how to handle JSX
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user