Skip to content

Commit

Permalink
* replace Pollname with string enum
Browse files Browse the repository at this point in the history
 * replace Record with Map because that works better with enum keys
  • Loading branch information
ConorMurphy21 committed Dec 27, 2023
1 parent e1d3d75 commit 729ac53
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 55 deletions.
7 changes: 4 additions & 3 deletions server/src/routes/gameHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Server, Socket } from 'socket.io';
/*** handler validation schemas ***/
import { ApiResult, isErr, isOk, isSuccess } from '../types/result';
import { ConfigurableOptions, getConfigurableOptionsSchema } from '../state/options';
import { PollName } from '../state/pollService';

const registerGameHandlers = (io: Server, socket: Socket) => {
/*** GAME STATE ENDPOINTS ***/
Expand Down Expand Up @@ -49,7 +50,7 @@ const registerGameHandlers = (io: Server, socket: Socket) => {
socket.on('promptResponse', (response: string) => {
const validationResult = z.string().max(60).min(1).safeParse(response);
if (!validationResult.success) {
logger.error('(gameHandlers) Prompt Response too large');
logger.warn('(gameHandlers) Prompt Response too large');
return;
}
response = validationResult.data;
Expand All @@ -68,8 +69,8 @@ const registerGameHandlers = (io: Server, socket: Socket) => {
});

// true to vote to skip, false to unvote to skip
socket.on('pollVote', (pollName: string) => {
const validationResult = z.string().safeParse(pollName);
socket.on('pollVote', (pollName: PollName) => {
const validationResult = z.nativeEnum(PollName).safeParse(pollName);
if (!validationResult.success) {
logger.error('(gameHandlers) PollVote invalid format');
return;
Expand Down
16 changes: 8 additions & 8 deletions server/src/state/gameState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Prompts } from './prompts';
import { getCorrections, stringMatch } from './matchUtils';
import { PollService } from './pollService';
import { PollName, PollService } from './pollService';
import logger from '../logger/logger';
import { Room, Player as RoomPlayer } from './rooms';
import { Player as RoomPlayer, Room } from './rooms';
import { Err, Info, Ok, Result, Success, VoidResult, Warning } from '../types/result';
import { ConfigurableOptions, defaultOptions, getConfigurableOptionsSchema, Options } from './options';

Expand Down Expand Up @@ -178,7 +178,7 @@ export class GameState {

if (this.options.promptSkipping) {
if (this._promptSkippedCb) {
this.pollService.registerPoll('skipPrompt', this._promptSkippedCb, Stage.Response);
this.pollService.registerPoll(PollName.SkipPrompt, this._promptSkippedCb, Stage.Response);
}
}

Expand Down Expand Up @@ -218,7 +218,7 @@ export class GameState {
return Ok({ response });
}

pollVote(id: string, pollName: string): Result<{ count: number; next: boolean }> {
pollVote(id: string, pollName: PollName): Result<{ count: number; next: boolean }> {
return this.pollService.acceptVote(pollName, id, this.stage);
}

Expand All @@ -241,7 +241,7 @@ export class GameState {
if (resetRetries) {
this.remainingSikeRetries = this.options.sikeRetries;
}
this.pollService.clearPoll('sikeDispute');
this.pollService.clearPoll(PollName.SikeDispute);
for (const player of this.players) {
player.match = '';
player.matchingComplete = false;
Expand All @@ -251,7 +251,7 @@ export class GameState {
/*** PROMPT SELECTION state changes ***/
beginSelection(): boolean {
this.stage = Stage.Selection;
this.pollService.clearPoll('skipPrompt');
this.pollService.clearPoll(PollName.SkipPrompt);

// increment round here, this way skipping prompts doesn't increment the round count
this.round++;
Expand Down Expand Up @@ -294,7 +294,7 @@ export class GameState {
this.initialSelector = (this.initialSelector + 1) % this.players.length;
this.stage = Stage.EndRound;
if (this._startNextPromptCb) {
this.pollService.registerPoll('startNextRound', this._startNextPromptCb, Stage.EndRound, undefined, 0.75);
this.pollService.registerPoll(PollName.StartNextRound, this._startNextPromptCb, Stage.EndRound, undefined, 0.75);
}
return false;
}
Expand Down Expand Up @@ -373,7 +373,7 @@ export class GameState {
this.stage = Stage.Matching;
if (this.options.sikeDispute && this.selectionType === SelectionType.Sike) {
this.pollService.registerPoll(
'sikeDispute',
PollName.SikeDispute,
() => this._sikeDisputeAction(),
Stage.Matching,
this.selectorId()
Expand Down
91 changes: 47 additions & 44 deletions server/src/state/pollService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { GameState, Stage } from './gameState';
import { Err, Ok, Result } from '../types/result';

export enum PollName {
SkipPrompt = 'skipPrompt',
StartNextRound = 'startNextRound',
SikeDispute = 'sikeDispute'
}

type Poll = {
stage: Stage;
completeCb: () => void;
Expand All @@ -11,37 +17,38 @@ type Poll = {

export class PollService {
private gameState: GameState;
private polls: Record<string, Poll>;
private polls: Map<PollName, Poll>;

constructor(gameState: GameState) {
this.gameState = gameState;
this.polls = {};
this.polls = new Map<PollName, Poll>();
}

registerPoll(
pollName: string,
pollName: PollName,
completeCb: () => void,
stage: Stage,
exclude?: string,
majorityPercent = 0.501
): void {
this.polls[pollName] = {
this.polls.set(pollName, {
completeCb,
majorityPercent,
stage,
exclude,
inFavor: []
};
});
}

clearPoll(pollName: string) {
delete this.polls[pollName];
clearPoll(pollName: PollName) {
this.polls.delete(pollName);
}

acceptVote(pollName: string, id: string, stage: Stage): Result<{ count: number; next: boolean }> {
if (!Object.prototype.hasOwnProperty.call(this.polls, pollName) || this.polls[pollName] === null) {
acceptVote(pollName: PollName, id: string, stage: Stage): Result<{ count: number; next: boolean }> {
const poll = this.polls.get(pollName);
if (!poll) {
return Err(`noPoll${pollName}`);
}
const poll = this.polls[pollName];
if (poll.stage && stage !== poll.stage) {
return Err('invalidStage');
}
Expand All @@ -54,67 +61,63 @@ export class PollService {
} else {
poll.inFavor.push(id);
}
const count = this.countVotes(pollName);
if (this.cbIfComplete(pollName)) {
const count = this._countVotes(poll);
if (this._cbIfComplete(poll, pollName)) {
return Ok({ count: 0, next: false });
}
return Ok({ count, next: this.nextComplete(pollName) });
return Ok({ count, next: this._nextComplete(poll) });
}

getVoteCounts(): Record<string, { count: number; next: boolean }> {
const ret: Record<string, { count: number; next: boolean }> = {};
for (const poll in this.polls) {
if (this.polls[poll]) {
ret[poll] = {
count: this.countVotes(poll),
next: this.nextComplete(poll)
};
} else {
ret[poll] = { count: 0, next: false };
}
// TODO: change this to Map<Pollname, ...>, (needs to be changed on client side too)
getVoteCounts(): Record<PollName, { count: number; next: boolean }> {
const ret: Record<PollName, { count: number; next: boolean }> = {
[PollName.SkipPrompt]: { count: 0, next: false },
[PollName.SikeDispute]: { count: 0, next: false },
[PollName.StartNextRound]: { count: 0, next: false }
};
for (const [pollName, poll] of this.polls) {
ret[pollName] = {
count: this._countVotes(poll),
next: this._nextComplete(poll)
};
}
return ret;
}

cbIfComplete(pollName: string): boolean {
const poll = this.polls[pollName];
if (this.complete(pollName)) {
_cbIfComplete(poll: Poll, pollName: PollName): boolean {
if (this._complete(poll)) {
this.clearPoll(pollName);
poll.completeCb();
poll!.completeCb();
return true;
}
return false;
}

complete(pollName: string) {
const poll = this.polls[pollName];
_complete(poll: Poll) {
const threshold = Math.ceil(this.gameState.numVoters(poll.exclude) * poll.majorityPercent);
return this.countVotes(pollName) >= threshold;
return this._countVotes(poll) >= threshold;
}

nextComplete(pollName: string) {
const poll = this.polls[pollName];
_nextComplete(poll: Poll) {
const threshold = Math.ceil(this.gameState.numVoters(poll.exclude) * poll.majorityPercent);
return this.countVotes(pollName) + 1 >= threshold;
return this._countVotes(poll) + 1 >= threshold;
}

countVotes(pollName: string) {
return this.polls[pollName].inFavor.length;
_countVotes(poll: Poll) {
return poll.inFavor.length;
}

checkComplete(): void {
for (const pollName in this.polls) {
if (this.polls[pollName]) this.cbIfComplete(pollName);
for (const [pollName, poll] of this.polls) {
this._cbIfComplete(poll, pollName);
}
}

disconnect(id: string): void {
for (const poll in this.polls) {
if (this.polls[poll]) {
const index = this.polls[poll].inFavor.indexOf(id);
if (index >= 0) {
this.polls[poll].inFavor.splice(index, 1);
}
for (const poll of this.polls.values()) {
const index = poll.inFavor.indexOf(id);
if (index >= 0) {
poll.inFavor.splice(index, 1);
}
}
}
Expand Down

0 comments on commit 729ac53

Please sign in to comment.