Skip to content

Commit

Permalink
feat: support game wins (#16)
Browse files Browse the repository at this point in the history
* feat: support game wins

* chore: clean up
  • Loading branch information
mateuszsokola authored Sep 1, 2024
1 parent d2ae761 commit e586fd6
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 3 deletions.
4 changes: 3 additions & 1 deletion components/board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import styles from "@/styles/board.module.css";
import Tile from "./tile";
import { GameContext } from "@/context/game-context";
import MobileSwiper, { SwipeInput } from "./mobile-swiper";
import Splash from "./splash";

export default function Board() {
const { getTiles, moveTiles, startGame } = useContext(GameContext);
const { getTiles, moveTiles, startGame, status } = useContext(GameContext);
const initialized = useRef(false);

const handleKeyDown = useCallback(
Expand Down Expand Up @@ -86,6 +87,7 @@ export default function Board() {
return (
<MobileSwiper onSwipe={handleSwipe}>
<div className={styles.board}>
{status === "won" && <Splash heading="You won!" />}
<div className={styles.tiles}>{renderTiles()}</div>
<div className={styles.grid}>{renderGrid()}</div>
</div>
Expand Down
18 changes: 18 additions & 0 deletions components/splash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { GameContext } from "@/context/game-context";
import styles from "@/styles/splash.module.css";
import { useContext } from "react";

export default function Splash({ heading = "You won!" }) {
const { startGame } = useContext(GameContext);

return (
<div className={`${styles.splash} ${styles.win}`}>
<div>
<h1>{heading}</h1>
<button className={styles.button} onClick={startGame}>
Play again
</button>
</div>
</div>
);
}
5 changes: 5 additions & 0 deletions constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ export const tileCountPerDimension = 4;
export const mergeAnimationDuration = 100; // ms

export const moveAnimationDuration = 200; // ms

/**
* Game setup
*/
export const gameWinTileValue = 2048;
20 changes: 19 additions & 1 deletion context/game-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import {
useReducer,
} from "react";
import { isNil, throttle } from "lodash";
import { mergeAnimationDuration, tileCountPerDimension } from "@/constants";
import {
gameWinTileValue,
mergeAnimationDuration,
tileCountPerDimension,
} from "@/constants";
import { Tile } from "@/models/tile";
import gameReducer, { initialState } from "@/reducers/game-reducer";

type MoveDirection = "move_up" | "move_down" | "move_left" | "move_right";

export const GameContext = createContext({
score: 0,
status: "ongoing",
moveTiles: (_: MoveDirection) => {},
getTiles: () => [] as Tile[],
startGame: () => {},
Expand Down Expand Up @@ -61,15 +66,27 @@ export default function GameProvider({ children }: PropsWithChildren) {
);

const startGame = () => {
dispatch({ type: "reset_game" });
dispatch({ type: "create_tile", tile: { position: [0, 1], value: 2 } });
dispatch({ type: "create_tile", tile: { position: [0, 2], value: 2 } });
};

const checkGameState = () => {
const isWon =
Object.values(gameState.tiles).filter((t) => t.value === gameWinTileValue)
.length > 0;

if (isWon) {
dispatch({ type: "update_status", status: "won" });
}
};

useEffect(() => {
if (gameState.hasChanged) {
setTimeout(() => {
dispatch({ type: "clean_up" });
appendRandomTile();
checkGameState();
}, mergeAnimationDuration);
}
}, [gameState.hasChanged]);
Expand All @@ -78,6 +95,7 @@ export default function GameProvider({ children }: PropsWithChildren) {
<GameContext.Provider
value={{
score: gameState.score,
status: gameState.status,
getTiles,
moveTiles,
startGame,
Expand Down
15 changes: 14 additions & 1 deletion reducers/game-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import { uid } from "uid";
import { tileCountPerDimension } from "@/constants";
import { Tile, TileMap } from "@/models/tile";

type GameStatus = "ongoing" | "won";

type State = {
board: string[][];
tiles: TileMap;
tilesByIds: string[];
hasChanged: boolean;
score: number;
status: GameStatus;
};
type Action =
| { type: "create_tile"; tile: Tile }
| { type: "clean_up" }
| { type: "move_up" }
| { type: "move_down" }
| { type: "move_left" }
| { type: "move_right" };
| { type: "move_right" }
| { type: "reset_game" }
| { type: "update_status"; status: GameStatus };

function createBoard() {
const board: string[][] = [];
Expand All @@ -34,6 +39,7 @@ export const initialState: State = {
tilesByIds: [],
hasChanged: false,
score: 0,
status: "ongoing",
};

export default function gameReducer(
Expand Down Expand Up @@ -287,6 +293,13 @@ export default function gameReducer(
score,
};
}
case "reset_game":
return initialState;
case "update_status":
return {
...state,
status: action.status,
};
default:
return state;
}
Expand Down
1 change: 1 addition & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
--secondary-background: #bbada0;
--cell-background: #cac1b5;
--tile-background: #eee4da;
--button-background: #8f7a66;

/* Colors */
--primary-text-color: #776e65;
Expand Down
38 changes: 38 additions & 0 deletions styles/splash.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.splash {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
text-align: center;
z-index: 3;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: calc(var(--pixel-size) * 0.75);
background: rgba(238, 228, 218, 0.6);
}

.win {
background: rgba(237, 194, 46, 0.6);
color: white;
}

.splash > div {
margin: 0 auto;
}

.splash > div > button {
margin: calc(var(--pixel-size) * 3) auto;
}

.button {
background: var(--button-background);
border: calc(var(--pixel-size) * 0.5) solid var(--button-background);
border-radius: calc(var(--pixel-size) * 0.5);
font-size: calc(var(--pixel-size) * 2);
line-height: calc(var(--pixel-size) * 4);
font-weight: bold;
color: var(--tile-text-color);
cursor: pointer;
}

0 comments on commit e586fd6

Please sign in to comment.