Skip to content

Commit

Permalink
feat: implement-falling-piece-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanFama592 committed Feb 13, 2024
2 parents a171cfd + 2a9978b commit 8378f22
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 48 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "tic-tac-toe-world",
"version": "1.2.11",
"version": "1.3.11",
"scripts": {
"dev": "astro dev --host",
"start:build": "astro check && astro build && astro preview --host",
Expand Down
17 changes: 2 additions & 15 deletions src/assets/react/contexts/GameContext.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import { createContext } from "react";
import type { BoardType, PlayersType, WinnerType, WinningPositionsType } from "@/assets/types/types";
import { defaultPlayers } from "@/assets/ts/constants";

interface IGameContext {
turn: number
winningLineLength: number
winningPositions: WinningPositionsType
players: PlayersType
board: BoardType
winner: WinnerType
setTurn: (value: number) => void
setBoard: (value: BoardType) => void
setWinner: (value: WinnerType) => void
setWinningPositions: (value: WinningPositionsType) => void
resetGame: () => void
}
import type { IGameContext } from "@/assets/types/interfaces";

/**
* The code is creating a context object called `GameContext` using the `createContext` function from
Expand All @@ -28,6 +14,7 @@ const GameContext = createContext<IGameContext>({
players: defaultPlayers,
winner: null,
board: [[]],
fallingPieceMode: false,
setTurn: () => { },
setWinningPositions: () => { },
setWinner: () => { },
Expand Down
28 changes: 24 additions & 4 deletions src/assets/react/hooks/useGameOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export type OptionsGroup = {
options: OptionsToggle;
handler: (index: number) => void;
}
fallingPieceMode: {
title: string;
index: number;
options: OptionsToggle;
handler: (index: number) => void;
}
}

const useGameOptions = () => {
Expand All @@ -35,14 +41,16 @@ const useGameOptions = () => {
const [sizeIndex, setSizeIndex] = useState(0);
const [playersIndex, setPlayersIndex] = useState(0);
const [winningLineLengthIndex, setWinningLineLengthIndex] = useState(0);
const [fallingPieceModeIndex, setFallingPieceModeIndex] = useState(0);

// create handlers
const handleSizeToggle = (index: number) => { setSizeIndex(index) }
const handlePlayersToggle = (index: number) => { setPlayersIndex(index) }
const handleWinningLineLengthToggle = (index: number) => { setWinningLineLengthIndex(index) }
const handleFallingPieceModeToggle = (index: number) => { setFallingPieceModeIndex(index) }

// initialize options
const initialGroupOptions = useMemo(() => generateOptions(playersIndex, sizeIndex, winningLineLengthIndex), [])
const initialGroupOptions = useMemo(() => generateOptions(playersIndex, sizeIndex, fallingPieceModeIndex, winningLineLengthIndex), [])

const [sizeOptions, setSizeOptions] = useState<OptionsToggle>(
initialGroupOptions.valueSize
Expand All @@ -54,24 +62,31 @@ const useGameOptions = () => {
initialGroupOptions.valueWinningLineLength
);

const [fallingPieceModeOptions, setFallingPieceModeIndexOptions] = useState<OptionsToggle>(
initialGroupOptions.valueFallingPieceMode
);

// reset function
const resetOptions = () => {
handleSizeToggle(0);
handlePlayersToggle(0);
handleWinningLineLengthToggle(0);
handleFallingPieceModeToggle(0);

setSizeOptions(initialGroupOptions.valueSize);
setPlayersOptions(initialGroupOptions.valuePlayers);
setWinningLineLengthOptions(initialGroupOptions.valueWinningLineLength);
setFallingPieceModeIndexOptions(initialGroupOptions.valueFallingPieceMode);
};

// update available options
useEffect(() => {
const options = generateOptions(playersIndex, sizeIndex, winningLineLengthIndex);
const options = generateOptions(playersIndex, sizeIndex, fallingPieceModeIndex, winningLineLengthIndex);
setSizeOptions(options.valueSize);
setPlayersOptions(options.valuePlayers);
setWinningLineLengthOptions(options.valueWinningLineLength);
}, [sizeIndex, playersIndex, winningLineLengthIndex]);
setFallingPieceModeIndexOptions(options.valueFallingPieceMode);
}, [sizeIndex, playersIndex, winningLineLengthIndex, fallingPieceModeIndex]);

// construct options group object
const optionsGroup: OptionsGroup = {
Expand All @@ -87,12 +102,17 @@ const useGameOptions = () => {
options: playersOptions,
handler: handlePlayersToggle,
},

winningLineLength: {
title: "Cantidad de piezas conectadas para ganar:",
index: winningLineLengthIndex,
options: winningLineLengthOptions,
handler: handleWinningLineLengthToggle,
},
fallingPieceMode: {
title: "Caida de piezas:",
index: fallingPieceModeIndex,
options: fallingPieceModeOptions,
handler: handleFallingPieceModeToggle,
}
}

Expand Down
54 changes: 48 additions & 6 deletions src/assets/ts/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,30 @@ const generateAllTrueOptions = () => {
return [value, true]
})

options.valueFallingPieceMode =
["si", "no"].map((value) => {
return [value, true]
})

return options
}

//TODO: replace to use a Json or javascript object to make it easier to extend configurations
// @ts-ignore
export const generateOptions = (playersIndex: number, sizeIndex: number, winningLineLengthIndex: number) => {
export const generateOptions = (
playersIndex: number,
sizeIndex: number,
fallingPieceModeIndex: number,
winningLineLengthIndex: number
) => {
const options = generateAllTrueOptions()

const valuePlayers = Number(options.valuePlayers[playersIndex]![0])
const valueSize = Number(options.valueSize[sizeIndex]![0])
//const valueWinningLineLength = Number(options.valueWinningLineLength[winningLineLengthIndex]![0])
// @ts-ignore
const valueWinningLineLength = Number(options.valueWinningLineLength[winningLineLengthIndex]![0])
// @ts-ignore
const valueFallingPieceMode = Boolean(options.valueFallingPieceMode[fallingPieceModeIndex]![0])

options.valueSize = [
["3", valuePlayers <= 3],
Expand All @@ -61,6 +74,11 @@ export const generateOptions = (playersIndex: number, sizeIndex: number, winning
["5", valueSize >= 5],
];

options.valueFallingPieceMode = [
["si", true],
["no", true],
];

return options;
};

Expand All @@ -78,6 +96,27 @@ export const checkTie = (board: BoardType): boolean => {
return board.flat().every(square => square !== null);
};

export const piecesFalling = (
board: BoardType,
piecePositionAbs: SizeDeclarationBoard
): { board: BoardType, indexs: SizeDeclarationBoard } => {
const pieceMain = board[piecePositionAbs[0]]![piecePositionAbs[1]]

let iters = 1

while (
board[(piecePositionAbs[0] + iters)] !== undefined
&& board[(piecePositionAbs[0] + iters)]![piecePositionAbs[1]] === null
) {
iters = iters + 1;
}

board[piecePositionAbs[0]]![piecePositionAbs[1]] = null
board[(piecePositionAbs[0] + iters - 1)]![piecePositionAbs[1]] = pieceMain ?? null

return { board, indexs: [(piecePositionAbs[0] + iters - 1), piecePositionAbs[1]] }
}

function get3x3GridOfABoard(
board: BoardType,
[initialRow, initialCol]: SizeDeclarationBoard
Expand Down Expand Up @@ -142,7 +181,7 @@ export const checkWinner = (
piecePositionAbs: SizeDeclarationBoard,
winningLineLength: number
): SizeDeclarationBoard[][] | null => {

const bucket: BucketTypeN = {
top: [],
bottom: [],
Expand All @@ -154,7 +193,10 @@ export const checkWinner = (
rightBottom: [],
}

const mainPieceIndex: number = board[piecePositionAbs[0]]![piecePositionAbs[1]]!
const mainPiece: number = board[piecePositionAbs[0]]![piecePositionAbs[1]]!

if (mainPiece === null || mainPiece === undefined) return null

const grid = get3x3GridOfABoard(board, piecePositionAbs)

if (!grid) return null;
Expand All @@ -165,7 +207,7 @@ export const checkWinner = (

if (indexLocalCol === 1 && indexLocalRow === 1) return;

if (borderingPiece !== mainPieceIndex) return;
if (borderingPiece !== mainPiece) return;

const orientation: keyof PosMovN | null = searchInSchemaMovement([indexLocalRow, indexLocalCol]);

Expand All @@ -180,7 +222,7 @@ export const checkWinner = (

bucket[orientation].push(piecePositionAbs)

while (currentPiece === mainPieceIndex) {
while (currentPiece === mainPiece) {
bucket[orientation].push(currentPiecePositionAbs)

currentPiecePositionAbs = [
Expand Down
18 changes: 17 additions & 1 deletion src/assets/types/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import type { BoardType, PlayersType, SizeDeclarationBoard } from "@/assets/types/types"
import type { BoardType, PlayersType, SizeDeclarationBoard, WinnerType, WinningPositionsType } from "@/assets/types/types"


export interface IGameContext {
turn: number
winningLineLength: number
fallingPieceMode: boolean
winningPositions: WinningPositionsType
players: PlayersType
board: BoardType
winner: WinnerType
setTurn: (value: number) => void
setBoard: (value: BoardType) => void
setWinner: (value: WinnerType) => void
setWinningPositions: (value: WinningPositionsType) => void
resetGame: () => void
}

export interface IGame {
size?: SizeDeclarationBoard,
winningLineLength?: number,
fallingPieceMode?: boolean,
players?: PlayersType,
initialTurn?: number,
disabledReset?: boolean,
Expand Down
3 changes: 1 addition & 2 deletions src/assets/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export type BoardType = (number | null)[][]

export type GridOfBoardType = (number | null | undefined)[][]

export type UpdateBoardType = (index: SizeDeclarationBoard) => void

export type OptionsToggle = [string, boolean][];

export type PosMovN = {
Expand Down Expand Up @@ -46,6 +44,7 @@ export type BucketTypeN = {
export type GenerateOptionsParams = {
valuePlayers: number;
valueSize: number,
valueFallingPieceMode: number,
valueWinningLineLength: number
};

Expand Down
38 changes: 22 additions & 16 deletions src/components/react/Board.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Square from './Square'
import { useCallback, useContext } from 'react'
import GameContext from '@/assets/react/contexts/GameContext'
import { checkTie, checkWinner, createCopyBoard, nextIndex } from '@/assets/ts/functions'
import type { BoardType, SizeDeclarationBoard, UpdateBoardType } from '@/assets/types/types'
import { checkTie, checkWinner, createCopyBoard, nextIndex, piecesFalling } from '@/assets/ts/functions'
import type { BoardType, SizeDeclarationBoard } from '@/assets/types/types'
import { sizeBoardColumnsVarCSS, sizeBoardRowsVarCSS, tieValue } from '@/assets/ts/constants'
import WinnerMessage from './WinnerMessage'

Expand All @@ -12,6 +12,7 @@ export default function Board() {
players,
winner,
board,
fallingPieceMode,
winningLineLength,
winningPositions,
setWinningPositions,
Expand All @@ -23,17 +24,17 @@ export default function Board() {
const gridTemplateRows = `repeat(var(${sizeBoardRowsVarCSS}), 2fr`
const gridTemplateColumns = `repeat(var(${sizeBoardColumnsVarCSS}), 2fr`

const checkBoard = (indexs: [number, number], board: BoardType) => {
const pieceswinning = checkWinner(board, indexs, winningLineLength)
const checkBoard = (indexs: SizeDeclarationBoard, newBoard: BoardType) => {
const piecesWinning = checkWinner(newBoard, indexs, winningLineLength)

if (pieceswinning !== null) {
setWinningPositions(pieceswinning)
if (piecesWinning !== null) {
setWinningPositions(piecesWinning)
setWinner(turn);
setTurn(turn);
return;
}

if (checkTie(board)) {
if (checkTie(newBoard)) {
setWinner(tieValue);
setTurn(tieValue);
return;
Expand All @@ -50,24 +51,29 @@ export default function Board() {
);
};

const updateBoard: UpdateBoardType = useCallback((indexs) => {
const boardPos = board[indexs[0]]?.[indexs[1]]
const updateBoard = useCallback((indexs: SizeDeclarationBoard) => {
let newIndexs = indexs
const boardCurrentPiece = board[newIndexs[0]]?.[newIndexs[1]]

// evaluate if there is a piece in the square or if there is a winner
if (boardPos || boardPos === undefined || winner) return;

const newBoard = createCopyBoard(board)

newBoard[indexs[0]]![indexs[1]] = turn
if (boardCurrentPiece || boardCurrentPiece === undefined || winner) return;

let newBoard = createCopyBoard(board)

newBoard[newIndexs[0]]![newIndexs[1]] = turn

if (fallingPieceMode) {
({ board: newBoard, indexs: newIndexs } = piecesFalling(newBoard, newIndexs));
}

setBoard(newBoard)

setTurn(
nextIndex(turn, players)
)

checkBoard(indexs, newBoard)
}, [turn, winner, board, players])
checkBoard(newIndexs, newBoard)
}, [turn, winner, board, players, checkBoard])

return (
<div
Expand Down
Loading

0 comments on commit 8378f22

Please sign in to comment.