diff --git a/public/images/game-of-life.png b/public/images/game-of-life.png
new file mode 100644
index 0000000..0e44910
Binary files /dev/null and b/public/images/game-of-life.png differ
diff --git a/src/app/components/algorithm-cards.jsx b/src/app/components/algorithm-cards.jsx
index 9ac12ac..87647eb 100644
--- a/src/app/components/algorithm-cards.jsx
+++ b/src/app/components/algorithm-cards.jsx
@@ -58,6 +58,11 @@ const algorithms = [
title: 'Binary Search',
description: "Binary search is an efficient algorithm for finding an item from a sorted list of item",
image: '/AlgorithmVisualizer/images/binary-search.png?height=200&width=300'
+ },{
+ id: 'game-of-life',
+ title: 'Game of Life',
+ description: "Visualize the Game of Life cellular automaton",
+ image: '/AlgorithmVisualizer/images/game-of-life.png?height=200&width=300'
},
// {
// id: '15-puzzle',
diff --git a/src/app/game-of-life/grid.css b/src/app/game-of-life/grid.css
new file mode 100644
index 0000000..cdd1145
--- /dev/null
+++ b/src/app/game-of-life/grid.css
@@ -0,0 +1,10 @@
+
+.Grid{
+ font-size: 0;
+}
+div{
+ padding: 0px;
+ margin: 0px;
+ margin-bottom: 0px;
+ padding-bottom: 0px;
+}
\ No newline at end of file
diff --git a/src/app/game-of-life/grid.jsx b/src/app/game-of-life/grid.jsx
new file mode 100644
index 0000000..e058a37
--- /dev/null
+++ b/src/app/game-of-life/grid.jsx
@@ -0,0 +1,32 @@
+
+import './grid.css';
+import Node from "./node";
+
+
+export default function Grid({ grid, onMouseDown, onMouseEnter, onMouseUp }) {
+ return (
+
onMouseDown(node.row, node.col)}
+ onMouseEnter={() => onMouseEnter(node.row, node.col)}
+ onMouseUp={() => onMouseUp(node.row, node.col)}
+ />
+ );
+
+ function getClassName() {
+ if (node.isAlive === true) {
+ return "node node-wall";
+ }else {
+ return "node";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/game-of-life/page.jsx b/src/app/game-of-life/page.jsx
new file mode 100644
index 0000000..e42c22c
--- /dev/null
+++ b/src/app/game-of-life/page.jsx
@@ -0,0 +1,181 @@
+"use client";
+import Navbar from '@/components/navbar';
+import { createRef, useRef, useState, useEffect } from 'react';
+import Menu from "./menu";
+import Grid from "./grid";
+
+
+export default function GameOfLifePage() {
+
+ let gridRef = createRef();
+
+ const [grid, setGrid] = useState([]);
+ const [running, setRunning] = useState(false);
+ const runningRef = useRef(false); // Add this ref
+
+
+ useEffect(() => {
+ const width = gridRef.current.offsetWidth;
+ const height = gridRef.current.offsetHeight;
+ const row = Math.max(Math.floor(height / 25) - 2, 10);
+ const col = Math.floor(width / 25);
+ setGrid(getInitialGrid(row, col));
+ }, []);
+
+ const handleMouseDown = (row, col) => {
+
+ const newGrid = getNewGridWithWallToggled(grid, row, col);
+ setGrid(newGrid);
+
+ // this.setState({ mouseIsPressed: true });
+ }
+
+ const handleMouseEnter = (row, col) => {
+ // if (this.state.mouseIsPressed === false) return;
+ // if ((this.state.startNode.row !== row || this.state.startNode.col !== col) && (this.state.endNode.row !== row || this.state.endNode.col !== col)) {
+ // const newGrid = getNewGridWithWallToggled(this.state.grid, row, col);
+ // this.setState({ grid: newGrid });
+ // }
+ }
+
+ const handleMouseUp = (row, col) => {
+ // this.setState({ mouseIsPressed: false });
+ }
+
+ const handleStart = () => {
+ setRunning(true);
+ runningRef.current = true; // Update ref
+
+ gameOfLife();
+ }
+
+ const handleStop = () => {
+ setRunning(false);
+ runningRef.current = false;
+ console.log("Simulation stopped");
+ }
+
+ const handleClearBoard = () => {
+ setRunning(false);
+ runningRef.current = false;
+ const width = gridRef.current.offsetWidth;
+ const height = gridRef.current.offsetHeight;
+ const row = Math.max(Math.floor(height / 25) - 2, 10);
+ const col = Math.floor(width / 25);
+ setGrid(getInitialGrid(row, col));
+ }
+
+ const gameOfLife = async () => {
+ let newGrid = getNextGeneration(grid);
+ while (runningRef.current) {
+ setGrid(newGrid);
+ newGrid = getNextGeneration(newGrid);
+ await sleep(200);
+ }
+ }
+
+ return (
+
+ );
+}
+
+const getInitialGrid = (row, col) => {
+ let grid = [];
+ for (let i = 0; i < row; i++) {
+ let row = [];
+ for (let j = 0; j < col; j++) {
+ row.push(createNode(i, j));
+ }
+ grid.push(row);
+ }
+ return grid;
+}
+
+const createNode = (row, col) => {
+ return {
+ row,
+ col,
+ isAlive: false
+ }
+}
+
+const getNewGridWithWallToggled = (grid, row, col) => {
+ const newGrid = grid.slice();
+ const node = newGrid[row][col];
+
+ const newNode = {
+ ...node,
+ isAlive: !node.isAlive,
+ };
+
+ newGrid[row][col] = newNode;
+ return newGrid;
+};
+
+const getNextGeneration = (grid) => {
+ const newGrid = grid.slice();
+ for (let i = 0; i < grid.length; i++) {
+ newGrid[i] = grid[i].slice();
+ for (let j = 0; j < grid[i].length; j++) {
+ const node = grid[i][j];
+ const aliveNeighbors = getAliveNeighbors(grid, node);
+
+ if (node.isAlive && (aliveNeighbors < 2 || aliveNeighbors > 3)) {
+ newGrid[i][j] = {
+ ...node,
+ isAlive: false
+ }
+ }
+ if (!node.isAlive && aliveNeighbors === 3) {
+ newGrid[i][j] = {
+ ...node,
+ isAlive: true
+ }
+ }
+ }
+ }
+ return newGrid;
+}
+
+const getAliveNeighbors = (grid, node) => {
+
+ const { row, col } = node;
+ const dirx = [-1, 1, 0, 0, -1, -1, 1, 1];
+ const diry = [0, 0, -1, 1, -1, 1, -1, 1];
+ let count = 0;
+ for (let i = 0; i < 8; i++) {
+ const newRow = row + dirx[i];
+ const newCol = col + diry[i];
+ if (newRow >= 0 && newRow < grid.length && newCol >= 0 && newCol < grid[0].length && grid[newRow][newCol].isAlive) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
\ No newline at end of file
diff --git a/src/app/game-of-life/styles.css b/src/app/game-of-life/styles.css
new file mode 100644
index 0000000..080607a
--- /dev/null
+++ b/src/app/game-of-life/styles.css
@@ -0,0 +1,100 @@
+.node{
+ height:25px;
+ width:25px;
+ background-color: white;
+ outline:1px solid rgb(175, 216, 248);
+ display: inline-block;
+}
+
+.node-start{
+ background-color: chartreuse;
+}
+.node-end{
+ background-color: brown;
+}
+.node-visited{
+ animation-name: visitedAnimation;
+ animation-iteration-count: 1;
+ animation-duration: 1.5s;
+ animation-delay: 0;
+ background-color: rgba(0, 190, 218, 0.75);
+}
+
+@keyframes visitedAnimation {
+ 0% {
+ transform: scale(0.3);
+ background-color: rgba(0, 0, 66, 0.75);
+ border-radius: 100%;
+ }
+
+ 50% {
+ background-color: rgba(17, 104, 217, 0.75);
+ }
+
+ 75% {
+ transform: scale(1.2);
+ background-color: rgba(0, 217, 159, 0.75);
+ }
+
+ 100% {
+ transform: scale(1);
+ background-color: rgba(0, 190, 218, 0.75);
+ }
+}
+
+.node-alive {
+ background-color: black;
+ outline: 1px solid black;
+ animation-name: wallAnimation;
+ animation-duration: 0.3s;
+ animation-timing-function: ease-out;
+ animation-delay: 0;
+ animation-direction: alternate;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ animation-play-state: running;
+}
+@keyframes wallAnimation {
+ 0% {
+ transform: scale(.3);
+ background-color: rgb(12, 53, 71);
+ }
+
+ 50% {
+ transform: scale(1.2);
+ background-color: rgb(12, 53, 71);
+ }
+
+ 100% {
+ transform: scale(1.0);
+ background-color: rgb(12, 53, 71);
+ }
+}
+
+.node-shortest-path {
+ animation-name: shortestPath;
+ animation-duration: 1.5s;
+ animation-timing-function: ease-out;
+ animation-delay: 0;
+ animation-direction: alternate;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ animation-play-state: running;
+}
+
+@keyframes shortestPath {
+ 0% {
+ transform: scale(0.6);
+ background-color: rgb(255, 254, 106);
+ }
+
+ 50% {
+ transform: scale(1.2);
+ background-color: rgb(255, 254, 106);
+ }
+
+ 100% {
+ transform: scale(1);
+ background-color: rgb(255, 254, 106);
+ }
+}
\ No newline at end of file