we're about to port the chrome-extension. everything else mostly works

This commit is contained in:
cottongin
2025-10-30 13:27:55 -04:00
parent 2db707961c
commit db2a8abe66
29 changed files with 2490 additions and 562 deletions

View File

@@ -3,6 +3,68 @@ const db = require('../database');
const router = express.Router();
/**
* Select a game using weighted random selection based on favor bias
*
* Bias system:
* - favor_bias = 1 (favored): 3x weight
* - favor_bias = 0 (neutral): 1x weight
* - favor_bias = -1 (disfavored): 0.2x weight (still possible but less likely)
*
* Also considers pack-level bias
*/
function selectGameWithBias(games) {
if (games.length === 0) return null;
if (games.length === 1) return games[0];
// Get pack biases
const packs = db.prepare('SELECT name, favor_bias FROM packs').all();
const packBiasMap = {};
packs.forEach(pack => {
packBiasMap[pack.name] = pack.favor_bias || 0;
});
// Calculate weights for each game
const weights = games.map(game => {
let weight = 1.0; // Base weight
// Apply game-level bias
const gameBias = game.favor_bias || 0;
if (gameBias === 1) {
weight *= 3.0; // Favored games are 3x more likely
} else if (gameBias === -1) {
weight *= 0.2; // Disfavored games are 5x less likely
}
// Apply pack-level bias
const packBias = packBiasMap[game.pack_name] || 0;
if (packBias === 1) {
weight *= 2.0; // Favored packs are 2x more likely
} else if (packBias === -1) {
weight *= 0.3; // Disfavored packs are ~3x less likely
}
return weight;
});
// Calculate total weight
const totalWeight = weights.reduce((sum, w) => sum + w, 0);
// Pick a random number between 0 and totalWeight
let random = Math.random() * totalWeight;
// Select game based on weighted random
for (let i = 0; i < games.length; i++) {
random -= weights[i];
if (random <= 0) {
return games[i];
}
}
// Fallback (shouldn't reach here)
return games[games.length - 1];
}
// Pick a random game with filters and repeat avoidance
router.post('/pick', (req, res) => {
try {
@@ -11,7 +73,8 @@ router.post('/pick', (req, res) => {
drawing,
length,
familyFriendly,
sessionId
sessionId,
excludePlayed
} = req.body;
// Build query for eligible games
@@ -60,14 +123,25 @@ router.post('/pick', (req, res) => {
// Apply repeat avoidance if session provided
if (sessionId) {
const lastGames = db.prepare(`
SELECT game_id FROM session_games
WHERE session_id = ?
ORDER BY played_at DESC
LIMIT 2
`).all(sessionId);
let lastGamesQuery;
if (excludePlayed) {
// Exclude ALL previously played games in this session
lastGamesQuery = db.prepare(`
SELECT DISTINCT game_id FROM session_games
WHERE session_id = ?
`).all(sessionId);
} else {
// Default: only exclude last 2 games
lastGamesQuery = db.prepare(`
SELECT game_id FROM session_games
WHERE session_id = ?
ORDER BY played_at DESC
LIMIT 2
`).all(sessionId);
}
const excludeIds = lastGames.map(g => g.game_id);
const excludeIds = lastGamesQuery.map(g => g.game_id);
if (excludeIds.length > 0) {
eligibleGames = eligibleGames.filter(game => !excludeIds.includes(game.id));
@@ -75,16 +149,17 @@ router.post('/pick', (req, res) => {
if (eligibleGames.length === 0) {
return res.status(404).json({
error: 'All eligible games have been played recently',
error: excludePlayed
? 'All eligible games have been played in this session'
: 'All eligible games have been played recently',
suggestion: 'Enable more games or adjust your filters',
recentlyPlayed: excludeIds
});
}
}
// Pick random game from eligible pool
const randomIndex = Math.floor(Math.random() * eligibleGames.length);
const selectedGame = eligibleGames[randomIndex];
// Apply favor biasing to selection
const selectedGame = selectGameWithBias(eligibleGames);
res.json({
game: selectedGame,