From db9ff1aa86a676a353daba41c94a611ee79c1025 Mon Sep 17 00:00:00 2001 From: Daithi Hearn Date: Thu, 1 Feb 2024 22:54:45 +0100 Subject: [PATCH 1/3] fix: card persisting across different games --- src/components/Game/PlayersAndCards.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Game/PlayersAndCards.tsx b/src/components/Game/PlayersAndCards.tsx index aa4d6bc..a54f8bb 100644 --- a/src/components/Game/PlayersAndCards.tsx +++ b/src/components/Game/PlayersAndCards.tsx @@ -4,7 +4,7 @@ import { Player, PlayerProfile } from "model/Player" import PlayerCard from "./PlayerCard" import { useProfiles } from "components/Hooks/useProfiles" import { useAppSelector } from "caches/hooks" -import { getGamePlayers } from "caches/GameSlice" +import { getGameId, getGamePlayers } from "caches/GameSlice" const compareSeat = (a: Player, b: Player) => { if (a.seatNumber > b.seatNumber) return 1 @@ -18,6 +18,7 @@ interface PP { } const PlayersAndCards = () => { + const gameId = useAppSelector(getGameId) const players = useAppSelector(getGamePlayers) const { allProfiles } = useProfiles() @@ -60,7 +61,10 @@ const PlayersAndCards = () => { {playerProfiles.map((pp, index) => ( - + Date: Thu, 1 Feb 2024 23:58:27 +0100 Subject: [PATCH 2/3] reinstating some auto play logic --- src/components/Game/Actions/PlayCard.tsx | 49 ++++++++++++++---------- src/utils/GameUtils.spec.ts | 5 +++ src/utils/GameUtils.ts | 6 +++ 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/components/Game/Actions/PlayCard.tsx b/src/components/Game/Actions/PlayCard.tsx index 3f73c97..73fc46c 100644 --- a/src/components/Game/Actions/PlayCard.tsx +++ b/src/components/Game/Actions/PlayCard.tsx @@ -1,13 +1,7 @@ -import { useCallback, useMemo, useState } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" import { useAppSelector } from "caches/hooks" -import { - getCardsWithoutBlanks, - getGameId, - getIsMyGo, - getRound, - getSelectedCards, -} from "caches/GameSlice" +import { getGame, getSelectedCards } from "caches/GameSlice" import { RoundStatus } from "model/Round" import { Button, @@ -21,16 +15,14 @@ import { } from "@mui/material" import { CardName } from "model/Cards" import { useGameActions } from "components/Hooks/useGameActions" +import { bestCardLead, getBestCard, getWorstCard } from "utils/GameUtils" type AutoPlayState = "off" | "best" | "worst" const PlayCard = () => { const theme = useTheme() const { playCard } = useGameActions() - const round = useAppSelector(getRound) - const gameId = useAppSelector(getGameId) - const myCards = useAppSelector(getCardsWithoutBlanks) - const isMyGo = useAppSelector(getIsMyGo) + const game = useAppSelector(getGame) const [autoPlay, setAutoPlay] = useState("off") const selectedCards = useAppSelector(getSelectedCards) @@ -72,14 +64,13 @@ const PlayCard = () => { const playButtonEnabled = useMemo( () => - isMyGo && - round && - round.status === RoundStatus.PLAYING && - round.completedHands.length + - myCards.filter(c => c.name !== CardName.EMPTY).length === + game.isMyGo && + game.round?.status === RoundStatus.PLAYING && + game.round.completedHands.length + + game.cardsFull.filter(c => c.name !== CardName.EMPTY).length === 5, - [isMyGo, round, myCards], + [game], ) const PlayCardButton = () => ( @@ -94,9 +85,25 @@ const PlayCard = () => { ) const selectCardToPlay = useCallback(() => { - if (selectedCards.length === 1 && gameId) - playCard({ gameId, card: selectedCards[0].name }) - }, [gameId, selectedCards]) + if (selectedCards.length === 1 && game.id) + playCard({ gameId: game.id, card: selectedCards[0].name }) + }, [game.id, selectedCards]) + + // Auto play logic + // 1. If best card lead or lead from bottom enabled, play worst card + // 2. If lead from the top enabled, play best card + useEffect(() => { + if (game.id && game.round?.suit && game.isMyGo) { + if (autoPlay === "worst" || bestCardLead(game.round)) { + const worstCard = getWorstCard(game.cardsFull, game.round) + if (worstCard) + playCard({ gameId: game.id, card: worstCard.name }) + } else if (autoPlay === "best") { + const bestCard = getBestCard(game.cardsFull, game.round) + if (bestCard) playCard({ gameId: game.id, card: bestCard.name }) + } + } + }, [playCard, autoPlay, game]) return ( diff --git a/src/utils/GameUtils.spec.ts b/src/utils/GameUtils.spec.ts index 7fb7d46..f061765 100644 --- a/src/utils/GameUtils.spec.ts +++ b/src/utils/GameUtils.spec.ts @@ -589,6 +589,11 @@ describe("GameUtils", () => { it("only one card left", () => { expect(getBestCard([CARDS.JOKER], ROUND)).toStrictEqual(CARDS.JOKER) }) + it("Hand with EMPTY cards", () => { + expect(getBestCard([EMPTY, ...HAND3], ROUND)).toStrictEqual( + CARDS.TWO_CLUBS, + ) + }) }) describe("canRenege", () => { diff --git a/src/utils/GameUtils.ts b/src/utils/GameUtils.ts index 14ec2ab..03af420 100644 --- a/src/utils/GameUtils.ts +++ b/src/utils/GameUtils.ts @@ -198,6 +198,9 @@ export const canRenege = (myCard: Card, cardLead: Card, suit: Suit) => { } export const getWorstCard = (cards: Card[], round: Round) => { + // Clear empty cards + cards = cards.filter(c => c.name !== CardName.EMPTY) + if (cards.length === 0) throw new Error("No cards to choose from") if (cards.length === 1) return cards[0] @@ -252,6 +255,9 @@ export const getWorstCard = (cards: Card[], round: Round) => { } export const getBestCard = (cards: Card[], round: Round) => { + // Clear empty cards + cards = cards.filter(c => c.name !== CardName.EMPTY) + if (cards.length === 0) throw new Error("No cards to choose from") if (cards.length === 1) return cards[0] From 3483ddaf01737243a0542d6341c8f46525b381bd Mon Sep 17 00:00:00 2001 From: Daithi Hearn Date: Fri, 2 Feb 2024 00:09:08 +0100 Subject: [PATCH 3/3] auto play last card --- src/components/Game/Actions/PlayCard.tsx | 25 ++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/Game/Actions/PlayCard.tsx b/src/components/Game/Actions/PlayCard.tsx index 73fc46c..ce6efab 100644 --- a/src/components/Game/Actions/PlayCard.tsx +++ b/src/components/Game/Actions/PlayCard.tsx @@ -23,6 +23,10 @@ const PlayCard = () => { const theme = useTheme() const { playCard } = useGameActions() const game = useAppSelector(getGame) + const cardsWithoutEmpty = useMemo( + () => game.cardsFull.filter(c => c.name !== CardName.EMPTY), + [game], + ) const [autoPlay, setAutoPlay] = useState("off") const selectedCards = useAppSelector(getSelectedCards) @@ -66,11 +70,9 @@ const PlayCard = () => { () => game.isMyGo && game.round?.status === RoundStatus.PLAYING && - game.round.completedHands.length + - game.cardsFull.filter(c => c.name !== CardName.EMPTY).length === - 5, + game.round.completedHands.length + cardsWithoutEmpty.length === 5, - [game], + [game, cardsWithoutEmpty], ) const PlayCardButton = () => ( @@ -90,20 +92,23 @@ const PlayCard = () => { }, [game.id, selectedCards]) // Auto play logic - // 1. If best card lead or lead from bottom enabled, play worst card - // 2. If lead from the top enabled, play best card + // 1. If only one card in hand, play it + // 2. If best card lead or lead from bottom enabled, play worst card + // 3. If lead from the top enabled, play best card useEffect(() => { if (game.id && game.round?.suit && game.isMyGo) { - if (autoPlay === "worst" || bestCardLead(game.round)) { - const worstCard = getWorstCard(game.cardsFull, game.round) + if (cardsWithoutEmpty.length === 1) { + playCard({ gameId: game.id, card: cardsWithoutEmpty[0].name }) + } else if (autoPlay === "worst" || bestCardLead(game.round)) { + const worstCard = getWorstCard(cardsWithoutEmpty, game.round) if (worstCard) playCard({ gameId: game.id, card: worstCard.name }) } else if (autoPlay === "best") { - const bestCard = getBestCard(game.cardsFull, game.round) + const bestCard = getBestCard(cardsWithoutEmpty, game.round) if (bestCard) playCard({ gameId: game.id, card: bestCard.name }) } } - }, [playCard, autoPlay, game]) + }, [playCard, autoPlay, game, cardsWithoutEmpty]) return (