Score-Based Victory

Use Case: First to X points wins, race to goal

Victory condition based on reaching a target score.

Game Definition

const WINNING_SCORE = 10;

export const game = defineGame({
  setup: ({ playerIds }) => ({
    players: Object.fromEntries(
      playerIds.map((id) => [id, { x: 400, y: 300, score: 0 }])
    ),
    winner: null as string | null,
    gameOver: false,
  }),

  actions: {
    score: {
      apply: (state, context, input: { points: number }) => {
        if (state.gameOver) return;

        const player = state.players[context.targetId];
        if (!player) return;

        player.score += input.points;

        // Check win condition
        if (player.score >= WINNING_SCORE) {
          state.winner = context.targetId;
          state.gameOver = true;
          console.log(`Player ${context.targetId} wins!`);
        }
      },
    },

    reset: {
      apply: (state) => {
        for (const player of Object.values(state.players)) {
          player.score = 0;
        }
        state.winner = null;
        state.gameOver = false;
      },
    },
  },
});

Features

  • ✅ Win condition
  • ✅ Game over state
  • ✅ Reset functionality
  • ✅ Winner tracking

Variations

Configurable Win Score

setup: ({ playerIds }) => ({
  players: Object.fromEntries(
    playerIds.map((id) => [id, { score: 0 }])
  ),
  winningScore: 10, // Configurable
  winner: null,
  gameOver: false
}),

actions: {
  setWinningScore: {
    apply: (state, context, input: { score: number }) => {
      if (!state.gameOver) {
        state.winningScore = input.score;
      }
    }
  },
  
  score: {
    apply: (state, context, input: { points: number }) => {
      if (state.gameOver) return;
      
      const player = state.players[context.targetId];
      player.score += input.points;
      
      if (player.score >= state.winningScore) {
        state.winner = context.targetId;
        state.gameOver = true;
      }
    }
  }
}

Negative Scoring

actions: {
  score: {
    apply: (state, context, input: { points: number }) => {
      const player = state.players[context.targetId];
      player.score += input.points;
      
      // Clamp to minimum
      player.score = Math.max(0, player.score);
      
      // Check win condition
      if (player.score >= WINNING_SCORE) {
        state.winner = context.targetId;
        state.gameOver = true;
      }
    }
  },
  
  penalty: {
    apply: (state, context, input: { points: number }) => {
      const player = state.players[context.targetId];
      player.score = Math.max(0, player.score - input.points);
    }
  }
}

Leaderboard

function getLeaderboard(state: GameState) {
  return Object.entries(state.players)
    .map(([id, player]) => ({ id, score: player.score }))
    .sort((a, b) => b.score - a.score);
}

// In your rendering code
const leaderboard = getLeaderboard(runtime.getState());
console.log('Top 3:', leaderboard.slice(0, 3));

See Also