Merge pull request #26 from cassoule/poker

Poker V1
This commit is contained in:
Milo Gourvest
2025-06-14 19:15:42 +02:00
committed by GitHub
4 changed files with 667 additions and 16 deletions

555
index.js
View File

@@ -16,7 +16,10 @@ import {
gork,
getRandomHydrateText,
getAPOUsers,
postAPOBuy, initialShuffledCards
postAPOBuy,
initialShuffledCards,
getFirstActivePlayerAfterDealer,
getNextActivePlayer, checkEndOfBettingRound, initialCards, checkRoomWinners
} from './utils.js';
import {channelPointsHandler, eloHandler, pokerTest, slowmodesHandler} from './game.js';
import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
@@ -50,6 +53,7 @@ import { v4 as uuidv4 } from 'uuid';
import { uniqueNamesGenerator, adjectives, languages, animals } from 'unique-names-generator';
import pkg from 'pokersolver';
const { Hand } = pkg;
import axios from 'axios';
// Create an express app
const app = express();
@@ -3474,17 +3478,31 @@ app.post('/create-poker-room', async (req, res) => {
created_at: Date.now(),
last_move_at: Date.now(),
players: {},
queue: {},
pioche: initialShuffledCards(),
tapis: [],
dealer: null,
sb: null,
bb: null,
highest_bet: null,
current_player: null,
current_turn: null,
playing: false,
winners: [],
waiting_for_restart: false,
fakeMoney: false,
}
res.status(200).send({ roomId: id })
try {
const url = (process.env.DEV_SITE === 'true' ? process.env.API_URL_DEV : process.env.API_URL) + '/poker-room/join'
const response = await axios.post(url, { userId: creatorId, roomId: id })
} catch (e) {
console.log(e)
}
io.emit('new-poker-room')
return res.status(200).send({ roomId: id })
});
app.get('/poker-rooms', (req, res) => {
@@ -3516,27 +3534,50 @@ app.post('/poker-room/join', async (req, res) => {
bet: null,
solve: null,
folded: false,
allin: false,
last_played_turn: null,
is_last_raiser: false,
}
try {
pokerRooms[roomId].players[userId] = player
if (pokerRooms[roomId].playing) {
pokerRooms[roomId].queue[userId] = player
} else {
pokerRooms[roomId].players[userId] = player
}
if (fakeMoney) pokerRooms[roomId].fakeMoney = true
pokerRooms[roomId].last_move_at = Date.now()
} catch (e) {
//
}
io.emit('player-joined')
io.emit('new-poker-room')
return res.status(200)
});
app.post('/poker-room/accept', async (req, res) => {
const { userId, roomId } = req.body
const player = pokerRooms[roomId].queue[userId]
if (!player) return res.status(404).send({ message: 'Joueur introuvable dans le file d\'attente'});
try {
pokerRooms[roomId].players[userId] = player
delete pokerRooms[roomId].queue[userId]
} catch (e) {
//
}
io.emit('new-poker-room')
return res.status(200)
})
app.post('/poker-room/leave', async (req, res) => {
const { userId, roomId } = req.body
try {
delete pokerRooms[roomId].players[userId]
pokerRooms[roomId].last_move_at = Date.now()
if (userId === pokerRooms[roomId].host_id) {
const newHostId = Object.keys(pokerRooms[roomId].players).find(id => id !== userId)
if (!newHostId) {
@@ -3549,7 +3590,6 @@ app.post('/poker-room/leave', async (req, res) => {
//
}
io.emit('player-joined')
io.emit('new-poker-room')
return res.status(200)
});
@@ -3557,6 +3597,9 @@ app.post('/poker-room/leave', async (req, res) => {
app.post('/poker-room/start', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
// preflop
try {
for (const playerId in pokerRooms[roomId].players) {
const player = pokerRooms[roomId].players[playerId]
@@ -3567,12 +3610,6 @@ app.post('/poker-room/start', async (req, res) => {
}
}
}
for (let i = 0; i < 3; i++) {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
}
for (const playerId in pokerRooms[roomId].players) {
const player = pokerRooms[roomId].players[playerId]
let fullHand = pokerRooms[roomId].tapis
@@ -3586,16 +3623,506 @@ app.post('/poker-room/start', async (req, res) => {
pokerRooms[roomId].sb = Object.keys(pokerRooms[roomId].players)[1]
pokerRooms[roomId].bb = Object.keys(pokerRooms[roomId].players)[2 % Object.keys(pokerRooms[roomId].players).length]
pokerRooms[roomId].players[Object.keys(pokerRooms[roomId].players)[1]].bet = 10 //SB
pokerRooms[roomId].players[Object.keys(pokerRooms[roomId].players)[1]].bank -= 10 //SB
pokerRooms[roomId].players[Object.keys(pokerRooms[roomId].players)[2 % Object.keys(pokerRooms[roomId].players).length]].bet = 20 //BB
pokerRooms[roomId].players[Object.keys(pokerRooms[roomId].players)[2 % Object.keys(pokerRooms[roomId].players).length]].bank -= 20 //BB
pokerRooms[roomId].highest_bet = 20
pokerRooms[roomId].current_player = Object.keys(pokerRooms[roomId].players)[3 % Object.keys(pokerRooms[roomId].players).length]
pokerRooms[roomId].current_turn = 0;
pokerRooms[roomId].players[pokerRooms[roomId].bb].last_played_turn = pokerRooms[roomId].current_turn
pokerRooms[roomId].playing = true
pokerRooms[roomId].last_move_at = Date.now()
io.emit('poker-room-started')
io.emit('new-poker-room')
return res.status(200)
})
app.post('/poker-room/flop', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
//flop
pokerRooms[roomId].current_turn = 1
try {
for (let i = 0; i < 3; i++) {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return res.status(200)
});
async function handleFlop(roomId) {
if (!pokerRooms[roomId]) return false
//flop
pokerRooms[roomId].current_turn = 1
try {
for (let i = 0; i < 3; i++) {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return true
}
app.post('/poker-room/turn', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
//turn
pokerRooms[roomId].current_turn = 2
try {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return res.status(200)
});
async function handleTurn(roomId) {
if (!pokerRooms[roomId]) return false
//turn
pokerRooms[roomId].current_turn = 2
try {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return true
}
app.post('/poker-room/river', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
//river
pokerRooms[roomId].current_turn = 3
try {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return res.status(200)
});
async function handleRiver(roomId) {
if (!pokerRooms[roomId]) return false
//river
pokerRooms[roomId].current_turn = 3
try {
if (pokerRooms[roomId].pioche.length > 0) {
pokerRooms[roomId].tapis.push(pokerRooms[roomId].pioche[0])
pokerRooms[roomId].pioche.shift()
}
await updatePokerPlayersSolve(roomId)
} catch(e) {
console.log(e)
}
pokerRooms[roomId].current_player = getFirstActivePlayerAfterDealer(pokerRooms[roomId])
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return true
}
app.post('/poker-room/showdown', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
//showdown
pokerRooms[roomId].current_turn = 4
pokerRooms[roomId].current_player = null
await updatePokerPlayersSolve(roomId)
pokerRooms[roomId].winners = checkRoomWinners(pokerRooms[roomId])
try {
const url = (process.env.DEV_SITE === 'true' ? process.env.API_URL_DEV : process.env.API_URL) + '/poker-room/winner'
const response = await axios.post(url, { roomId: roomId, winnerIds: pokerRooms[roomId].winners })
} catch (e) {
console.log(e)
}
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return res.status(200)
})
async function handleShowdown(roomId) {
if (!pokerRooms[roomId]) return false
//showdown
pokerRooms[roomId].current_turn = 4
pokerRooms[roomId].current_player = null
await updatePokerPlayersSolve(roomId)
pokerRooms[roomId].winners = checkRoomWinners(pokerRooms[roomId])
try {
await handleWinner(roomId, pokerRooms[roomId].winners)
} catch (e) {
console.log(e)
}
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
return true
}
app.post('/poker-room/progressive-showdown', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
while(pokerRooms[roomId].current_turn < 4) {
let allGood = true
switch (pokerRooms[roomId].current_turn) {
case 0:
allGood = await handleFlop(roomId)
break;
case 1:
allGood = await handleTurn(roomId)
break;
case 2:
allGood = await handleRiver(roomId)
break;
case 3:
allGood = await handleShowdown(roomId)
break;
default:
allGood = false
break;
}
if (!allGood) console.log('error in progressive showdown')
await sleep(1000)
}
return res.status(200)
})
app.post('/poker-room/winner', async (req, res) => {
const { roomId, winnerIds } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
//if (!pokerRooms[roomId].players[winnerIds]) return res.status(404).send({ message: 'Joueur introuvable' })
pokerRooms[roomId].current_player = null
pokerRooms[roomId].current_turn = 4
let pool = 0;
for (const playerId in pokerRooms[roomId].players) {
const player = pokerRooms[roomId].players[playerId]
pool += player?.bet ?? 0
player.bet = 0
if (player.bank === 0 && !pokerRooms[roomId].winners.includes(player.id)) {
try {
delete pokerRooms[roomId].players[player.id]
if (player.id === pokerRooms[roomId].host_id) {
const newHostId = Object.keys(pokerRooms[roomId].players).find(id => id !== player.id)
if (!newHostId) {
delete pokerRooms[roomId]
} else {
pokerRooms[roomId].host_id = newHostId
}
}
} catch (e) {
//
}
}
}
pokerRooms[roomId].winners.forEach((winner) => {
pokerRooms[roomId].players[winner].bank += Math.floor(pool / winnerIds.length)
});
pokerRooms[roomId].waiting_for_restart = true
io.emit('new-poker-room')
return res.status(200)
})
async function handleWinner(roomId, winnerIds) {
if (!pokerRooms[roomId]) return false
pokerRooms[roomId].current_player = null
pokerRooms[roomId].current_turn = 4
let pool = 0;
for (const playerId in pokerRooms[roomId].players) {
const player = pokerRooms[roomId].players[playerId]
pool += player?.bet ?? 0
player.bet = 0
if (player.bank === 0 && !pokerRooms[roomId].winners.includes(player.id)) {
try {
delete pokerRooms[roomId].players[player.id]
} catch (e) {
//
}
}
}
pokerRooms[roomId].winners = checkRoomWinners(pokerRooms[roomId])
pokerRooms[roomId]?.winners.forEach((winner) => {
pokerRooms[roomId].players[winner].bank += Math.floor(pool / winnerIds.length)
});
pokerRooms[roomId].waiting_for_restart = true
io.emit('new-poker-room')
return true
}
app.post('/poker-room/next-round', async (req, res) => {
const { roomId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
pokerRooms[roomId].waiting_for_restart = false
pokerRooms[roomId].winners = []
pokerRooms[roomId].pioche = initialShuffledCards()
pokerRooms[roomId].tapis = []
pokerRooms[roomId].dealer = null
pokerRooms[roomId].sb = null
pokerRooms[roomId].bb = null
pokerRooms[roomId].highest_bet = null
pokerRooms[roomId].current_player = null
pokerRooms[roomId].current_turn = null
for (const playerId in pokerRooms[roomId].players) {
const player = pokerRooms[roomId].players[playerId]
player.hand = []
player.bet = null
player.solve = null
player.folded = false
player.allin = false
player.last_played_turn = null
player.is_last_raiser = false
}
try {
const url = (process.env.DEV_SITE === 'true' ? process.env.API_URL_DEV : process.env.API_URL) + '/poker-room/start'
const response = await axios.post(url, { roomId: roomId })
} catch (e) {
console.log(e)
}
io.emit('new-poker-room')
return res.status(200)
})
app.post('/poker-room/action/fold', async (req, res) => {
const { roomId, playerId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
if (!pokerRooms[roomId].players[playerId]) return res.status(404).send({ message: 'Joueur introuvable' })
if (pokerRooms[roomId].current_player !== playerId) return res.status(403).send({ message: 'Ce n\'est pas ton tour' });
try {
pokerRooms[roomId].players[playerId].folded = true
pokerRooms[roomId].players[playerId].last_played_turn = pokerRooms[roomId].current_turn
await checksAfterPokerAction(roomId)
io.emit('new-poker-room')
} catch(e) {
console.log(e)
return res.status(500).send({ message: e})
}
return res.status(200)
});
app.post('/poker-room/action/check', async (req, res) => {
const { roomId, playerId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
if (!pokerRooms[roomId].players[playerId]) return res.status(404).send({ message: 'Joueur introuvable' })
if (pokerRooms[roomId].current_player !== playerId) return res.status(403).send({ message: 'Ce n\'est pas ton tour' });
try {
pokerRooms[roomId].players[playerId].last_played_turn = pokerRooms[roomId].current_turn
await checksAfterPokerAction(roomId)
io.emit('new-poker-room')
} catch(e) {
console.log(e)
return res.status(500).send({ message: e})
}
return res.status(200)
});
app.post('/poker-room/action/call', async (req, res) => {
const { roomId, playerId } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
if (!pokerRooms[roomId].players[playerId]) return res.status(404).send({ message: 'Joueur introuvable' })
if (pokerRooms[roomId].current_player !== playerId) return res.status(403).send({ message: 'Ce n\'est pas ton tour' });
try {
let diff = pokerRooms[roomId].highest_bet - pokerRooms[roomId].players[playerId].bet
if (diff > pokerRooms[roomId].players[playerId].bank) {
diff = pokerRooms[roomId].players[playerId].bank
pokerRooms[roomId].players[playerId].allin = true
}
pokerRooms[roomId].players[playerId].bet += diff
pokerRooms[roomId].players[playerId].bank -= diff
pokerRooms[roomId].players[playerId].last_played_turn = pokerRooms[roomId].current_turn
if (Object.values(pokerRooms[roomId].players).find(p => p.allin)) pokerRooms[roomId].players[playerId].allin = true
await checksAfterPokerAction(roomId)
io.emit('new-poker-room')
} catch(e) {
console.log(e)
return res.status(500).send({ message: e})
}
return res.status(200)
});
app.post('/poker-room/action/raise', async (req, res) => {
const { roomId, playerId, amount } = req.body
if (!pokerRooms[roomId]) return res.status(404).send({ message: 'Table introuvable' })
if (!pokerRooms[roomId].players[playerId]) return res.status(404).send({ message: 'Joueur introuvable' })
if (pokerRooms[roomId].current_player !== playerId) return res.status(403).send({ message: 'Ce n\'est pas ton tour' });
if (amount > pokerRooms[roomId].players[playerId].bank) return res.status(403).send({ message: 'Tu n\as pas assez'});
try {
if (amount === pokerRooms[roomId].players[playerId].bank) {
pokerRooms[roomId].players[playerId].allin = true
}
pokerRooms[roomId].players[playerId].bet += amount
pokerRooms[roomId].players[playerId].bank -= amount
pokerRooms[roomId].players[playerId].last_played_turn = pokerRooms[roomId].current_turn
for (let id in pokerRooms[roomId].players) {
pokerRooms[roomId].players[id].is_last_raiser = false
}
pokerRooms[roomId].players[playerId].is_last_raiser = true
pokerRooms[roomId].highest_bet = pokerRooms[roomId].players[playerId].bet
await checksAfterPokerAction(roomId)
io.emit('new-poker-room')
} catch(e) {
console.log(e)
return res.status(500).send({ message: e})
}
return res.status(200)
});
async function checksAfterPokerAction(roomId) {
const data = checkEndOfBettingRound(pokerRooms[roomId])
if (data.winner !== null) {
try {
pokerRooms[roomId].winners = [data.winner]
const url = (process.env.DEV_SITE === 'true' ? process.env.API_URL_DEV : process.env.API_URL) + '/poker-room/winner'
const response = await axios.post(url, { roomId: roomId, winnerIds: [data.winner] })
} catch (e) {
console.log(e)
}
} else if (data.endRound) {
try {
const url = (process.env.DEV_SITE === 'true' ? process.env.API_URL_DEV : process.env.API_URL) + '/poker-room/' + data.nextPhase
const response = await axios.post(url, { roomId: roomId})
} catch (e) {
console.log(e)
}
} else {
pokerRooms[roomId].current_player = getNextActivePlayer(pokerRooms[roomId])
}
pokerRooms[roomId].last_move_at = Date.now()
io.emit('new-poker-room')
}
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
}
}
import http from 'http';
import { Server } from 'socket.io';
const server = http.createServer(app);

38
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"dependencies": {
"@google/genai": "^0.8.0",
"@mistralai/mistralai": "^1.6.0",
"axios": "^1.9.0",
"better-sqlite3": "^11.9.1",
"discord-interactions": "^4.0.0",
"discord.js": "^14.18.0",
@@ -339,6 +340,17 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1068,6 +1080,26 @@
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
@@ -2005,6 +2037,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",

View File

@@ -18,6 +18,7 @@
"dependencies": {
"@google/genai": "^0.8.0",
"@mistralai/mistralai": "^1.6.0",
"axios": "^1.9.0",
"better-sqlite3": "^11.9.1",
"discord-interactions": "^4.0.0",
"discord.js": "^14.18.0",

View File

@@ -1,7 +1,10 @@
import 'dotenv/config';
import OpenAI from "openai";
import { GoogleGenAI } from "@google/genai";
import { Mistral } from '@mistralai/mistralai';
import {GoogleGenAI} from "@google/genai";
import {Mistral} from '@mistralai/mistralai';
import pkg from 'pokersolver';
const { Hand } = pkg;
export async function DiscordRequest(endpoint, options) {
// append endpoint to root API URL
@@ -197,4 +200,86 @@ export async function postAPOBuy(userId, amount) {
})
.then(response => response)
.catch(error => console.log('Post error:', error))
}
export function getFirstActivePlayerAfterDealer(room) {
const players = Object.values(room.players);
const dealerPosition = players.findIndex((p) => p.id === room.dealer);
for (let i = 1; i < players.length; i++) {
const nextPos = (dealerPosition + i) % players.length;
if (!players[nextPos].folded && !players[nextPos].allin) {
return players[nextPos].id;
}
}
return null;
}
export function getNextActivePlayer(room) {
const players = Object.values(room.players);
const currentPlayerPosition = players.findIndex((p) => p.id === room.current_player);
for (let i = 1; i < players.length; i++) {
const nextPos = (currentPlayerPosition + i) % players.length;
if (!players[nextPos].folded && !players[nextPos].allin) {
return players[nextPos].id;
}
}
return null;
}
export function checkEndOfBettingRound(room) {
const players = Object.values(room.players);
const activePlayers = players.filter((p) => !p.folded);
if (activePlayers.length === 1) {
return { endRound: true, winner: activePlayers[0].id, nextPhase: null };
}
const allInPlayers = activePlayers.filter(p => p.allin)
if (allInPlayers.length === activePlayers.length) {
return { endRound: true, winner: null, nextPhase: 'progressive-showdown' };
}
const allActed = activePlayers.every(p => (p.bet === room.highest_bet || p.allin) && p.last_played_turn === room.current_turn);
if (allActed) {
let nextPhase;
switch (room.current_turn) {
case 0:
nextPhase = 'flop';
break;
case 1:
nextPhase = 'turn';
break;
case 2:
nextPhase = 'river';
break;
case 3:
nextPhase = 'showdown';
break;
default:
nextPhase = null;
break;
}
return { endRound: true, winner: null, nextPhase: nextPhase };
}
return { endRound: false, winner: null, nextPhase: null };
}
export function checkRoomWinners(room) {
const players = Object.values(room.players);
const activePlayers = players.filter(p => !p.folded);
const playerSolutions = activePlayers.map(player => ({
id: player.id,
solution: Hand.solve([...room.tapis, ...player.hand], 'standard', false)
}));
const solutions = playerSolutions.map(ps => ps.solution);
const winningIndices = Hand.winners(solutions).map(winningHand =>
solutions.findIndex(sol => sol === winningHand)
);
return winningIndices.map(index => playerSolutions[index].id);
}