mirror of
https://github.com/cassoule/flopobot_v2.git
synced 2026-03-18 21:40:27 +01:00
upgraded solitaire, SOTD
This commit is contained in:
36
game.js
36
game.js
@@ -10,7 +10,7 @@ import {
|
||||
getUserElo,
|
||||
insertElos,
|
||||
updateElo,
|
||||
getAllSkins
|
||||
getAllSkins, deleteSOTD, insertSOTD
|
||||
} from './init_database.js'
|
||||
import {C4_COLS, C4_ROWS, skins} from "./index.js";
|
||||
|
||||
@@ -542,7 +542,7 @@ export function createDeck() {
|
||||
* @param {Object} gameState - The current state of the game.
|
||||
* @param {Object} moveData - The details of the move.
|
||||
*/
|
||||
export function moveCard(gameState, moveData) {
|
||||
export async function moveCard(gameState, moveData) {
|
||||
const { sourcePileType, sourcePileIndex, sourceCardIndex, destPileType, destPileIndex } = moveData;
|
||||
|
||||
// Identify the source pile array
|
||||
@@ -570,6 +570,14 @@ export function moveCard(gameState, moveData) {
|
||||
// Using the spread operator (...) to add all items from the cardsToMove array.
|
||||
destPile.push(...cardsToMove);
|
||||
|
||||
if (sourcePileType === 'foundationPiles') {
|
||||
sotdMoveUpdate(gameState, -15)
|
||||
} else if (destPileType === 'foundationPiles') {
|
||||
sotdMoveUpdate(gameState, 10)
|
||||
} else {
|
||||
sotdMoveUpdate(gameState, 0)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -581,7 +589,7 @@ export function moveCard(gameState, moveData) {
|
||||
* 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) {
|
||||
export async function drawCard(gameState) {
|
||||
if (gameState.stockPile.length > 0) {
|
||||
const card = gameState.stockPile.pop();
|
||||
card.faceUp = true;
|
||||
@@ -592,6 +600,7 @@ export function drawCard(gameState) {
|
||||
gameState.stockPile.forEach(card => (card.faceUp = false));
|
||||
gameState.wastePile = [];
|
||||
}
|
||||
sotdMoveUpdate(gameState, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,4 +614,25 @@ export function checkWinCondition(gameState) {
|
||||
0
|
||||
);
|
||||
return foundationCardCount === 52;
|
||||
}
|
||||
|
||||
export function initTodaysSOTD() {
|
||||
const deck = shuffle(createDeck());
|
||||
const todaysSOTD = deal(deck)
|
||||
deleteSOTD.run()
|
||||
insertSOTD.run({
|
||||
id: 0,
|
||||
tableauPiles: JSON.stringify(todaysSOTD.tableauPiles),
|
||||
foundationPiles: JSON.stringify(todaysSOTD.foundationPiles),
|
||||
stockPile: JSON.stringify(todaysSOTD.stockPile),
|
||||
wastePile: JSON.stringify(todaysSOTD.wastePile),
|
||||
})
|
||||
console.log('Today\' SOTD is ready')
|
||||
}
|
||||
|
||||
export function sotdMoveUpdate(gameState, points) {
|
||||
if (gameState.isSOTD) {
|
||||
gameState.moves++
|
||||
gameState.score += points
|
||||
}
|
||||
}
|
||||
80
index.js
80
index.js
@@ -27,7 +27,7 @@ import {
|
||||
pokerEloHandler,
|
||||
randomSkinPrice,
|
||||
slowmodesHandler,
|
||||
deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, createDeck,
|
||||
deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, createDeck, initTodaysSOTD,
|
||||
} from './game.js';
|
||||
import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
||||
import cron from 'node-cron';
|
||||
@@ -50,9 +50,19 @@ import {
|
||||
getSkin,
|
||||
getAllAvailableSkins,
|
||||
getUserInventory,
|
||||
getTopSkins, updateUserCoins,
|
||||
insertLog, stmtLogs,
|
||||
getLogs, getUserLogs, getUserElo, getUserGames, getUsersByElo, resetDailyReward, queryDailyReward,
|
||||
getTopSkins,
|
||||
updateUserCoins,
|
||||
insertLog,
|
||||
stmtLogs,
|
||||
getLogs,
|
||||
getUserLogs,
|
||||
getUserElo,
|
||||
getUserGames,
|
||||
getUsersByElo,
|
||||
resetDailyReward,
|
||||
queryDailyReward,
|
||||
deleteSOTD,
|
||||
insertSOTD, getSOTD,
|
||||
} from './init_database.js';
|
||||
import { getValorantSkins, getSkinTiers } from './valo.js';
|
||||
import {sleep} from "openai/core";
|
||||
@@ -81,7 +91,7 @@ const activeInventories = {};
|
||||
const activeSearchs = {};
|
||||
const activeSlowmodes = {};
|
||||
const activePredis = {};
|
||||
let todaysHydrateCron = ''
|
||||
let todaysSOTD = {};
|
||||
const SPAM_INTERVAL = process.env.SPAM_INTERVAL
|
||||
|
||||
const client = new Client({
|
||||
@@ -548,6 +558,9 @@ client.on('messageCreate', async (message) => {
|
||||
}
|
||||
console.log(`Result for ${amount} skins`)
|
||||
}
|
||||
else if (message.content.toLowerCase().startsWith('?sotd')) {
|
||||
initTodaysSOTD()
|
||||
}
|
||||
else if (message.author.id === process.env.DEV_ID) {
|
||||
const prefix = process.env.DEV_SITE === 'true' ? 'dev' : 'flopo'
|
||||
if (message.content === prefix + ':add-coins-to-users') {
|
||||
@@ -635,10 +648,6 @@ client.on('messageCreate', async (message) => {
|
||||
client.once('ready', async () => {
|
||||
console.log(`Logged in as ${client.user.tag}`);
|
||||
console.log(`[Connected with ${FLAPI_URL}]`)
|
||||
const randomMinute = Math.floor(Math.random() * 60);
|
||||
const randomHour = Math.floor(Math.random() * (18 - 8 + 1)) + 8;
|
||||
todaysHydrateCron = `${randomMinute} ${randomHour} * * *`
|
||||
console.log(todaysHydrateCron)
|
||||
await getAkhys();
|
||||
console.log('FlopoBOT marked as ready')
|
||||
|
||||
@@ -683,13 +692,8 @@ client.once('ready', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// ─── 💀 Midnight Chaos Timer ──────────────────────
|
||||
// at midnight
|
||||
cron.schedule(process.env.CRON_EXPR, async () => {
|
||||
const randomMinute = Math.floor(Math.random() * 60);
|
||||
const randomHour = Math.floor(Math.random() * (18 - 8 + 1)) + 8;
|
||||
todaysHydrateCron = `${randomMinute} ${randomHour} * * *`
|
||||
console.log(todaysHydrateCron)
|
||||
|
||||
try {
|
||||
const akhys = getAllUsers.all()
|
||||
akhys.forEach((akhy) => {
|
||||
@@ -698,6 +702,8 @@ client.once('ready', async () => {
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
initTodaysSOTD()
|
||||
});
|
||||
|
||||
// users/skins dayly fetch at 7am
|
||||
@@ -4460,6 +4466,33 @@ app.post('/solitaire/start', async (req, res) => {
|
||||
res.json({ success: true, gameState });
|
||||
});
|
||||
|
||||
app.post('/solitaire/start/sotd', async (req, res) => {
|
||||
const userId = req.body.userId
|
||||
const sotd = getSOTD.get()
|
||||
|
||||
const gameState = {
|
||||
tableauPiles: JSON.parse(sotd.tableauPiles),
|
||||
foundationPiles: JSON.parse(sotd.foundationPiles),
|
||||
stockPile: JSON.parse(sotd.stockPile),
|
||||
wastePile: JSON.parse(sotd.wastePile),
|
||||
isDone: false,
|
||||
isSOTD: true,
|
||||
hasFinToday: false,
|
||||
startTime: Date.now(),
|
||||
moves: 0,
|
||||
score: 0,
|
||||
}
|
||||
|
||||
activeSolitaireGames[userId] = gameState
|
||||
res.json({ success: true, gameState });
|
||||
})
|
||||
|
||||
app.post('/solitaire/reset', async (req, res) => {
|
||||
const userId = req.body.userId;
|
||||
delete activeSolitaireGames[userId]
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /solitaire/state/:userId
|
||||
* Gets the current game state for a user. If no game exists, creates a new one.
|
||||
@@ -4467,12 +4500,11 @@ app.post('/solitaire/start', async (req, res) => {
|
||||
app.get('/solitaire/state/:userId', (req, res) => {
|
||||
const { userId } = req.params;
|
||||
let gameState = activeSolitaireGames[userId];
|
||||
if (!gameState) {
|
||||
console.log(`Creating new Solitaire game for user: ${userId}`);
|
||||
/*if (!gameState) {
|
||||
const deck = shuffle(createDeck());
|
||||
gameState = deal(deck);
|
||||
activeSolitaireGames[userId] = gameState;
|
||||
}
|
||||
}*/
|
||||
res.json({ success: true, gameState });
|
||||
});
|
||||
|
||||
@@ -4480,7 +4512,7 @@ app.get('/solitaire/state/:userId', (req, res) => {
|
||||
* POST /solitaire/move
|
||||
* Receives all necessary move data from the frontend.
|
||||
*/
|
||||
app.post('/solitaire/move', (req, res) => {
|
||||
app.post('/solitaire/move', async (req, res) => {
|
||||
// Destructure the complete move data from the request body
|
||||
// Frontend must send all these properties.
|
||||
const {
|
||||
@@ -4501,9 +4533,11 @@ app.post('/solitaire/move', (req, res) => {
|
||||
// Pass the entire data object to the validation function
|
||||
if (isValidMove(gameState, req.body)) {
|
||||
// If valid, mutate the state
|
||||
moveCard(gameState, req.body);
|
||||
await moveCard(gameState, req.body);
|
||||
const win = checkWinCondition(gameState);
|
||||
if (win) gameState.isDone = true
|
||||
if (win) {
|
||||
gameState.isDone = true
|
||||
}
|
||||
res.json({ success: true, gameState, win });
|
||||
} else {
|
||||
// If the move is invalid, send a specific error message
|
||||
@@ -4515,13 +4549,13 @@ app.post('/solitaire/move', (req, res) => {
|
||||
* POST /solitaire/draw
|
||||
* Draws a card from the stock pile to the waste pile.
|
||||
*/
|
||||
app.post('/solitaire/draw', (req, res) => {
|
||||
app.post('/solitaire/draw', async (req, res) => {
|
||||
const { userId } = req.body;
|
||||
const gameState = activeSolitaireGames[userId];
|
||||
if (!gameState) {
|
||||
return res.status(404).json({ error: `Game not found for user ${userId}` });
|
||||
}
|
||||
drawCard(gameState);
|
||||
await drawCard(gameState);
|
||||
res.json({ success: true, gameState });
|
||||
});
|
||||
|
||||
|
||||
@@ -120,4 +120,20 @@ export const getUserElo = flopoDB.prepare(`SELECT * FROM elos WHERE id = @id`);
|
||||
export const updateElo = flopoDB.prepare('UPDATE elos SET elo = @elo WHERE id = @id');
|
||||
|
||||
|
||||
export const getUsersByElo = flopoDB.prepare('SELECT * FROM users JOIN elos ON elos.id = users.id ORDER BY elos.elo DESC')
|
||||
export const getUsersByElo = flopoDB.prepare('SELECT * FROM users JOIN elos ON elos.id = users.id ORDER BY elos.elo DESC')
|
||||
|
||||
export const stmtSOTD = flopoDB.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS sotd (
|
||||
id INT PRIMARY KEY,
|
||||
tableauPiles TEXT,
|
||||
foundationPiles TEXT,
|
||||
stockPile TEXT,
|
||||
wastePile TEXT,
|
||||
isDone BOOLEAN DEFAULT false
|
||||
)
|
||||
`);
|
||||
stmtSOTD.run()
|
||||
|
||||
export const getSOTD = flopoDB.prepare(`SELECT * FROM sotd WHERE id = '0'`)
|
||||
export const insertSOTD = flopoDB.prepare(`INSERT INTO sotd (id, tableauPiles, foundationPiles, stockPile, wastePile) VALUES (@id, @tableauPiles, @foundationPiles, @stockPile, @wastePile)`)
|
||||
export const deleteSOTD = flopoDB.prepare(`DELETE FROM sotd WHERE id = '0'`)
|
||||
Reference in New Issue
Block a user