From 12c0b371c9ecb3cb23ec61e6f8380638d5d1ce92 Mon Sep 17 00:00:00 2001 From: Milo Date: Tue, 22 Jul 2025 13:06:43 +0200 Subject: [PATCH 1/3] trying stuffs --- game.js | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 71 +++++++++++++++++++++++- 2 files changed, 231 insertions(+), 1 deletion(-) diff --git a/game.js b/game.js index e71dd09..2541a0f 100644 --- a/game.js +++ b/game.js @@ -366,4 +366,165 @@ export function formatConnect4BoardForDiscord(board) { null: '⚪' }; return board.map(row => row.map(cell => symbols[cell]).join('')).join('\n'); +} + +export function shuffle(arr) { + let currentIndex = arr.length, randomIndex + + while (currentIndex !== 0) { + randomIndex = Math.floor(Math.random() * currentIndex) + currentIndex-- + + [arr[currentIndex], arr[randomIndex]] = [arr[randomIndex], arr[currentIndex]] + } + + return arr +} + +export function deal(deck) { + const tableauPiles = [[], [], [], [], [], [], []] + const foundationPiles = [[], [], [], []] + const stockPile = [] + const wastePile = [] + + for (let i = 0; i < 7; i++) { + for (let j = i; j < 7; j++) { + tableauPiles[j].push(deck.shift()) + } + tableauPiles[i][tableauPiles[i].length - 1].faceUp = true + } + + stockPile.push(...deck) + + return { + tableauPiles, + foundationPiles, + stockPile, + wastePile + } +} + +export function isValidMove(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState) { + const sourceCard = sourcePile === 'wastePile' ? gameState[sourcePile][gameState[sourcePile].length - 1] : gameState[sourcePile][destPileIndex][gameState[sourcePile][destPileIndex].length - 1] + const destCard = destPile === 'empty' ? null : gameState[destPile][destPileIndex][gameState[destPile][destPileIndex].length - 1] + + console.log(sourceCard) + console.log(destCard) + + if (sourcePile.startsWith('tableauPiles')) { + if (destPile.startsWith('tableauPiles')) { + if (destCard === null && sourceCard.rank === 'K') return true + if (destCard === null) return false + const sourceRankValue = getRankValue(sourceCard.rank) + const destRankValue = getRankValue(destCard.rank) + + const sourceColor = (sourceCard.suit === 'd' || sourceCard.suit === 'h') ? 'red' : 'black' + const destColor = (destCard.suit === 'd' || destCard.suit === 'h') ? 'red' : 'black' + + console.log('tab -> tab') + console.log({sourceRankValue, sourceColor}) + console.log({destRankValue, destColor}) + + return (destRankValue - sourceRankValue === 1 && sourceColor !== destColor) + } + else if (destPile.startsWith('foundationPiles')) { + if (destCard === null && sourceCard.rank === 'A') return true + if (destCard === null) return false + + console.log('tab -> found') + console.log([sourceCard.suit, getRankValue(sourceCard.rank)]) + console.log([destCard.suit, getRankValue(destCard.rank)]) + + return (sourceCard.suit === destCard.suit && getRankValue(sourceCard.rank) - getRankValue(destCard.rank) === -1) + } + return false + } + + if (sourcePile === 'wastePile') { + if (destPile.startsWith('tableauPiles')) { + if (destCard === null && sourceCard.rank === 'K') return true + if (destCard === null) return false + const sourceRankValue = getRankValue(sourceCard.rank) + const destRankValue = getRankValue(destCard.rank) + + const sourceColor = (sourceCard.suit === 'd' || sourceCard.suit === 'h') ? 'red' : 'black' + const destColor = (destCard.suit === 'd' || destCard.suit === 'h') ? 'red' : 'black' + + console.log('waste -> tab') + console.log({sourceRankValue, sourceColor}) + console.log({destRankValue, destColor}) + + return (destRankValue - sourceRankValue === 1 && sourceColor !== destColor) + } + else if (destPile.startsWith('foundationPiles')) { + if (destCard === null && sourceCard.rank === 'A') return true + if (destCard === null) return false + + console.log('waste -> found') + console.log([sourceCard.suit, getRankValue(sourceCard.rank)]) + console.log([destCard.suit, getRankValue(destCard.rank)]) + + return (sourceCard.suit === destCard.suit && getRankValue(sourceCard.rank) - getRankValue(destCard.rank) === -1) + } + return false + } + return false +} + +export function getRankValue(rank) { + switch (rank) { + case 'A': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'T': return 10; + case 'J': return 11; + case 'Q': return 12; + case 'K': return 13; + default: return 0; + } +} + +export function moveCard(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState) { + const card = sourcePile === 'wastePile' ? gameState[sourcePile].splice(sourceCardIndex, 1)[0] : gameState[sourcePile].splice(gameState[sourcePile].length - 1, 1)[0]; + + if (destPile === 'empty') { + gameState[destPile] = [card] + } else if (destPile.startsWith('tableauPiles')) { + gameState[destPile].push(card); + } else { + gameState[destPile].push(card); + } + + if (sourcePile.startsWith('tableauPiles') && gameState[sourcePile].length > 0 ) { + gameState[sourcePile][gameState[sourcePile].length - 1].faceUp = true; + } +} + +export function drawCard(gameState) { + if (gameState.stockPile.length > 0) { + const card = gameState.stockPile.shift(); + card.faceUp = true; + gameState.wastePile.push(card); + } else { + gameState.stockPile = gameState.wastePile.reverse().map(card => { + card.faceUp = false; + return card; + }); + gameState.wastePile = []; + } +} + +export function checkWinCondition(gameState) { + for (const pile of gameState.foundationPiles) { + if (pile.length !== 13) { + return false; + } + } + return true; } \ No newline at end of file diff --git a/index.js b/index.js index 01d1d5f..cf22fff 100644 --- a/index.js +++ b/index.js @@ -26,7 +26,8 @@ import { eloHandler, formatConnect4BoardForDiscord, pokerEloHandler, randomSkinPrice, - slowmodesHandler + slowmodesHandler, + deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, } from './game.js'; import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import cron from 'node-cron'; @@ -74,6 +75,7 @@ app.use((req, res, next) => { }); // To keep track of our active games const activeGames = {}; +const activeSolitaireGames = {}; const activePolls = {}; const activeInventories = {}; const activeSearchs = {}; @@ -4446,6 +4448,73 @@ async function updatePokerPlayersSolve(roomId) { } } +app.post('/solitaire/start', async (req, res) => { + const userId = req.body.userId; + const deck = createDeck(); + const gameState = deal(deck); + activeSolitaireGames[userId] = gameState + res.json({ success: true, gameState }); +}); + +app.get('/solitaire/state/:userId', (req, res) => { + const userId = req.params.userId; + let gameState = activeSolitaireGames[userId]; + + if (!gameState) { + const deck = createDeck(); + gameState = deal(deck); + activeSolitaireGames[userId] = gameState; + + console.log(`New Solitaire game created for user ${userId}`); + } + + res.json({ success: true, gameState }); +}); + +app.post('/solitaire/move', (req, res) => { + const { userId, sourcePile, sourceCardIndex, destPile, destPileIndex } = req.body; + const gameState = activeSolitaireGames[userId]; + + if (!gameState) { + return res.status(404).json({ error: "Game not found" }); + } + + const valid = isValidMove(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState); + + if (valid) { + moveCard(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState); + const win = checkWinCondition(gameState); + res.json({ success: true, gameState, win }); + } else { + res.status(400).json({ error: "Invalid move" }); + } +}); + +app.post('/solitaire/draw', (req, res) => { + const { userId } = req.body; + const gameState = activeSolitaireGames[userId]; + + if (!gameState) { + return res.status(404).json({ error: `Game not found for ${userId}` }); + } + + drawCard(gameState); + + res.json({ success: true, gameState }); +}); + +function createDeck() { + const suits = ['c', 'd', 'h', 's']; + const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']; + const deck = []; + for (const suit of suits) { + for (const rank of ranks) { + deck.push({ suit, rank, image: `${rank}${suit}.png`, faceUp: false }); + } + } + return shuffle(deck); +} + import http from 'http'; import { Server } from 'socket.io'; import * as test from "node:test"; From b0b319039c54694f1858d73d33027b5e20982951 Mon Sep 17 00:00:00 2001 From: Milo Date: Tue, 22 Jul 2025 14:35:45 +0200 Subject: [PATCH 2/3] solitaire working --- game.js | 325 +++++++++++++++++++++++++++++++++---------------------- index.js | 65 ++++++----- 2 files changed, 231 insertions(+), 159 deletions(-) diff --git a/game.js b/game.js index 2541a0f..29b8165 100644 --- a/game.js +++ b/game.js @@ -368,163 +368,230 @@ export function formatConnect4BoardForDiscord(board) { return board.map(row => row.map(cell => symbols[cell]).join('')).join('\n'); } -export function shuffle(arr) { - let currentIndex = arr.length, randomIndex - +/** + * Shuffles an array in place using the Fisher-Yates algorithm. + * @param {Array} array - The array to shuffle. + * @returns {Array} The shuffled array. + */ +export function shuffle(array) { + let currentIndex = array.length, + randomIndex; while (currentIndex !== 0) { - randomIndex = Math.floor(Math.random() * currentIndex) - currentIndex-- - - [arr[currentIndex], arr[randomIndex]] = [arr[randomIndex], arr[currentIndex]] + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; } - - return arr + return array; } +/** + * Deals a shuffled deck into the initial Solitaire game state. + * @param {Array} deck - A shuffled deck of cards. + * @returns {Object} The initial gameState object. + */ export function deal(deck) { - const tableauPiles = [[], [], [], [], [], [], []] - const foundationPiles = [[], [], [], []] - const stockPile = [] - const wastePile = [] + const gameState = { + tableauPiles: [[], [], [], [], [], [], []], + foundationPiles: [[], [], [], []], + stockPile: [], + wastePile: [], + }; + // Deal cards to the tableau piles for (let i = 0; i < 7; i++) { for (let j = i; j < 7; j++) { - tableauPiles[j].push(deck.shift()) + gameState.tableauPiles[j].push(deck.shift()); } - tableauPiles[i][tableauPiles[i].length - 1].faceUp = true } - stockPile.push(...deck) + // Flip the top card of each tableau pile + gameState.tableauPiles.forEach(pile => { + if (pile.length > 0) { + pile[pile.length - 1].faceUp = true; + } + }); - return { - tableauPiles, - foundationPiles, - stockPile, - wastePile - } + // The rest of the deck becomes the stock + gameState.stockPile = deck; + + return gameState; } -export function isValidMove(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState) { - const sourceCard = sourcePile === 'wastePile' ? gameState[sourcePile][gameState[sourcePile].length - 1] : gameState[sourcePile][destPileIndex][gameState[sourcePile][destPileIndex].length - 1] - const destCard = destPile === 'empty' ? null : gameState[destPile][destPileIndex][gameState[destPile][destPileIndex].length - 1] +/** + * Checks if a proposed move is valid according to the rules of Klondike Solitaire. + * @param {Object} gameState - The current state of the game. + * @param {Object} moveData - The details of the move. + * @returns {boolean} + */ +export function isValidMove(gameState, moveData) { + // Use more descriptive names to avoid confusion + const { sourcePileType, sourcePileIndex, sourceCardIndex, destPileType, destPileIndex } = moveData; - console.log(sourceCard) - console.log(destCard) - - if (sourcePile.startsWith('tableauPiles')) { - if (destPile.startsWith('tableauPiles')) { - if (destCard === null && sourceCard.rank === 'K') return true - if (destCard === null) return false - const sourceRankValue = getRankValue(sourceCard.rank) - const destRankValue = getRankValue(destCard.rank) - - const sourceColor = (sourceCard.suit === 'd' || sourceCard.suit === 'h') ? 'red' : 'black' - const destColor = (destCard.suit === 'd' || destCard.suit === 'h') ? 'red' : 'black' - - console.log('tab -> tab') - console.log({sourceRankValue, sourceColor}) - console.log({destRankValue, destColor}) - - return (destRankValue - sourceRankValue === 1 && sourceColor !== destColor) - } - else if (destPile.startsWith('foundationPiles')) { - if (destCard === null && sourceCard.rank === 'A') return true - if (destCard === null) return false - - console.log('tab -> found') - console.log([sourceCard.suit, getRankValue(sourceCard.rank)]) - console.log([destCard.suit, getRankValue(destCard.rank)]) - - return (sourceCard.suit === destCard.suit && getRankValue(sourceCard.rank) - getRankValue(destCard.rank) === -1) - } - return false - } - - if (sourcePile === 'wastePile') { - if (destPile.startsWith('tableauPiles')) { - if (destCard === null && sourceCard.rank === 'K') return true - if (destCard === null) return false - const sourceRankValue = getRankValue(sourceCard.rank) - const destRankValue = getRankValue(destCard.rank) - - const sourceColor = (sourceCard.suit === 'd' || sourceCard.suit === 'h') ? 'red' : 'black' - const destColor = (destCard.suit === 'd' || destCard.suit === 'h') ? 'red' : 'black' - - console.log('waste -> tab') - console.log({sourceRankValue, sourceColor}) - console.log({destRankValue, destColor}) - - return (destRankValue - sourceRankValue === 1 && sourceColor !== destColor) - } - else if (destPile.startsWith('foundationPiles')) { - if (destCard === null && sourceCard.rank === 'A') return true - if (destCard === null) return false - - console.log('waste -> found') - console.log([sourceCard.suit, getRankValue(sourceCard.rank)]) - console.log([destCard.suit, getRankValue(destCard.rank)]) - - return (sourceCard.suit === destCard.suit && getRankValue(sourceCard.rank) - getRankValue(destCard.rank) === -1) - } - return false - } - return false -} - -export function getRankValue(rank) { - switch (rank) { - case 'A': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'T': return 10; - case 'J': return 11; - case 'Q': return 12; - case 'K': return 13; - default: return 0; - } -} - -export function moveCard(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState) { - const card = sourcePile === 'wastePile' ? gameState[sourcePile].splice(sourceCardIndex, 1)[0] : gameState[sourcePile].splice(gameState[sourcePile].length - 1, 1)[0]; - - if (destPile === 'empty') { - gameState[destPile] = [card] - } else if (destPile.startsWith('tableauPiles')) { - gameState[destPile].push(card); + let sourcePile; + // Get the actual source pile array based on its type and index + if (sourcePileType === 'tableauPiles') { + sourcePile = gameState.tableauPiles[sourcePileIndex]; + } else if (sourcePileType === 'wastePile') { + sourcePile = gameState.wastePile; } else { - gameState[destPile].push(card); + return false; // Cannot drag from foundation or stock } - if (sourcePile.startsWith('tableauPiles') && gameState[sourcePile].length > 0 ) { - gameState[sourcePile][gameState[sourcePile].length - 1].faceUp = true; + // Get the actual card being dragged (the top of the stack) + const sourceCard = sourcePile[sourceCardIndex]; + + // A card must exist and be face-up to be moved + if (!sourceCard || !sourceCard.faceUp) { + return false; + } + + // --- Validate move TO a Tableau Pile --- + if (destPileType === 'tableauPiles') { + const destinationPile = gameState.tableauPiles[destPileIndex]; + const topCard = destinationPile.length > 0 ? destinationPile[destinationPile.length - 1] : null; + + if (!topCard) { + // If the destination tableau pile is empty, only a King can be moved there. + return sourceCard.rank === 'K'; + } + + // If the destination pile is not empty, check game rules + const sourceColor = getCardColor(sourceCard.suit); + const destColor = getCardColor(topCard.suit); + const sourceValue = getRankValue(sourceCard.rank); + const destValue = getRankValue(topCard.rank); + + // Card being moved must be opposite color and one rank lower than the destination top card. + return sourceColor !== destColor && destValue - sourceValue === 1; + } + + // --- Validate move TO a Foundation Pile --- + if (destPileType === 'foundationPiles') { + // You can only move one card at a time to a foundation pile. + const stackBeingMoved = sourcePile.slice(sourceCardIndex); + if (stackBeingMoved.length > 1) { + return false; + } + + const destinationPile = gameState.foundationPiles[destPileIndex]; + const topCard = destinationPile.length > 0 ? destinationPile[destinationPile.length - 1] : null; + + if (!topCard) { + // If the foundation is empty, only an Ace can be moved there. + return sourceCard.rank === 'A'; + } + + // If not empty, card must be same suit and one rank higher. + const sourceValue = getRankValue(sourceCard.rank); + const destValue = getRankValue(topCard.rank); + + return sourceCard.suit === topCard.suit && sourceValue - destValue === 1; + } + + return false; +} + +/** + * An array of suits and ranks to create a deck. + */ +const SUITS = ['h', 'd', 's', 'c']; // Hearts, Diamonds, Spades, Clubs +const RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K']; + +/** + * Gets the numerical value of a card's rank. + * @param {string} rank - e.g., 'A', 'K', '7' + * @returns {number} + */ +function getRankValue(rank) { + if (rank === 'A') return 1; + if (rank === 'T') return 10; + if (rank === 'J') return 11; + if (rank === 'Q') return 12; + if (rank === 'K') return 13; + return parseInt(rank, 10); +} + +/** + * Gets the color of a card's suit. + * @param {string} suit - e.g., 'h', 's' + * @returns {string} 'red' or 'black' + */ +function getCardColor(suit) { + return suit === 'h' || suit === 'd' ? 'red' : 'black'; +} + +/** + * Creates a standard 52-card deck. + * @returns {Array} + */ +export function createDeck() { + const deck = []; + for (const suit of SUITS) { + for (const rank of RANKS) { + deck.push({ suit, rank, faceUp: false }); + } + } + return deck; +} + +/** + * Mutates the game state by performing a valid move, correctly handling stacks. + * @param {Object} gameState - The current state of the game. + * @param {Object} moveData - The details of the move. + */ +export function moveCard(gameState, moveData) { + const { sourcePileType, sourcePileIndex, sourceCardIndex, destPileType, destPileIndex } = moveData; + + // Identify the source pile array + const sourcePile = sourcePileType === 'tableauPiles' + ? gameState.tableauPiles[sourcePileIndex] + : gameState.wastePile; + + // Identify the destination pile array + const destPile = destPileType === 'tableauPiles' + ? gameState.tableauPiles[destPileIndex] + : gameState.foundationPiles[destPileIndex]; + + // Using splice(), cut the entire stack of cards to be moved from the source pile. + const cardsToMove = sourcePile.splice(sourceCardIndex); + + // Add the stack of cards to the destination pile. + // Using the spread operator (...) to add all items from the cardsToMove array. + destPile.push(...cardsToMove); + + // After moving, if the source was a tableau pile and it's not empty, + // flip the new top card to be face-up. + if (sourcePileType === 'tableauPiles' && sourcePile.length > 0) { + sourcePile[sourcePile.length - 1].faceUp = true; } } +/** + * Moves a card from the stock to the waste pile. If stock is empty, resets it from the waste. + * @param {Object} gameState - The current state of the game. + */ export function drawCard(gameState) { if (gameState.stockPile.length > 0) { - const card = gameState.stockPile.shift(); + const card = gameState.stockPile.pop(); card.faceUp = true; gameState.wastePile.push(card); - } else { - gameState.stockPile = gameState.wastePile.reverse().map(card => { - card.faceUp = false; - return card; - }); + } else if (gameState.wastePile.length > 0) { + // When stock is empty, move waste pile back to stock, face down + gameState.stockPile = gameState.wastePile.reverse(); + gameState.stockPile.forEach(card => (card.faceUp = false)); gameState.wastePile = []; } } +/** + * Checks if the game has been won (all cards are in the foundation piles). + * @param {Object} gameState - The current state of the game. + * @returns {boolean} + */ export function checkWinCondition(gameState) { - for (const pile of gameState.foundationPiles) { - if (pile.length !== 13) { - return false; - } - } - return true; + const foundationCardCount = gameState.foundationPiles.reduce( + (acc, pile) => acc + pile.length, + 0 + ); + return foundationCardCount === 52; } \ No newline at end of file diff --git a/index.js b/index.js index cf22fff..7e6774c 100644 --- a/index.js +++ b/index.js @@ -27,7 +27,7 @@ import { pokerEloHandler, randomSkinPrice, slowmodesHandler, - deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, + deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, createDeck, } from './game.js'; import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import cron from 'node-cron'; @@ -4456,65 +4456,70 @@ app.post('/solitaire/start', async (req, res) => { res.json({ success: true, gameState }); }); +/** + * GET /solitaire/state/:userId + * Gets the current game state for a user. If no game exists, creates a new one. + */ app.get('/solitaire/state/:userId', (req, res) => { - const userId = req.params.userId; + const { userId } = req.params; let gameState = activeSolitaireGames[userId]; - if (!gameState) { - const deck = createDeck(); + console.log(`Creating new Solitaire game for user: ${userId}`); + const deck = shuffle(createDeck()); gameState = deal(deck); activeSolitaireGames[userId] = gameState; - - console.log(`New Solitaire game created for user ${userId}`); } - res.json({ success: true, gameState }); }); +/** + * POST /solitaire/move + * Receives all necessary move data from the frontend. + */ app.post('/solitaire/move', (req, res) => { - const { userId, sourcePile, sourceCardIndex, destPile, destPileIndex } = req.body; + // Destructure the complete move data from the request body + // Frontend must send all these properties. + const { + userId, + sourcePileType, + sourcePileIndex, + sourceCardIndex, + destPileType, + destPileIndex + } = req.body; + const gameState = activeSolitaireGames[userId]; if (!gameState) { - return res.status(404).json({ error: "Game not found" }); + return res.status(404).json({ error: 'Game not found for this user.' }); } - const valid = isValidMove(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState); - - if (valid) { - moveCard(sourcePile, sourceCardIndex, destPile, destPileIndex, gameState); + // Pass the entire data object to the validation function + if (isValidMove(gameState, req.body)) { + // If valid, mutate the state + moveCard(gameState, req.body); const win = checkWinCondition(gameState); res.json({ success: true, gameState, win }); } else { - res.status(400).json({ error: "Invalid move" }); + // If the move is invalid, send a specific error message + res.status(400).json({ error: 'Invalid move' }); } }); +/** + * POST /solitaire/draw + * Draws a card from the stock pile to the waste pile. + */ app.post('/solitaire/draw', (req, res) => { const { userId } = req.body; const gameState = activeSolitaireGames[userId]; - if (!gameState) { - return res.status(404).json({ error: `Game not found for ${userId}` }); + return res.status(404).json({ error: `Game not found for user ${userId}` }); } - drawCard(gameState); - res.json({ success: true, gameState }); }); -function createDeck() { - const suits = ['c', 'd', 'h', 's']; - const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']; - const deck = []; - for (const suit of suits) { - for (const rank of ranks) { - deck.push({ suit, rank, image: `${rank}${suit}.png`, faceUp: false }); - } - } - return shuffle(deck); -} - import http from 'http'; import { Server } from 'socket.io'; import * as test from "node:test"; From 606f6e4a7323c680dac1ec8b063e0be24a231aba Mon Sep 17 00:00:00 2001 From: milo Date: Tue, 22 Jul 2025 20:06:55 +0200 Subject: [PATCH 3/3] solitaire working (w/o reward) --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7e6774c..6d12d54 100644 --- a/index.js +++ b/index.js @@ -4450,7 +4450,7 @@ async function updatePokerPlayersSolve(roomId) { app.post('/solitaire/start', async (req, res) => { const userId = req.body.userId; - const deck = createDeck(); + const deck = shuffle(createDeck()); const gameState = deal(deck); activeSolitaireGames[userId] = gameState res.json({ success: true, gameState });