Skip to content

Instantly share code, notes, and snippets.

@CGamesPlay
Created February 26, 2022 04:39
Show Gist options
  • Save CGamesPlay/43b00d03ce7e453a9a8ea513b34eb807 to your computer and use it in GitHub Desktop.
Save CGamesPlay/43b00d03ce7e453a9a8ea513b34eb807 to your computer and use it in GitHub Desktop.
import { Methods, Context } from "./.hathora/methods";
import { Response } from "../api/base";
import {
UserId,
PlayerState,
IJoinGameRequest,
IChooseGestureRequest,
INextRoundRequest,
Gesture,
} from "../api/types";
function getUserInput(opts: Array<[UserId[], string[]]>) {
return opts;
}
type FlowConfig<S> = {
flow: (state: S) => Promise<void>;
} & {
[k: string]: (...args: unknown[]) => unknown;
};
type FlowState<S> = { validMoves: Array<[UserId[], string[]]>; state: S };
function useFlow<S, I>(config: FlowConfig<S>): I {
class Impl {}
const _impl = Impl as any;
const { initialize, getUserState, flow, ...methods } = config;
_impl.initialize = (...args: unknown[]) => {
const state = initialize(...args);
return { validMoves: [], state };
};
for (const name in methods) {
_impl[name] = (state: FlowState<S>, userId: UserId, ...rest: unknown[]) => {
if (!isValidMove(state.validMoves, userId, name)) {
return Response.error("invalid move");
}
return methods[name](state.state, userId, ...rest);
};
}
_impl.getUserState = (state: FlowState<S>, userId: UserId) => {
const ret = getUserState(state.state, userId) as any;
return {
...ret,
validMoves: getValidMovesForUser(state.validMoves, userId),
};
};
return Impl as unknown as I;
}
/**
* Returns true if the provided config allows the provided userId to call the
* provided method name at this time.
*/
function isValidMove(
config: Array<[UserId[], string[]]>,
userId: UserId,
method: string
) {
return true;
}
/**
* Returns the names of methods the provided user is allowed to call based on
* the provided config.
*/
function getValidMovesForUser(
config: Array<[UserId[], string[]]>,
userId: UserId
): string[] {
return [];
}
export const Impl = useFlow<PlayerState, Methods<PlayerState>>({
async flow(state) {
await getUserInput([]);
},
initialize(userId: UserId, ctx: Context): PlayerState {
return { round: 0, player1: { id: userId, score: 0 } };
},
joinGame(
state: PlayerState,
userId: UserId,
ctx: Context,
request: IJoinGameRequest
): Response {
if (state.player1.id === userId || state.player2?.id === userId) {
return Response.error("Already joined");
}
if (state.player2 !== null) {
return Response.error("Game full");
}
state.player2 = { id: userId, score: 0 };
return Response.ok();
},
chooseGesture(
state: PlayerState,
userId: UserId,
ctx: Context,
request: IChooseGestureRequest
): Response {
if (state.player2 === null) {
return Response.error("Game not started");
}
const player = [state.player1, state.player2].find((p) => p.id === userId);
if (player === undefined) {
return Response.error("Invalid player");
}
if (player.gesture !== null) {
return Response.error("Already picked");
}
player.gesture = request.gesture;
const otherPlayer =
userId === state.player1.id ? state.player2 : state.player1;
if (otherPlayer.gesture !== null) {
if (gestureWins(player.gesture, otherPlayer.gesture)) {
player.score++;
} else if (gestureWins(otherPlayer.gesture, player.gesture)) {
otherPlayer.score++;
}
}
return Response.ok();
},
nextRound(
state: PlayerState,
userId: UserId,
ctx: Context,
request: INextRoundRequest
): Response {
if (state.player2 === null) {
return Response.error("Game not started");
}
if (state.player1.gesture === null || state.player2.gesture === null) {
return Response.error("Round still in progress");
}
state.round++;
state.player1.gesture = null;
state.player2.gesture = null;
return Response.ok();
},
getUserState(state: PlayerState, userId: UserId): PlayerState {
if (state.player2 === null) {
return state;
}
if (state.player1.gesture !== null && state.player2.gesture !== null) {
return state;
}
return {
round: state.round,
player1:
userId === state.player1.id
? state.player1
: { ...state.player1, gesture: undefined },
player2:
userId === state.player2.id
? state.player2
: { ...state.player2, gesture: undefined },
};
},
});
function gestureWins(gesture: Gesture, otherGesture: Gesture) {
return (
(gesture === Gesture.ROCK && otherGesture === Gesture.SCISSOR) ||
(gesture === Gesture.SCISSOR && otherGesture === Gesture.PAPER) ||
(gesture === Gesture.PAPER && otherGesture === Gesture.ROCK)
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment