function clamp(value, min, max) { return Math.min(max, Math.max(min, value)) } export function init(seed) { const playerA = seed?.room?.playerA const playerB = seed?.room?.playerB return { playerA, playerB, players: { [playerA]: { x: 1, y: 1, score: 0 }, [playerB]: { x: 9, y: 9, score: 0 }, }, serverTick: 0, } } export function tick(state, intents, _dtMs, ctx) { const players = {} for (const [userId, player] of Object.entries(state?.players ?? {})) { players[userId] = { ...player } } for (const entry of intents ?? []) { const userId = entry?.userId const intent = entry?.intent const player = players[userId] if (!player || intent?.type !== 'move') continue const dx = Number.isFinite(intent.dx) ? clamp(intent.dx, -1, 1) : 0 const dy = Number.isFinite(intent.dy) ? clamp(intent.dy, -1, 1) : 0 player.x = clamp(player.x + dx, 0, 10) player.y = clamp(player.y + dy, 0, 10) } return { ...state, players, serverTick: Number.isInteger(ctx?.tick) ? ctx.tick : (state?.serverTick ?? 0) + 1, } } export function views(state) { const out = {} for (const userId of [state?.playerA, state?.playerB]) { if (typeof userId !== 'string') continue const opponentId = userId === state.playerA ? state.playerB : state.playerA const own = state.players?.[userId] const opponent = state.players?.[opponentId] const visible = own && opponent && Math.abs(own.x - opponent.x) + Math.abs(own.y - opponent.y) <= 3 out[userId] = { stateJson: JSON.stringify({ own, visibleOpponent: visible ? opponent : null, serverTick: state.serverTick, }), } } return out }