mirror of
https://github.com/cassoule/flopobot_v2.git
synced 2026-03-18 21:40:27 +01:00
@@ -9,7 +9,7 @@ function createTimesChoices() {
|
||||
for (let choice of choices) {
|
||||
commandChoices.push({
|
||||
name: capitalize(choice.name),
|
||||
value: choice.value.toString(),
|
||||
value: choice.value?.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
115
game.js
115
game.js
@@ -10,7 +10,7 @@ import {
|
||||
getUserElo,
|
||||
insertElos,
|
||||
updateElo,
|
||||
getAllSkins
|
||||
getAllSkins, deleteSOTD, insertSOTD, clearSOTDStats, getAllSOTDStats
|
||||
} from './init_database.js'
|
||||
import {C4_COLS, C4_ROWS, skins} from "./index.js";
|
||||
|
||||
@@ -164,14 +164,14 @@ export async function eloHandler(p1, p2, p1score, p2score, type) {
|
||||
|
||||
if (!p1elo) {
|
||||
await insertElos.run({
|
||||
id: p1.toString(),
|
||||
id: p1?.toString(),
|
||||
elo: 100,
|
||||
})
|
||||
p1elo = await getUserElo.get({ id: p1 })
|
||||
}
|
||||
if (!p2elo) {
|
||||
await insertElos.run({
|
||||
id: p2.toString(),
|
||||
id: p2?.toString(),
|
||||
elo: 100,
|
||||
})
|
||||
p2elo = await getUserElo.get({ id: p2 })
|
||||
@@ -179,7 +179,7 @@ export async function eloHandler(p1, p2, p1score, p2score, type) {
|
||||
|
||||
if (p1score === p2score) {
|
||||
insertGame.run({
|
||||
id: p1.toString() + '-' + p2.toString() + '-' + Date.now().toString(),
|
||||
id: p1?.toString() + '-' + p2?.toString() + '-' + Date.now()?.toString(),
|
||||
p1: p1,
|
||||
p2: p2,
|
||||
p1_score: p1score,
|
||||
@@ -206,7 +206,7 @@ export async function eloHandler(p1, p2, p1score, p2score, type) {
|
||||
updateElo.run({ id: p2, elo: p2newElo })
|
||||
|
||||
insertGame.run({
|
||||
id: p1.toString() + '-' + p2.toString() + '-' + Date.now().toString(),
|
||||
id: p1?.toString() + '-' + p2?.toString() + '-' + Date.now()?.toString(),
|
||||
p1: p1,
|
||||
p2: p2,
|
||||
p1_score: p1score,
|
||||
@@ -263,7 +263,7 @@ export async function pokerEloHandler(room) {
|
||||
updateElo.run({ id: player.id, elo: newElo })
|
||||
|
||||
insertGame.run({
|
||||
id: player.id + '-' + Date.now().toString(),
|
||||
id: player.id + '-' + Date.now()?.toString(),
|
||||
p1: player.id,
|
||||
p2: null,
|
||||
p1_score: actualScore,
|
||||
@@ -368,6 +368,20 @@ export function formatConnect4BoardForDiscord(board) {
|
||||
return board.map(row => row.map(cell => symbols[cell]).join('')).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a seedable pseudorandom number generator (PRNG) using the Mulberry32 algorithm.
|
||||
* @param {number} seed - An initial number to seed the generator.
|
||||
* @returns {function} A function that, when called, returns a pseudorandom number between 0 and 1.
|
||||
*/
|
||||
export function createSeededRNG(seed) {
|
||||
return function() {
|
||||
let t = seed += 0x6D2B79F5;
|
||||
t = Math.imul(t ^ t >>> 15, t | 1);
|
||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
||||
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles an array in place using the Fisher-Yates algorithm.
|
||||
* @param {Array} array - The array to shuffle.
|
||||
@@ -384,6 +398,31 @@ export function shuffle(array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles an array in place using a seedable PRNG via the Fisher-Yates algorithm.
|
||||
* @param {Array} array - The array to shuffle.
|
||||
* @param {function} rng - A seedable random number generator function.
|
||||
* @returns {Array} The shuffled array.
|
||||
*/
|
||||
export function seededShuffle(array, rng) {
|
||||
let currentIndex = array.length,
|
||||
randomIndex;
|
||||
|
||||
// While there remain elements to shuffle.
|
||||
while (currentIndex !== 0) {
|
||||
// Pick a remaining element using the seeded RNG.
|
||||
randomIndex = Math.floor(rng() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
// And swap it with the current element.
|
||||
[array[currentIndex], array[randomIndex]] = [
|
||||
array[randomIndex],
|
||||
array[currentIndex],
|
||||
];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals a shuffled deck into the initial Solitaire game state.
|
||||
* @param {Array} deck - A shuffled deck of cards.
|
||||
@@ -542,7 +581,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 +609,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 +628,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 +639,7 @@ export function drawCard(gameState) {
|
||||
gameState.stockPile.forEach(card => (card.faceUp = false));
|
||||
gameState.wastePile = [];
|
||||
}
|
||||
sotdMoveUpdate(gameState, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,4 +653,55 @@ export function checkWinCondition(gameState) {
|
||||
0
|
||||
);
|
||||
return foundationCardCount === 52;
|
||||
}
|
||||
|
||||
export function initTodaysSOTD() {
|
||||
const rankings = getAllSOTDStats.all()
|
||||
const firstPlaceId = rankings > 0 ? rankings[0].user_id : null
|
||||
|
||||
if (firstPlaceId) {
|
||||
const firstPlaceUser = getUser.get(firstPlaceId)
|
||||
if (firstPlaceUser) {
|
||||
updateUserCoins.run({ id: firstPlaceId, coins: firstPlaceUser.coins + 1000 });
|
||||
insertLog.run({
|
||||
id: firstPlaceId + '-' + Date.now(),
|
||||
user_id: firstPlaceId,
|
||||
action: 'SOTD_FIRST_PLACE',
|
||||
target_user_id: null,
|
||||
coins_amount: 1000,
|
||||
user_new_amount: firstPlaceUser.coins + 1000,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const newRandomSeed = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
let numericSeed = 0;
|
||||
for (let i = 0; i < newRandomSeed.length; i++) {
|
||||
numericSeed = (numericSeed + newRandomSeed.charCodeAt(i)) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
const rng = createSeededRNG(numericSeed);
|
||||
const deck = createDeck();
|
||||
const shuffledDeck = seededShuffle(deck, rng);
|
||||
const todaysSOTD = deal(shuffledDeck);
|
||||
todaysSOTD.seed = newRandomSeed;
|
||||
|
||||
clearSOTDStats.run()
|
||||
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),
|
||||
seed: todaysSOTD.seed,
|
||||
})
|
||||
console.log('Today\'s SOTD is ready')
|
||||
}
|
||||
|
||||
export function sotdMoveUpdate(gameState, points) {
|
||||
if (gameState.isSOTD) {
|
||||
gameState.moves++
|
||||
gameState.score += points
|
||||
}
|
||||
}
|
||||
192
index.js
192
index.js
@@ -27,7 +27,7 @@ import {
|
||||
pokerEloHandler,
|
||||
randomSkinPrice,
|
||||
slowmodesHandler,
|
||||
deal, isValidMove, moveCard, shuffle, drawCard, checkWinCondition, createDeck,
|
||||
deal, isValidMove, moveCard, seededShuffle, drawCard, checkWinCondition, createDeck, initTodaysSOTD, createSeededRNG,
|
||||
} 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, insertSOTDStats, deleteUserSOTDStats, getUserSOTDStats, getAllSOTDStats,
|
||||
} 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
|
||||
@@ -3148,7 +3154,7 @@ app.post('/slowmode', async (req, res) => {
|
||||
return res.status(200).json({ message: 'Slowmode retiré'})
|
||||
} else {
|
||||
let timeLeft = (activeSlowmodes[userId].endAt - Date.now())/1000
|
||||
timeLeft = timeLeft > 60 ? (timeLeft/60).toFixed().toString() + 'min' : timeLeft.toFixed().toString() + 'sec'
|
||||
timeLeft = timeLeft > 60 ? (timeLeft/60).toFixed()?.toString() + 'min' : timeLeft.toFixed()?.toString() + 'sec'
|
||||
return res.status(403).json({ message: `${user.globalName} est déjà en slowmode (${timeLeft})`})
|
||||
}
|
||||
} else if (userId === commandUserId) {
|
||||
@@ -3196,7 +3202,7 @@ app.post('/start-predi', async (req, res) => {
|
||||
}
|
||||
|
||||
const startTime = Date.now()
|
||||
const newPrediId = commandUserId.toString() + '-' + startTime.toString()
|
||||
const newPrediId = commandUserId?.toString() + '-' + startTime?.toString()
|
||||
|
||||
let msgId;
|
||||
try {
|
||||
@@ -4448,16 +4454,102 @@ async function updatePokerPlayersSolve(roomId) {
|
||||
for (const playerId in pokerRooms[roomId].players) {
|
||||
const player = pokerRooms[roomId].players[playerId]
|
||||
let fullHand = pokerRooms[roomId].tapis
|
||||
player.solve = Hand.solve(fullHand.concat(player.hand), 'standard', false)?.descr
|
||||
if (!fullHand && !player.hand) {
|
||||
player.solve = Hand.solve([], 'standard', false)?.descr
|
||||
} else if (!fullHand) {
|
||||
player.solve = Hand.solve(player.hand, 'standard', false)?.descr
|
||||
} else if (!player.hand) {
|
||||
player.solve = Hand.solve(fullHand, 'standard', false)?.descr
|
||||
} else {
|
||||
player.solve = Hand.solve(fullHand.concat(player.hand), 'standard', false)?.descr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/solitaire/sotd/rankings', async (req, res) => {
|
||||
const rankings = getAllSOTDStats.all()
|
||||
|
||||
return res.json({ rankings })
|
||||
})
|
||||
|
||||
app.post('/solitaire/start', async (req, res) => {
|
||||
const userId = req.body.userId;
|
||||
const deck = shuffle(createDeck());
|
||||
const gameState = deal(deck);
|
||||
let userSeed = req.body.userSeed;
|
||||
|
||||
if (activeSolitaireGames[userId] && !activeSolitaireGames[userId].isSOTD) {
|
||||
return res.json({ succes: true, gameState: activeSolitaireGames[userId]})
|
||||
}
|
||||
|
||||
if (userSeed) {
|
||||
let numericSeed = 0
|
||||
for (let i = 0; i < userSeed.length; i++) {
|
||||
numericSeed = (numericSeed + userSeed.charCodeAt(i)) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
const rng = createSeededRNG(numericSeed);
|
||||
const deck = createDeck()
|
||||
const shuffledDeck = seededShuffle(deck, rng);
|
||||
const gameState = deal(shuffledDeck);
|
||||
gameState.seed = userSeed;
|
||||
|
||||
activeSolitaireGames[userId] = gameState;
|
||||
|
||||
return res.json({ success: true, gameState });
|
||||
} else {
|
||||
const newRandomSeed = Date.now()?.toString(36) + Math.random()?.toString(36).substr(2);
|
||||
let numericSeed = 0;
|
||||
for (let i = 0; i < newRandomSeed.length; i++) {
|
||||
numericSeed = (numericSeed + newRandomSeed.charCodeAt(i)) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
const rng = createSeededRNG(numericSeed);
|
||||
const deck = createDeck();
|
||||
const shuffledDeck = seededShuffle(deck, rng);
|
||||
const gameState = deal(shuffledDeck);
|
||||
gameState.seed = newRandomSeed;
|
||||
|
||||
activeSolitaireGames[userId] = gameState;
|
||||
|
||||
return res.json({ success: true, gameState });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/solitaire/start/sotd', async (req, res) => {
|
||||
const userId = req.body.userId
|
||||
const sotd = getSOTD.get();
|
||||
|
||||
const user = getUser.get(userId);
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (activeSolitaireGames[userId] && activeSolitaireGames[userId].isSOTD) {
|
||||
return res.json({ success: true, gameState: activeSolitaireGames[userId]})
|
||||
}
|
||||
|
||||
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(),
|
||||
endTime: null,
|
||||
moves: 0,
|
||||
score: 0,
|
||||
seed: sotd.seed,
|
||||
}
|
||||
|
||||
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 });
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -4467,12 +4559,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 +4571,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,10 +4592,53 @@ 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
|
||||
res.json({ success: true, gameState, win });
|
||||
if (win) {
|
||||
gameState.isDone = true
|
||||
if (gameState.isSOTD) {
|
||||
gameState.hasFinToday = true;
|
||||
gameState.endTime = Date.now();
|
||||
const userStats = getUserSOTDStats.get(userId);
|
||||
if (userStats) {
|
||||
if (
|
||||
(gameState.score > userStats.score) ||
|
||||
(gameState.score === userStats.score && gameState.moves < userStats.moves) ||
|
||||
(gameState.score === userStats.score && gameState.moves === userStats.moves && gameState.time < userStats.time)
|
||||
) {
|
||||
deleteUserSOTDStats.run(userId);
|
||||
insertSOTDStats.run({
|
||||
id: userId,
|
||||
user_id: userId,
|
||||
time: gameState.endTime - gameState.startTime,
|
||||
moves: gameState.moves,
|
||||
score: gameState.score,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
insertSOTDStats.run({
|
||||
id: userId,
|
||||
user_id: userId,
|
||||
time: gameState.endTime - gameState.startTime,
|
||||
moves: gameState.moves,
|
||||
score: gameState.score,
|
||||
})
|
||||
const user = getUser.get(userId)
|
||||
if (user) {
|
||||
updateUserCoins.run({ id: userId, coins: user.coins + 1000 });
|
||||
insertLog.run({
|
||||
id: userId + '-' + Date.now(),
|
||||
user_id: userId,
|
||||
action: 'SOTD_WIN',
|
||||
target_user_id: null,
|
||||
coins_amount: 1000,
|
||||
user_new_amount: user.coins + 1000,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res.json({ success: true, gameState, win, endTime: win ? Date.now() : null });
|
||||
} else {
|
||||
// If the move is invalid, send a specific error message
|
||||
res.status(400).json({ error: 'Invalid move' });
|
||||
@@ -4515,13 +4649,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,38 @@ 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,
|
||||
seed TEXT
|
||||
)
|
||||
`);
|
||||
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, seed) VALUES (@id, @tableauPiles, @foundationPiles, @stockPile, @wastePile, @seed)`)
|
||||
export const deleteSOTD = flopoDB.prepare(`DELETE FROM sotd WHERE id = '0'`)
|
||||
|
||||
export const stmtSOTDStats = flopoDB.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS sotd_stats (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT REFERENCES users,
|
||||
time INTEGER,
|
||||
moves INTEGER,
|
||||
score INTEGER
|
||||
)
|
||||
`);
|
||||
stmtSOTDStats.run()
|
||||
|
||||
export const getAllSOTDStats = flopoDB.prepare(`SELECT sotd_stats.*, users.globalName FROM sotd_stats JOIN users ON users.id = sotd_stats.user_id ORDER BY score DESC, moves ASC, time ASC`);
|
||||
export const getUserSOTDStats = flopoDB.prepare(`SELECT * FROM sotd_stats WHERE user_id = ?`);
|
||||
export const insertSOTDStats = flopoDB.prepare(`INSERT INTO sotd_stats (id, user_id, time, moves, score) VALUES (@id, @user_id, @time, @moves, @score)`);
|
||||
export const clearSOTDStats = flopoDB.prepare(`DELETE FROM sotd_stats`);
|
||||
export const deleteUserSOTDStats = flopoDB.prepare(`DELETE FROM sotd_stats WHERE user_id = ?`);
|
||||
Reference in New Issue
Block a user