Skip to main content

Overview

CPU opponents in Mokepon provide automated challengers that ensure players always have enemies to battle, even when no other human players are online. The server maintains exactly 3 CPU Mokepons at all times when players are active.
CPU opponents are fully automated - they don’t move around the map but are positioned strategically for players to encounter during exploration.

CPU Generation System

Generation Constants

The server defines how many CPU opponents to create:
const CPU_COUNT = 3; // Number of CPU Mokepons to create

CPU Generation Function

When the game starts or when all human players disconnect, the server generates fresh CPU opponents:
function generateCPUMokepons() {
  // Clear existing CPU players
  cpuMokepons = [];

  // Available mokepon names
  const mokeponNames = [
    "Hipodoge",
    "Capipepo",
    "Ratigueya",
    "Pydos",
    "Tucapalma",
    "Langostelvis",
  ];

  // Shuffle the array to pick random mokepons
  const shuffled = [...mokeponNames].sort(() => 0.5 - Math.random());
  const selectedNames = shuffled.slice(0, CPU_COUNT);

  // Create CPU players with selected mokepons
  for (let i = 0; i < CPU_COUNT; i++) {
    const cpuId = `cpu-${i}-${Date.now()}`;
    const cpuPlayer = new Player(cpuId);

    // Mark as CPU
    cpuPlayer.isCPU = true;

    // Assign a mokepon
    const mokepon = new Mokepon(selectedNames[i]);
    cpuPlayer.assignMokepon(mokepon);

    // Set position (evenly distributed across the map)
    const segment = 100 / (CPU_COUNT + 1);
    cpuPlayer.xPercent = 20 + segment * i + (Math.random() * 10 - 5);
    cpuPlayer.yPercent = 30 + Math.random() * 40;

    // Store attacks (standard set plus an extra attack)
    const attacks = [];
    for (let j = 0; j < 3; j++) {
      attacks.push(mokepon.type);
    }

    // Add two different attacks based on type
    if (mokepon.type === "💧") {
      attacks.push("🔥", "🌱");
    } else if (mokepon.type === "🔥") {
      attacks.push("💧", "🌱");
    } else {
      attacks.push("💧", "🔥");
    }

    cpuPlayer.assignAttacks(attacks);
    cpuPlayer.availableAttacks = [...attacks];
    cpuPlayer.extraAttackAdded = false;

    cpuMokepons.push(cpuPlayer);
    players.push(cpuPlayer);
  }
}

Generation Process

The system randomly selects 3 unique Mokepons from the pool of 6 available types, ensuring variety in each game session.

Available CPU Mokepons

CPU opponents can be any of the six Mokepon types:
MokeponTypeBeatsWeak To
Hipodoge💧 Water🔥 Fire🌱 Plant
Pydos💧 Water🔥 Fire🌱 Plant
Capipepo🌱 Plant💧 Water🔥 Fire
Tucapalma🌱 Plant💧 Water🔥 Fire
Ratigueya🔥 Fire🌱 Plant💧 Water
Langostelvis🔥 Fire🌱 Plant💧 Water

Type Assignment

Each Mokepon’s type is automatically assigned based on its name:
function getMokeponType(name) {
  switch (name) {
    case "Hipodoge":
    case "Pydos":
      return "💧";
    case "Capipepo":
    case "Tucapalma":
      return "🌱";
    case "Ratigueya":
    case "Langostelvis":
      return "🔥";
    default:
      return "💧";
  }
}

CPU Positioning

Strategic Distribution

CPU opponents are positioned across the map using a segmented distribution algorithm:
// Set position (evenly distributed across the map)
const segment = 100 / (CPU_COUNT + 1); // = 25 for 3 CPUs
cpuPlayer.xPercent = 20 + segment * i + (Math.random() * 10 - 5);
cpuPlayer.yPercent = 30 + Math.random() * 40;
Positioning Logic:
  • X-axis: Divided into segments (25% each for 3 CPUs)
    • CPU 0: ~20-30% of map width
    • CPU 1: ~45-55% of map width
    • CPU 2: ~70-80% of map width
    • Random variance of ±5% within each segment
  • Y-axis: Random between 30-70% of map height
    • Keeps CPUs away from top and bottom edges
    • Ensures good vertical spread
The segmented positioning ensures CPUs are spread out across the map, requiring players to explore different areas to encounter all opponents.

Attack System

Base Attack Loadout

Each CPU starts with 5 attacks:
  • 3 attacks of their primary type (matching their Mokepon’s type)
  • 2 attacks of the other types (one of each opposing type)
const attacks = [];
// 3 primary type attacks
for (let j = 0; j < 3; j++) {
  attacks.push(mokepon.type);
}

// Add two different attacks based on type
if (mokepon.type === "💧") {
  attacks.push("🔥", "🌱");
} else if (mokepon.type === "🔥") {
  attacks.push("💧", "🌱");
} else {
  attacks.push("💧", "🔥");
}
Example for a Water-type CPU:
  • 💧 💧 💧 🔥 🌱
This gives CPUs a strong advantage with their primary type while maintaining some versatility.

CPU Attack Selection

Random Selection Logic

CPUs select attacks randomly from their available pool using the /cpu-attack endpoint:
app.get("/mokepon/:playerId/cpu-attack", (req, res) => {
  const playerId = req.params.playerId || "";
  const cpuPlayer = players.find(
    (player) => player.id === playerId && player.isCPU
  );

  if (cpuPlayer && cpuPlayer.attacks && cpuPlayer.attacks.length > 0) {
    // Initialize available attacks for this round if needed
    if (
      !cpuPlayer.availableAttacks ||
      cpuPlayer.availableAttacks.length === 0
    ) {
      cpuPlayer.availableAttacks = [...cpuPlayer.attacks];
    }

    if (!cpuPlayer.attackList) {
      cpuPlayer.attackList = [];
    }

    // Pick a random attack from available attacks
    const randomIndex = Math.floor(
      Math.random() * cpuPlayer.availableAttacks.length
    );
    const attack = cpuPlayer.availableAttacks[randomIndex];

    // Remove the selected attack from available attacks
    cpuPlayer.availableAttacks.splice(randomIndex, 1);

    // Add to CPU's attack list
    cpuPlayer.attackList.push(attack);

    res.send({
      attack,
      allAttacks: cpuPlayer.attackList,
      remainingAttacks: cpuPlayer.availableAttacks,
    });
  }
});

Attack Selection Process

  1. CPU picks a random attack from available pool
  2. Selected attack is removed from available attacks
  3. Attack is added to the battle sequence
  4. When all attacks are used, the pool resets for the next round

Attack Reset System

Between battle rounds, CPU attacks can be reset:
app.post("/mokepon/:playerId/reset-attacks", (req, res) => {
  const playerId = req.params.playerId || "";
  const cpuPlayer = players.find(
    (player) => player.id === playerId && player.isCPU
  );

  if (cpuPlayer) {
    // Reset available attacks to full list
    cpuPlayer.availableAttacks = [...cpuPlayer.attacks];
    
    // Clear the attack list for the new round
    cpuPlayer.attackList = [];

    res.status(200).send({
      success: true,
      message: "CPU attacks reset successfully",
      availableAttacks: cpuPlayer.availableAttacks,
    });
  }
});
This reset system ensures CPUs have their full attack pool available at the start of each battle round, maintaining consistent difficulty.

Type Advantage System

Dynamic Difficulty Adjustment

CPUs get an extra attack when they have a type advantage over the player:
app.post("/mokepon/:playerId/check-advantage", (req, res, next) => {
  try {
    const playerId = req.params.playerId || "";
    const { playerMokeponType } = req.body || {};

    const cpuPlayer = players.find(
      (player) => player.id === playerId && player.isCPU
    );

    // Check if CPU has advantage over player
    const cpuType = cpuPlayer.mokepon.type;
    const hasAdvantage = combatRules[cpuType] === playerMokeponType;

    if (hasAdvantage) {
      // Add an extra attack of the same type as the CPU mokepon
      if (!cpuPlayer.extraAttackAdded) {
        cpuPlayer.attacks.push(cpuType);
        cpuPlayer.availableAttacks.push(cpuType);
        cpuPlayer.extraAttackAdded = true;
      }

      res.json({
        success: true,
        hasAdvantage: true,
        attacks: cpuPlayer.attacks,
        availableAttacks: cpuPlayer.availableAttacks,
      });
    }
  } catch (error) {
    next(error);
  }
});
Combat Rules:
const combatRules = {
  "💧": "🔥", // Water beats Fire
  "🔥": "🌱", // Fire beats Plant
  "🌱": "💧", // Plant beats Water
};
When a CPU has type advantage, their attack pool increases from 5 to 6 attacks, giving them a 4:2 ratio of their strong type vs weak types, making them significantly harder to beat.

CPU Behavior

Static Positioning

Unlike human players, CPU opponents:
  • Do not move around the map
  • Remain at their spawn location throughout the game
  • Only participate in battles when encountered by players

Persistence Rules

CPU opponents persist in the game as long as human players are active:
function areHumanPlayersActive() {
  return players.some((player) => !player.isCPU);
}

function checkAndRegenerateCPUs() {
  if (!areHumanPlayersActive() && cpuMokepons.length > 0) {
    console.log("No human players left. Regenerating CPU Mokepons.");

    // Remove existing CPU players
    const humanPlayers = players.filter((player) => !player.isCPU);
    players.length = 0;
    players.push(...humanPlayers);

    // Generate new CPU Mokepons
    generateCPUMokepons();
  }
}

Regeneration Trigger

When the last human player disconnects, all CPU opponents are removed and regenerated. This means each new game session has fresh CPUs with randomized Mokepons and positions.

Battle Strategy Tips

Fighting CPU Opponents

Type Advantage

Choose a Mokepon with type advantage against the CPU. Check the CPU’s type before engaging.

Attack Prediction

CPUs have 3 attacks of their primary type. Expect them to use it frequently due to random selection.

Disadvantage Battles

If you have type disadvantage, the CPU gets an extra attack (4 vs 2 ratio). Consider avoiding or preparing for a tough fight.

Statistical Edge

With 60% of attacks being their strong type, CPUs are predictable. Use this to plan your attack sequence strategically.

Optimal Strategy

  1. Scout First: Check the types of all 3 CPU opponents on the map
  2. Choose Wisely: Select a Mokepon that has advantage over at least 2 of the 3 CPUs
  3. Plan Encounters: Battle CPUs in order of advantage (best matchup first)
  4. Attack Selection: Prioritize your super-effective attacks early in the sequence
  5. Expect Patterns: CPUs will likely use their primary type attack 60% of the time
Since CPUs are positioned at different locations, you can choose the order in which you encounter them. Battle the ones you have type advantage against first to build up experience!

Difficulty Analysis

CPU Strength Factors

Advantages:
  • 60% attack composition of primary type
  • Random selection is unpredictable
  • Extra attack when they have type advantage (66.7% strong attacks)
Disadvantages:
  • No strategic thinking - purely random selection
  • Cannot adapt to player patterns
  • Static positioning makes them avoidable
  • Predictable type ratios

Skill Level

CPUs provide medium difficulty opponents:
  • Easier than skilled human players (no strategy)
  • Harder than completely random opponents (type-weighted attacks)
  • Good for learning game mechanics
  • Excellent practice for understanding type advantages
CPU opponents serve as training encounters that teach players about type matchups and attack sequencing before facing human opponents with strategic planning.

Technical Implementation

CPU Identification

Players can identify CPU opponents through:
  • The isCPU flag in position data
  • CPU IDs following the pattern: cpu-{index}-{timestamp}
  • Static positioning (CPUs don’t move)

API Endpoints for CPU

EndpointPurpose
/mokepon/:playerId/cpu-attackGet CPU’s next random attack
/mokepon/:playerId/reset-attacksReset CPU attack pool for new round
/mokepon/:playerId/check-advantageCheck and apply type advantage bonus
All CPU logic is handled server-side, ensuring consistent behavior and preventing client-side manipulation.

Build docs developers (and LLMs) love