Two teams try and fill in any horizontal, vertical, or diagonal line on a bingo board by playing maps on osu! osu.bingo
osu
at microservice 121 lines 3.5 kB view raw
1/** 2 * Starts a bingo game! 3 */ 4 5import q from '$lib/drizzle/queries'; 6import { addGame } from './watch'; 7import boards from '$lib/bingo-helpers/default_boards'; 8import { logger } from '$lib/logger'; 9import { sendToGame } from '$lib/emitter/server'; 10import { clearTimer } from './auto_deletion'; 11import type { Options } from '$lib/gamerules/options'; 12 13export const startGame = async (game_id: string) => { 14 logger.info(`Starting game ${game_id}!`, { type: 'game_start' }); 15 const now = Date.now(); 16 clearTimer(game_id); 17 18 const game = await q.getGame(game_id); 19 if (!game) return; 20 21 // Merge options from template with game-specific options 22 const template: Options = JSON.parse(game.template.data); 23 const gameOptions: Options = JSON.parse(game.options); 24 25 const options = { ...template, ...gameOptions, setup: { ...template.setup, ...gameOptions.setup } } 26 27 // Grab board from default boards if necessary 28 const board = 29 typeof options.board == 'string' ? boards[options.board] : options.board; 30 options.board = board; 31 32 await q.updateGameOptions(game_id, options); 33 34 // =========== 35 // MAP PICKING 36 // =========== 37 38 39 // Group squares via block 40 const blocks: { block: Options.Block, squares: [number, number][] }[] = [] 41 for (let i = 0; i < board.squares.length; i++) { 42 let block = board.blocks?.find(x => x.indices.includes(i))?.block; 43 if (!block) block = options.setup; 44 45 const blocksIdx = blocks.findIndex(x => x.block == block); 46 if (blocksIdx != -1) { 47 blocks[blocksIdx].squares.push(board.squares[i]); 48 } else { 49 blocks.push({ block, squares: [board.squares[i]] }) 50 } 51 } 52 53 // Pick maps in each block 54 for (const { block, squares } of blocks) { 55 const totalChance = block.maps.reduce((a, b) => a + (b.chance ?? 1), 0); 56 57 // Calculate how many maps in each pool to fetch 58 const counts = []; 59 for (const mappool of block.maps) { 60 const chance = (mappool.chance ?? 1) / totalChance; 61 counts.push({ 62 mappool, 63 maps: Math.floor(squares.length * chance), 64 }); 65 } 66 67 // If the count doesn't add up to the number of squares, add the difference to the smallest pool 68 if (counts.map((x) => x.maps).reduce((a, b) => a + b) < squares.length) { 69 counts.sort((a, b) => a.maps - b.maps); 70 counts[0].maps += squares.length - counts.map((x) => x.maps).reduce((a, b) => a + b); 71 } 72 73 // Fetch maps in all the pools 74 const maps = []; 75 for (const pool of counts) { 76 if (pool.maps == 0) continue; 77 78 const picks = await q.getRandomMaps( 79 pool.mappool.mappool_id, 80 pool.maps, 81 // Pick block's settings, or fallback to default block's settings 82 block.stars?.min ?? options.setup.stars?.min, 83 block.stars?.max ?? options.setup.stars?.max, 84 block.length?.min ?? options.setup.length?.min, 85 block.length?.max ?? options.setup.length?.max, 86 pool.mappool.mode 87 ); 88 maps.push(...picks); 89 } 90 91 // Shuffle maps 92 maps.sort(() => (Math.random() > 0.5 ? -1 : 1)); 93 for (let i = 0; i < squares.length; i++) { 94 const square = squares[i]; 95 await q.newSquare({ 96 game_id, 97 map_id: maps[i].id, 98 x_pos: square[0], 99 y_pos: square[1] 100 }); 101 } 102 } 103 104 // Create Events 105 for (const event of template.event) { 106 const date = new Date(now + event.seconds_after_start * 1000); 107 await q.setEvent(game_id, event.event, date); 108 } 109 110 // Update last settings and send game to clients 111 await q.setGameState(game.id, 1); 112 await q.setStartTime(game.id, new Date(now)); 113 addGame(game_id); 114 sendToGame(game_id, { 115 type: 'state', 116 data: { 117 state: 1, 118 card: await q.getGame(game_id) 119 } 120 }); 121};