1
Fork 0

Add more logic

This commit is contained in:
Hadeed 2024-06-02 21:52:05 +04:00
parent 0918fe65fa
commit 5ea9e93239
8 changed files with 106 additions and 32 deletions

View file

@ -4,7 +4,7 @@ name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ['main']
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@ -17,7 +17,7 @@ permissions:
# Allow one concurrent deployment
concurrency:
group: 'pages'
group: "pages"
cancel-in-progress: true
jobs:
@ -34,7 +34,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache: "npm"
- name: Install dependencies
run: |
rm package-lock.json
@ -47,7 +47,7 @@ jobs:
uses: actions/upload-pages-artifact@v3
with:
# Upload dist folder
path: './dist'
path: "./dist"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View file

@ -1,6 +1,6 @@
# Frontend Mentor - Tic Tac Toe solution
This is a solution to the [Tic Tac Toe challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/tic-tac-toe-game-Re7ZF_E2v). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
This is a solution to the [Tic Tac Toe challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/tic-tac-toe-game-Re7ZF_E2v). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
## Table of contents
@ -36,7 +36,7 @@ Users should be able to:
Add a screenshot of your solution. The easiest way to do this is to use Firefox to view your project, right-click the page and select "Take a Screenshot". You can choose either a full-height screenshot or a cropped one based on how long the page is. If it's very long, it might be best to crop it.
Alternatively, you can use a tool like [FireShot](https://getfireshot.com/) to take the screenshot. FireShot has a free option, so you don't need to purchase it.
Alternatively, you can use a tool like [FireShot](https://getfireshot.com/) to take the screenshot. FireShot has a free option, so you don't need to purchase it.
Then crop/optimize/edit your image however you like, add it to your project, and update the file path in the image above.
@ -71,15 +71,17 @@ To see how you can add code snippets, see below:
```html
<h1>Some HTML code I'm proud of</h1>
```
```css
.proud-of-this-css {
color: papayawhip;
}
```
```js
const proudOfThisFunc = () => {
console.log('🎉')
}
console.log("🎉");
};
```
If you want more help with writing markdown, we'd recommend checking out [The Markdown Guide](https://www.markdownguide.org/) to learn more.

View file

@ -83,12 +83,12 @@ Remember, if you're looking for feedback on your solution, be sure to ask questi
There are multiple places you can share your solution:
1. Share your solution page in the **#finished-projects** channel of the [community](https://www.frontendmentor.io/community).
1. Share your solution page in the **#finished-projects** channel of the [community](https://www.frontendmentor.io/community).
2. Tweet [@frontendmentor](https://twitter.com/frontendmentor) and mention **@frontendmentor**, including the repo and live URLs in the tweet. We'd love to take a look at what you've built and help share it around.
3. Share your solution on other social channels like LinkedIn.
4. Blog about your experience building your project. Writing about your workflow, technical choices, and talking through your code is a brilliant way to reinforce what you've learned. Great platforms to write on are [dev.to](https://dev.to/), [Hashnode](https://hashnode.com/), and [CodeNewbie](https://community.codenewbie.org/).
We provide templates to help you share your solution once you've submitted it on the platform. Please do edit them and include specific questions when you're looking for feedback.
We provide templates to help you share your solution once you've submitted it on the platform. Please do edit them and include specific questions when you're looking for feedback.
The more specific you are with your questions the more likely it is that another member of the community will give you feedback.

View file

@ -1,12 +1,15 @@
import MainMenu from "./mainMenu";
import Game from "./game";
import Modal from "./modal";
import { useStore } from "./store";
export default () => {
const isGameRunning = useStore((state) => state.isGameRunning);
const gameKey = useStore((state) => state.gameKey);
const players = useStore((state) => state.players);
return (
<main className="flex min-h-screen items-center justify-center bg-navy-700">
{/* <MainMenu /> */}
<Game />
{isGameRunning ? <Game players={players} key={gameKey} /> : <MainMenu />}
</main>
);
};

View file

@ -8,9 +8,39 @@ import OvalOutline from "../assets/icon-o-outline.svg?react";
import logo from "../assets/logo.svg";
import restart from "../assets/icon-restart.svg";
import { restartGame } from "./store";
import Modal from "./modal";
export default () => {
const getWinner = (grid) => {
const combos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
const wins = (combo) => {
const symbols = combo.map((i) => grid[i]);
return symbols.every((symbol) => symbol == symbols[0] && symbol != "");
};
const winningCombo = combos.find(wins);
if (winningCombo) {
return grid[winningCombo[0]];
}
return "";
};
export default ({ players: _players }) => {
const [score, updateScore] = useImmer({ X: 0, O: 0, ties: 0 });
const [grid, updateGrid] = useImmer(Array(9).fill(""));
const [modal, setModal] = useState(false);
@ -18,6 +48,15 @@ export default () => {
const TurnOutline = turn == "X" ? CrossOutline : OvalOutline;
const TurnIndicator = turn == "X" ? Cross : Oval;
const winner = getWinner(grid);
if (winner) {
updateScore((score) => {
score[winner]++;
});
updateGrid(() => Array(9).fill(""));
}
const renderSymbol = (symbol) => {
if (symbol == "") {
let colorClass = turn == "X" ? "text-blue-700" : "text-yellow-700";
@ -77,16 +116,16 @@ export default () => {
<footer className="flex items-center justify-between text-navy-700">
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-blue-700">
<p className="text-base uppercase">X (you)</p>
<p className="text-h-m uppercase">14</p>
<p className="text-base uppercase">X ({_players.X})</p>
<p className="text-h-m uppercase">{score.X}</p>
</div>
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-silver-700">
<p className="text-base uppercase">Ties</p>
<p className="text-h-m uppercase">32</p>
<p className="text-h-m uppercase">{score.ties}</p>
</div>
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-yellow-700">
<p className="text-base uppercase">O (cpu)</p>
<p className="text-h-m uppercase">11</p>
<p className="text-base uppercase">O ({_players.O})</p>
<p className="text-h-m uppercase">{score.O}</p>
</div>
</footer>
</div>
@ -106,9 +145,7 @@ export default () => {
<p className="text-h-xs uppercase">No, cancel</p>
</button>
<button
onClick={() => {
updateGrid(() => Array(9).fill(""));
}}
onClick={restartGame}
className="rounded-xl bg-yellow-700 px-5 py-4 inner-shadow-1-yellow-900 hover:bg-yellow-400"
>
<p className="text-h-xs uppercase">Yes, restart</p>

View file

@ -1,12 +1,24 @@
import * as RadioGroup from "@radix-ui/react-radio-group";
import { useState } from "react";
import logo from "../assets/logo.svg";
import Cross from "../assets/icon-x.svg?react";
import Oval from "../assets/icon-o.svg?react";
import { useStore, setPlayerOneSymbol } from "./store";
import { setIsGameRunning, updatePlayers } from "./store";
export default () => {
const playerOneSymbol = useStore((state) => state.playerOneSymbol);
const [playerOneSymbol, setPlayerOneSymbol] = useState("X");
const playerTwoSymbol = playerOneSymbol == "X" ? "O" : "X";
const startGame = (playerTwo) => {
updatePlayers({
[playerOneSymbol]: "P1",
[playerTwoSymbol]: playerTwo,
});
setIsGameRunning(true);
};
return (
<div className="m-6 flex w-full max-w-lg flex-col items-center space-y-10">
@ -22,8 +34,8 @@ export default () => {
loop={false}
>
{[
["cross", Cross],
["oval", Oval],
["X", Cross],
["O", Oval],
].map(([value, Symbol]) => (
<RadioGroup.Item
className="group h-full w-1/2 rounded-xl hover:bg-silver-700/5 data-[state='checked']:bg-silver-700"
@ -43,10 +55,16 @@ export default () => {
</p>
</div>
<div className="w-full space-y-5">
<button className="flex w-full items-center justify-center rounded-2xl bg-yellow-700 p-4 inner-shadow-2-yellow-900 hover:bg-yellow-400">
<button
onClick={() => startGame("CPU")}
className="flex w-full items-center justify-center rounded-2xl bg-yellow-700 p-4 inner-shadow-2-yellow-900 hover:bg-yellow-400"
>
<p className="text-h-s uppercase text-navy-700">New game (vs cpu)</p>
</button>
<button className="flex w-full items-center justify-center rounded-2xl bg-blue-700 p-4 inner-shadow-2-blue-900 hover:bg-blue-400">
<button
onClick={() => startGame("P2")}
className="flex w-full items-center justify-center rounded-2xl bg-blue-700 p-4 inner-shadow-2-blue-900 hover:bg-blue-400"
>
<p className="text-h-s uppercase text-navy-700">
New game (vs player)
</p>

View file

@ -17,7 +17,7 @@ export default ({ isOpen, children, className, onClose }) => {
return (
<dialog
className="backdrop:bg-black/50 h-64 w-screen max-w-[100vw] bg-navy-700"
className="h-64 w-screen max-w-[100vw] bg-navy-700 backdrop:bg-black/50"
ref={ref}
>
<div className="flex h-full w-full items-center justify-center">

View file

@ -1,8 +1,22 @@
import { produce } from "immer";
import { create } from "zustand";
export const useStore = create((set) => ({
playerOneSymbol: "cross",
isGameRunning: false,
gameKey: 0,
players: {
X: "",
O: "",
},
}));
export const setPlayerOneSymbol = (playerOneSymbol) =>
useStore.setState(() => ({ playerOneSymbol }));
export const setIsGameRunning = (isGameRunning) =>
useStore.setState(() => ({ isGameRunning }));
export const updatePlayers = (players) =>
useStore.setState(() => ({ players }));
export const restartGame = () =>
useStore.setState(({ gameKey }) => ({
gameKey: 1 - gameKey,
}));