Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions frontend/src/App.d.ts

This file was deleted.

22 changes: 11 additions & 11 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import './App.css';
import { generateBoard } from './utils/board';
import type { Board } from './types/types';
import { numberOfMine, size, BoardComponent, resetBoardState } from './components/Board';
import { resetCellState } from "./components/Cell";
export let UserName = '';
import { numberOfMine, size, BoardComponent } from './components/Board';


function App() {
Expand All @@ -19,7 +17,6 @@ function App() {

const startGame = () => {
if (username.trim() !== '') {
UserName = username;
setStart(Date.now());
setElapsedTime(0);
setGameStarted(true);
Expand All @@ -29,9 +26,10 @@ function App() {
}
};

const handleGameOver = useCallback(() => setIsGameOver(true), []);
const handleGameClear = useCallback(() => setIsGameOver(true), []);

const restartGame = () => {
resetCellState();
resetBoardState();
setBoard(generateBoard(size, size, numberOfMine));
setStart(Date.now());
setElapsedTime(0);
Expand Down Expand Up @@ -125,8 +123,9 @@ function App() {
board={board}
setBoard={setBoard}
flaggingMode={false}
onGameOver={() => setIsGameOver(true)}
onGameClear={() => setIsGameOver(true)}
onGameOver={handleGameOver}
onGameClear={handleGameClear}
userName={username}
/>
<button
onClick={restartGame}
Expand Down Expand Up @@ -168,8 +167,9 @@ function App() {
board={board}
setBoard={setBoard}
flaggingMode={flaggingMode}
onGameOver={() => setIsGameOver(true)}
onGameClear={() => setIsGameOver(true)}
onGameOver={handleGameOver}
onGameClear={handleGameClear}
userName={username}
/>
<div style={{
display: "flex",
Expand Down
13 changes: 0 additions & 13 deletions frontend/src/components/Board.d.ts

This file was deleted.

91 changes: 52 additions & 39 deletions frontend/src/components/Board.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import React, { useRef, useState } from "react";
import React, { useCallback, useRef, useState } from "react";
import type { Board } from "../types/types";
import { generateBoard } from "../utils/board";
import {Cell} from "./Cell";
import { Cell } from "./Cell";

export const size = 15;
export const numberOfMine = Math.floor(size * size / 10);

Check warning on line 7 in frontend/src/components/Board.tsx

View workflow job for this annotation

GitHub Actions / Frontend (type check, lint, build)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components

Check warning on line 7 in frontend/src/components/Board.tsx

View workflow job for this annotation

GitHub Actions / Frontend (type check, lint, build)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
export let chainedblock = 0;
export let firstblock = false;

export function resetBoardState() {
chainedblock = 0;
firstblock = false;
}
const API_BASE_URL = "https://1r2mypgiag.execute-api.ap-southeast-2.amazonaws.com/prod";

interface Props {
board: Board;
setBoard: React.Dispatch<React.SetStateAction<Board>>;
flaggingMode: boolean;
onGameOver: () => void;
onGameClear: () => void;
userName: string;
}

const calculateCellSize = () => {
Expand All @@ -29,52 +25,70 @@

const initialCellSize = calculateCellSize();

export const BoardComponent: React.FC<Props> = ({ board, setBoard, flaggingMode, onGameOver, onGameClear }) => {
export const BoardComponent: React.FC<Props> = ({ board, setBoard, flaggingMode, onGameOver, onGameClear, userName }) => {
const [isGameActive, setIsGameActive] = useState(true);
const [hasClickedOnce, setHasClickedOnce] = useState(false);
const startTimeRef = useRef<number>(0);
const chainedblockRef = useRef(0);
const openedblockRef = useRef(0);

const handleGameClear = () => {
setIsGameActive(false);
onGameClear();
};
const handleGameOver = () => {
setIsGameActive(false);
onGameOver();
};

const handleManualOpen = useCallback(() => {
openedblockRef.current++;
if (chainedblockRef.current + openedblockRef.current + numberOfMine >= size * size) {
const timeTaken = Math.floor((Date.now() - startTimeRef.current) / 1000);
setIsGameActive(false);
onGameClear();
alert("🎊 Game Clear!\n in " + timeTaken + " seconds!");
fetch(`${API_BASE_URL}/scores`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
player_name: userName,
time_taken: timeTaken,
blocks: (size * size),
}),
})
.then(res => res.json())
.then(data => console.log('Score API response:', data))
.catch(err => console.error('API Error:', err));
}
}, [onGameClear, userName]);

const currentCellSize = initialCellSize;
const handleClick = (r: number, c: number) => {
if (!isGameActive) return;
if (!firstblock) {
if (!hasClickedOnce) {
let newBoard: Board;
do {
newBoard = generateBoard(size, size, numberOfMine);
} while (newBoard[r][c].isMine || newBoard[r][c].neighborMines !== 0);
newBoard[r][c].isOpen = true;
firstblock = true;
setHasClickedOnce(true);
startTimeRef.current = Date.now();
if (newBoard[r][c].neighborMines === 0) {
chainOpen(newBoard, newBoard[r][c]);
}
chainedblockRef.current += chainOpen(newBoard, newBoard[r][c]);
setBoard(newBoard);
}else{
} else {
const newBoard = board.map((row) => row.map((cell) => ({ ...cell })));
newBoard[r][c].isOpen = true;
if (newBoard[r][c].neighborMines === 0 && !newBoard[r][c].isMine) {
chainOpen(newBoard, newBoard[r][c]);
chainedblockRef.current += chainOpen(newBoard, newBoard[r][c]);
}
setBoard(newBoard);
}
};
return (
<div style={{
display: "grid",
justifyContent: "center",
justifyContent: "center",
alignContent: "center",

gridTemplateColumns: `repeat(${size}, ${currentCellSize+5}px)`,
gridTemplateColumns: `repeat(${size}, ${currentCellSize+5}px)`,
paddingBottom: "50px",
width: `${(currentCellSize + 5) * size}px`,

borderRadius: "8px",
}}>
{board.map((row, r) =>
Expand All @@ -84,42 +98,41 @@
cell={cell}
cellSize={currentCellSize}
board={board}
startTime={startTimeRef.current}
hasClickedOnce={hasClickedOnce}
onClick={(newBoard) => {
if (newBoard) {
setBoard(newBoard);
} else {
handleClick(r, c);
}
}}
onGameClear={handleGameClear}
onGameOver={handleGameOver}
isGameActive={isGameActive}
flaggingMode={flaggingMode}
onManualOpen={handleManualOpen}
/>
))
)}
</div>
);
};


function chainOpen(board: Board, cell: Board[0][0]): void {
function chainOpen(board: Board, cell: Board[0][0]): number {
let count = 0;
const move = [-1, 0, 1];
for(let i = 0; i < 3; i++){
for(let j = 0; j < 3; j++){
if(i === 1 && j === 1){
continue;
}
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) continue;
const nr = cell.row + move[i], nc = cell.col + move[j];
if(0 <= nr && nr < board.length && 0 <= nc && nc < board[0].length
&& !board[nr][nc].isMine && !board[nr][nc].isOpen){
chainedblock++;
if (0 <= nr && nr < board.length && 0 <= nc && nc < board[0].length
&& !board[nr][nc].isMine && !board[nr][nc].isOpen) {
board[nr][nc].isOpen = true;
if(board[nr][nc].neighborMines === 0) {
chainOpen(board, board[nr][nc]);
count++;
if (board[nr][nc].neighborMines === 0) {
count += chainOpen(board, board[nr][nc]);
}
}
}
}
}
return count;
}
16 changes: 0 additions & 16 deletions frontend/src/components/Cell.d.ts

This file was deleted.

41 changes: 7 additions & 34 deletions frontend/src/components/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import React from "react";
import { UserName } from "../App";
import type { Board, Cell as CellType} from "../types/types";
import { size, numberOfMine, chainedblock} from "./Board";
import { posOfmine } from "../utils/board";
export let openedblock = 0;
const API_BASE_URL = "https://1r2mypgiag.execute-api.ap-southeast-2.amazonaws.com/prod";


type Props = {
cell: CellType;
cellSize: number;
onClick: (newBoard?: Board) => void;
board: Board;
startTime: number;
onGameClear: () => void;
hasClickedOnce: boolean;
onManualOpen: () => void;
onGameOver: () => void;
isGameActive: boolean;
flaggingMode: boolean;
};

export function resetCellState() {
openedblock = 0;
}

const getCellText = (cell: CellType): string => {
let display = "";
if(cell.isOpen){
Expand All @@ -38,7 +29,8 @@ const getCellText = (cell: CellType): string => {
}
return display;
};
export const Cell: React.FC<Props> = ({ cell, cellSize, onClick, board, startTime, onGameClear, onGameOver, isGameActive, flaggingMode}) => {

export const Cell: React.FC<Props> = ({ cell, cellSize, onClick, board, hasClickedOnce, onManualOpen, onGameOver, isGameActive, flaggingMode}) => {
const handleClick = () => {
if(!isGameActive) return;
if(flaggingMode){
Expand All @@ -49,7 +41,7 @@ export const Cell: React.FC<Props> = ({ cell, cellSize, onClick, board, startTim
}
if(cell.isOpen) return;
if(cell.isflagged) return;
if(cell.isMine){
if(cell.isMine && hasClickedOnce){
const newBoard = board.map((row) => row.map((c) => ({ ...c })));
newBoard[cell.row][cell.col].openedMine = true;
for(const mine of posOfmine){
Expand All @@ -62,26 +54,7 @@ export const Cell: React.FC<Props> = ({ cell, cellSize, onClick, board, startTim
}
onClick();
if(!cell.isOpen){
openedblock++;
if(!(chainedblock + openedblock + numberOfMine < size * size)){
const timeTaken = Math.floor((Date.now() - startTime) / 1000);
onGameClear();
alert("🎊 Game Clear!\n in "+ timeTaken+" seconds!");
fetch(`${API_BASE_URL}/scores`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
player_name: UserName,
time_taken: timeTaken,
blocks: (size * size),
}),
})
.then(res => res.json())
.then(data => console.log('Score API response:', data))
.catch(err => console.error('API Error:', err));
}
onManualOpen();
}
}
return (
Expand All @@ -101,4 +74,4 @@ export const Cell: React.FC<Props> = ({ cell, cellSize, onClick, board, startTim
{getCellText(cell)}
</button>
);
};
};
6 changes: 0 additions & 6 deletions frontend/src/components/timer.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/main.d.ts

This file was deleted.

Loading