Skip to content

Commit

Permalink
@aetn23/call (#53)
Browse files Browse the repository at this point in the history
Co-authored-by: sormys <szymonp1806@gmail.com>
Co-authored-by: Karol Wąsowski <wasowski02@protonmail.com>
  • Loading branch information
3 people authored May 21, 2023
1 parent 3fce4a7 commit 8afc38d
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import startGame from './routes/startGame'
import actionFold from './routes/gameplay/fold'
import actionRaise from './routes/gameplay/raise'
import actionCheck from './routes/gameplay/check'
import actionCall from './routes/gameplay/call'
import { rateLimiter } from './utils/rateLimiter'

export const app = express()
Expand Down Expand Up @@ -48,4 +49,6 @@ app.use(actionRaise)

app.use(actionCheck)

app.use(actionCall)

app.use(errorHandling)
89 changes: 89 additions & 0 deletions src/routes/gameplay/call.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { getClient } from '../../utils/databaseConnection'
import { celebrate, Joi, Segments } from 'celebrate'
import {
sendFirebaseMessageToEveryone,
verifyFCMToken,
} from '../../utils/firebase'
import express, { type Router } from 'express'
import { rateLimiter } from '../../utils/rateLimiter'
import {
isPlayerInGame,
isPlayersTurn,
setPlayerState,
setNewCurrentPlayer,
changeGameRoundIfNeeded,
playerHasEnoughMoney,
playerRaised,
getMaxBet,
} from '../../utils/commonRequest'
import sha256 from 'crypto-js/sha256'
import { PlayerState } from '../../utils/types'

const router: Router = express.Router()

router.get(
'/actionCall',
rateLimiter,
celebrate({
[Segments.QUERY]: Joi.object().keys({
playerToken: Joi.string().required().min(1).max(250).label('playerToken'),
gameId: Joi.number().required().min(0).max(999999).label('gameId'),
}),
}),
async (req, res) => {
const playerToken = req.query.playerToken as string
const gameId = req.query.gameId as string
if (!(await verifyFCMToken(playerToken))) {
return res.sendStatus(401)
}

const client = getClient()

client
.connect()
.then(async () => {
if (!(await isPlayerInGame(playerToken, gameId, client))) {
return res.sendStatus(400)
}

if (!(await isPlayersTurn(playerToken, gameId, client))) {
return res.sendStatus(402)
}

const maxBet = await getMaxBet(gameId, client)

if (
!(await playerHasEnoughMoney(gameId, playerToken, maxBet, client))
) {
return res.sendStatus(403)
}

const newPlayer = await setNewCurrentPlayer(playerToken, gameId, client)

await setPlayerState(playerToken, client, PlayerState.Called)
await playerRaised(gameId, playerToken, maxBet, client)
await changeGameRoundIfNeeded(gameId, newPlayer, client)

const message = {
data: {
player: sha256(playerToken).toString(),
type: PlayerState.Called,
actionPayload: maxBet.toString(),
},
token: '',
}

await sendFirebaseMessageToEveryone(message, gameId, client)
res.sendStatus(200)
})
.catch((err) => {
console.log(err.stack)
return res.sendStatus(500)
})
.finally(async () => {
await client.end()
})
}
)

export default router
13 changes: 12 additions & 1 deletion src/routes/gameplay/fold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ router.get(
await setPlayerState(playerToken, client, PlayerState.Folded)
const newPlayer = await setNewCurrentPlayer(playerToken, gameId, client)
if (newPlayer === '') {
const winner = (await playersStillInGame(gameId, client))[0]
const message = {
data: {
player: sha256(newPlayer).toString(),
player: sha256(winner).toString(),
type: PlayerState.Won,
actionPayload: '',
},
Expand Down Expand Up @@ -88,3 +89,13 @@ router.get(
)

export default router

export async function playersStillInGame(gameId: string, client) {
const query = `SELECT token
FROM players
WHERE game_id = $1 AND last_action <> $2 AND last_action <> $3
AND last_action IS NOT NULL
`
const values = [gameId, PlayerState.Folded, PlayerState.NoAction]
return (await client.query(query, values)).rows[0]
}
98 changes: 98 additions & 0 deletions src/tests/call.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { app } from '../app'
import request from 'supertest'
import { getClient } from '../utils/databaseConnection'
import type { NewGameInfo } from '../utils/types'
import { getGameIdAndStatus, getPlayersInGame } from '../utils/commonRequest'

test('Call, correct arguments 1', async () => {
const gameMasterToken = 'CALLTEST'
const gameMasterNick = 'CALLNICK'
const playerToken = 'CALLTEST2'
const playerNick = 'CALLNICK2'
const player2Token = 'CALLTEST3'
const player2Nick = 'CALLNICK3'

const client = getClient()
await client.connect()
const res = await request(app)
.get(
`/createGame?creatorToken=${gameMasterToken}&nickname=${gameMasterNick}`
)
.expect(200)

const key = (res.body as NewGameInfo).gameId
await request(app)
.get(
`/joinGame?playerToken=${playerToken}&nickname=${playerNick}&gameId=${key}`
)
.expect(200)

await request(app)
.get(
`/joinGame?playerToken=${player2Token}&nickname=${player2Nick}&gameId=${key}`
)
.expect(200)

await request(app)
.get(`/startGame?creatorToken=${gameMasterToken}`)
.expect(200)

const gameId =
(await getGameIdAndStatus(gameMasterToken, client)).gameId ?? ''
const players = await getPlayersInGame(gameId, client)
console.log(players)

await request(app)
.get(
`/actionRaise?playerToken=${players[1].token}&gameId=${gameId}&amount=5`
)
.expect(402)

await request(app)
.get(
`/actionRaise?playerToken=${players[0].token}&gameId=${gameId}&amount=300`
)
.expect(200)
await request(app)
.get(`/actionCall?playerToken=${players[1].token}&gameId=${gameId}`)
.expect(200)
const getRound = 'SELECT game_round FROM Games WHERE game_id=$1'

expect(
await (
await client.query(getRound, [gameId])
).rows[0].game_round
).toEqual('1') // Player 2 can still act

await request(app)
.get(
`/actionRaise?playerToken=${players[2].token}&gameId=${gameId}&amount=2`
)
.expect(404)

await request(app)
.get(
`/actionRaise?playerToken=${players[2].token}&gameId=${gameId}&amount=2000000000`
)
.expect(403)

await request(app)
.get(
`/actionRaise?playerToken=${players[2].token}&gameId=${gameId}&amount=400`
)
.expect(200)

await request(app)
.get(`/actionCall?playerToken=${players[0].token}&gameId=${gameId}`)
.expect(200)

await request(app)
.get(`/actionFold?playerToken=${players[1].token}&gameId=${gameId}`)
.expect(200)

await request(app)
.get(`/actionFold?playerToken=${players[2].token}&gameId=${gameId}`)
.expect(402) // round ended

await client.end()
}, 20000)
34 changes: 30 additions & 4 deletions src/utils/commonRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ export async function setPlayerState(
await client.query(query, [state, playerToken])
}

export async function getPlayerState(playerToken: string, client: Client) {
const query = 'SELECT last_action FROM Players WHERE player_token=$1'
await client.query(query, [playerToken])
export async function getPlayerState(
playerToken: string,
client: Client
): Promise<string> {
const query = 'SELECT last_action FROM Players WHERE token=$1'
return (await client.query(query, [playerToken])).rows[0].last_action
}

export async function setNewCurrentPlayer(
Expand Down Expand Up @@ -92,6 +95,7 @@ export async function setNewCurrentPlayer(
playersTurns.rows[i].token,
gameId,
])
console.log(playersTurns.rows[i].token)
return playersTurns.rows[i].token
}
}
Expand All @@ -100,6 +104,7 @@ export async function setNewCurrentPlayer(
playersTurns.rows[0].token,
gameId,
])
console.log(playersTurns.rows[0].token)
return playersTurns.rows[0].token
}
}
Expand Down Expand Up @@ -197,7 +202,20 @@ export async function playerHasEnoughMoney(
amount: string,
client: Client
): Promise<boolean> {
const query = 'SELECT 1 FROM Players WHERE token=$1 AND funds+bet>=$2'
const smallBlindValue = await getSmallBlindValue(gameId, client)
const playerSize = (await getPlayersInGame(gameId, client)).length
const smallBlind = await getSmallBlind(gameId, playerSize, client)
const smallBlindState = await getPlayerState(smallBlind, client)
const bigBlind = await getBigBlind(gameId, playerSize, client)
const bigBlindState = await getPlayerState(bigBlind, client)

if (playerToken === smallBlind && smallBlindState == null) {
amount = (+amount - +smallBlindValue).toString()
} else if (playerToken === bigBlind && bigBlindState == null) {
amount = (+amount - +smallBlindValue * 2).toString()
}

const query = 'SELECT 1 FROM Players WHERE token=$1 AND funds>=$2'
return (await client.query(query, [playerToken, amount])).rowCount !== 0
}

Expand Down Expand Up @@ -227,3 +245,11 @@ export async function playerRaised(
await client.query(setNewBet, [amount, playerToken])
await client.query(putMoneyToTable, [parseInt(amount) - oldBet, gameId])
}

export async function getMaxBet(
gameId: string,
client: Client
): Promise<string> {
const query = 'SELECT MAX(bet) as max FROM Players WHERE game_id=$1'
return (await client.query(query, [gameId])).rows[0].max
}

0 comments on commit 8afc38d

Please sign in to comment.