基于Nodejs和MongoDB的Tambola(Bingo)游戏中奖匹配逻辑实现咨询
Tambola宾果游戏中奖判定实现方案
因为你的数据量最多只有500条,优先推荐逻辑更简单、更适合新手调试的Node业务层处理方案,完全可以满足性能需求。
前置准备
先在服务端维护一个已开出号码的Set集合,用于快速判断号码是否被开出,每次生成新的不重复随机数后存入该集合:
// 存储所有已开出的号码,查询时间复杂度O(1) const drawnNumbers = new Set()
方案1:Node业务层判定(新手推荐)
直接从MongoDB拉取全量票数据,逐个判断奖项条件即可:
const { MongoClient } = require('mongodb') const mongoUri = '替换为你的MongoDB连接地址' const client = new MongoClient(mongoUri) async function getCurrentWinners() { try { await client.connect() const ticketCol = client.db('ticketdb').collection('tickets') // 拉取所有票数据,最多500条无性能压力 const allTickets = await ticketCol.find({}).toArray() const winnerResult = { 顶线奖: [], 中线奖: [], 底线奖: [], FullHouse: [] } for (const ticket of allTickets) { const tktMatrix = ticket.tkt // 判定顶线奖:第一行所有非空值都已开出 const topLineWin = tktMatrix[0].every(num => num === '' || drawnNumbers.has(num)) if (topLineWin) winnerResult.顶线奖.push({ id: ticket._id, name: ticket.name, phone: ticket.phone_no }) // 判定中线奖:第二行所有非空值都已开出 const midLineWin = tktMatrix[1].every(num => num === '' || drawnNumbers.has(num)) if (midLineWin) winnerResult.中线奖.push({ id: ticket._id, name: ticket.name, phone: ticket.phone_no }) // 判定底线奖:第三行所有非空值都已开出 const bottomLineWin = tktMatrix[2].every(num => num === '' || drawnNumbers.has(num)) if (bottomLineWin) winnerResult.底线奖.push({ id: ticket._id, name: ticket.name, phone: ticket.phone_no }) // 判定FullHouse:所有15个号码都已开出 const allTicketNums = tktMatrix.flat().filter(num => num !== '') const fullHouseWin = allTicketNums.every(num => drawnNumbers.has(num)) if (fullHouseWin) winnerResult.FullHouse.push({ id: ticket._id, name: ticket.name, phone: ticket.phone_no }) } return winnerResult } finally { await client.close() } }
开号主逻辑
// 每5秒开号+判定中奖 setInterval(async () => { // 生成1-99不重复随机数 let newNum do { newNum = Math.floor(Math.random() * 99) + 1 } while(drawnNumbers.has(newNum) && drawnNumbers.size < 99) // 所有号码开完终止流程 if (drawnNumbers.size >= 99) { console.log('本轮游戏所有号码已开完') process.exit(0) } drawnNumbers.add(newNum) console.log('本次开出号码:', newNum) // 获取当前中奖名单 const winners = await getCurrentWinners() console.log('当前中奖名单:', winners) }, 5000)
方案优化:减少查询数据量
如果需要进一步降低性能消耗,每次开号后只查询包含该新号码的票,只处理这些票即可:
// 替换之前的全量查询语句 const relatedTickets = await ticketCol.find({ $or: [ {'tkt.0': newNum}, {'tkt.1': newNum}, {'tkt.2': newNum} ] }).toArray()
方案2:MongoDB聚合判定(进阶可选)
如果需要在数据库层直接完成判定,可以使用以下聚合语句,调用时把已开出的Set转成数组传入即可:
async function getWinnersByAgg(drawnNumArr) { const ticketCol = client.db('ticketdb').collection('tickets') return await ticketCol.aggregate([ { $addFields: { topLineWin: { $allElementsTrue: { $map: { input: '$tkt.0', in: {$or: [{$eq: ['$$this', '']}, {$in: ['$$this', drawnNumArr]}]} } } }, midLineWin: { $allElementsTrue: { $map: { input: '$tkt.1', in: {$or: [{$eq: ['$$this', '']}, {$in: ['$$this', drawnNumArr]}]} } } }, bottomLineWin: { $allElementsTrue: { $map: { input: '$tkt.2', in: {$or: [{$eq: ['$$this', '']}, {$in: ['$$this', drawnNumArr]}]} } } }, fullHouseWin: { $setIsSubset: [ {$filter: {input: {$concatArrays: ['$tkt.0', '$tkt.1', '$tkt.2']}, cond: {$ne: ['$$this', '']}}}, drawnNumArr ] } } }, { $match: { $or: [ {topLineWin: true}, {midLineWin: true}, {bottomLineWin: true}, {fullHouseWin: true} ] } } ]).toArray() }
额外注意事项
如果需要避免同一个票重复领取同一个奖项,可以给tickets集合新增won_awards数组字段,中奖后将对应奖项名称写入该字段,下次判定时过滤掉已经领取过的奖项即可。
内容的提问来源于stack exchange,提问作者aemisom
相关产品推荐
相关产品推荐

