Created
February 26, 2022 04:39
-
-
Save CGamesPlay/43b00d03ce7e453a9a8ea513b34eb807 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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