To run this simulation as a Node.js application, we will build a server that
manages the centralized game state and uses a Pub/Sub event pattern
(simulating two-way asynchronous socket communication). This layout ensures
that you can drop in true WebSockets (socket.io or ws) later without modifying
your game core.
We will write a fully functional script containing the BotBrain AI engine, the
NetworkSocketSimulator, and an automated runner to watch 3 AIs completely
obliterate each other.
1. The Dynamic AI Decision Tree (BotBrain)
A smart-looking AI shouldn't just act at random. The BotBrain evaluates two
separate decisions every turn:
The Purchase Decision: Spends currency dynamically. It ensures it keeps an
even ratio of delivery vehicles to payloads, and prioritizes building defenses
if it is actively getting bombed.
The Intent Decision: Chooses an action based on its personality. Aggressive
characters seek revenge against whoever last hit them; defensive characters
focus on subversion via propaganda.
2. Full Node.js Simulation Code
Save the code below as server.js and run it instantly using node server.js. It
requires no external dependencies.
/ ========================================== // 1. GAME LOGICAL REGISTRIES &
DATA SCHEMA // ========================================== const
CHARACTER_REGISTRY = [ { id: "infidel_castro", name: "Infidel Castro", perks:
{ casualtyMultiplier: 1.2, propagandaBonus: 1.0 }, ai: { aggro: 0.8,
propaganda: 0.2 } }, { id: "mao_down", name: "Mao Down", perks: {
casualtyMultiplier: 1.0, propagandaBonus: 1.3 }, ai: { aggro: 0.2, propaganda:
0.8 } }, { id: "col_borstch", name: "Col. Borstch", perks: { casualtyMultiplier
: 1.0, propagandaBonus: 1.0 }, ai: { aggro: 0.4, propaganda: 0.4 } } ]; const
ITEM_REGISTRY = { short_range_rocket: { name: "Short-Range Rocket", type:
"vehicle", cost: 3, travelDelay: 0, evasion: 0.3 }, icbm: { name: "ICBM
Rocket", type: "vehicle", cost: 6, travelDelay: 1, evasion: 0.5 },
warhead_10mt: { name: "10MT Warhead", type: "payload", cost: 2, minKills: 8,
maxKills: 15 }, warhead_50mt: { name: "50MT Warhead", type: "payload", cost:
5, minKills: 18, maxKills: 30 }, abm_battery: { name: "ABM Battery", type:
"defense", cost: 3, permanent: false, success: 0.7 }, sdi_laser: { name: "SDI
Laser Grid", type: "defense", cost: 8, permanent: true, success: 0.4 } }; //
========================================== // 2. THE AI DECISION ENGINE
(BotBrain) // ========================================== const BotBrain = {
/** * AI Phase A: Spend resources before actions are locked in */
executeAIPurchasePhase(player) { let activeShopping = true; while
(activeShopping && player.resourcePoints >= 3) { let choice = null; // 1.
Desperation defense buy if (player.inventory.abm_battery === 0 &&
player.resourcePoints >= 3) { choice = "abm_battery"; } // 2. Long-term
defense buy else if (player.inventory.sdi_laser === 0 && player.resourcePoints
>= 8 && Math.random() > 0.5) { choice = "sdi_laser"; } // 3. Balance matching
ammo to rockets else { const totalVehicles = player.inventory.short_range_rocke
t + player.inventory.icbm; const totalPayloads = player.inventory.warhead_10mt
+ player.inventory.warhead_50mt; if (totalVehicles <=
totalPayloads) { choice = player.resourcePoints >= 6 && Math.random() > 0.5 ?
"icbm" : "short_range_rocket"; } else { choice = player.resourcePoints >= 5 &&
Math.random() > 0.5 ? "warhead_50mt" : "warhead_10mt"; } } if (choice &&
player.resourcePoints >= ITEM_REGISTRY[choice].cost) { player.resourcePoints
-= ITEM_REGISTRY[choice].cost; player.inventory[choice]++; console.log(`
[MARKET] ${player.name} purchased a ${ITEM_REGISTRY[choice].name}.`); } else {
activeShopping = false; } } }, /** * AI Phase B: Formulate an action intent
payload */ generateIntent(player, allPlayers) { const profile =
player.character.ai; const validTargets = allPlayers.filter(p => p.id !==
player.id && p.cities.some(c => c.intact)); // Pick a random target from
survivors const target = validTargets[Math.floor(Math.random() *
validTargets.length)]; if (!target) return { playerId: player.id, actionId:
"FORTIFY" }; const roll = Math.random(); // 1. Try to shoot if ammo exists
const hasVehicle = player.inventory.short_range_rocket > 0 ||
player.inventory.icbm > 0; const hasPayload = player.inventory.warhead_10mt >
0 || player.inventory.warhead_50mt > 0; if (roll < profile.aggro && hasVehicle
&& hasPayload) { const vId = player.inventory.icbm > 0 ? "icbm" :
"short_range_rocket"; const pId = player.inventory.warhead_50mt > 0 ?
"warhead_50mt" : "warhead_10mt"; return { playerId: player.id, actionId:
"LAUNCH_STRIKE", targetPlayerId: target.id, assets: { vehicleId: vId,
payloadId: pId } }; } // 2. Try propaganda subversion else if (roll <
(profile.aggro + profile.propaganda)) { return { playerId: player.id,
actionId: "PROPAGANDA", targetPlayerId: target.id }; } // 3. Fallback to
active screening else if (player.inventory.abm_battery > 0) { return {
playerId: player.id, actionId: "ACTIVATE_DEFENSE" }; } return { playerId:
player.id, actionId: "FORTIFY" }; } }; // =====================================
===== // 3. TWO-WAY SOCKET EMULATION LAYER // =================================
========= const NetworkSocketSimulator = { listeners: {}, on(event, call
back) { this.listeners[event] = callback; }, // Simulates an inbound message
coming into the server from a remote player connection emitToServer(event,
data) { if (this.listeners[event]) { // Decouple execution time to simulate
network latency thread gaps setImmediate(() => this.listeners[event](data)); }
}, // Simulates broadcasting updates back out down the sockets to clients
broadcastToClients(event, data) { // In real life: io.emit(event, data); if
(event === "LOG_EVENT") { console.log(data.text); } } }; // ===================
======================= // 4. CENTRAL STRATEGIC GAME ENGINE //
========================================== const GameEngine = { state: {
round: 1, players: [], queue: [], active: true }, startMatch(configs) {
this.state.players = configs.map((cfg, i) => ({ id: i, name: cfg.name,
connectionType: cfg.type, character: CHARACTER_REGISTRY.find(c => c.id ===
cfg.charId), resourcePoints: 8, inventory: { short_range_rocket: 1, icbm: 0,
warhead_10mt: 1, warhead_50mt: 0, abm_battery: 1, sdi_laser: 0 }, cities: [ {
name: "Metropolis", population: 25, intact: true }, { name: "Outpost",
population: 15, intact: true } ], currentTurnIntent: null })); // Wire up
network event inbound intake hook NetworkSocketSimulator.on("SUBMIT_INTENT",
(payload) => this.handleIncomingIntent(payload)); this.nextRound(); },
nextRound() { if (!this.state.active) return; NetworkSocketSimulator.broadcastT
oClients("LOG_EVENT", { text: `
=== ROUND ${this.state.round} ===` }); // Process AI Upkeep Production
purchases instantly this.state.players.forEach(p => { if (!p.cities.some(c =>
c.intact)) return; if (p.connectionType === "ai") BotBrain.executeAIPurchasePha
se(p); }); // Request intents this.state.players.forEach(p => { if
(!p.cities.some(c => c.intact)) return; if (p.connectionType === "ai") { const
intent = BotBrain.generateIntent(p, this.state.players); // Push it down our
abstract communication pipeline channel NetworkSocketSimulator.emitToServer("SU
BMIT_INTENT", intent); } }); }, handleIncomingIntent(intent) { const
player = this.state.players[intent.playerId]; player.currentTurnIntent =
intent; // Check if all surviving channels filled slots const ready =
this.state.players.every(p => p.currentTurnIntent !== null || !p.cities.some(c
=> c.intact)); if (ready) this.resolveTurn(); }, resolveTurn() { const players
= this.state.players; const shields = new Set(); // 1. Consume stocks & cache
active defenses players.forEach(p => { const intent = p.currentTurnIntent; if
(!intent) return; if (intent.actionId === "ACTIVATE_DEFENSE") {
p.inventory.abm_battery--; shields.add(p.id); } if (intent.actionId ===
"LAUNCH_STRIKE") { p.inventory[intent.assets.vehicleId]--; p.inventory[intent.a
ssets.payloadId]--; } }); // 2. Resolve Propaganda players.forEach(p => {
const intent = p.currentTurnIntent; if (intent && intent.actionId ===
"PROPAGANDA") { const target = players[intent.targetPlayerId]; const base =
Math.floor(Math.random() * 5) + 2; let actual = Math.min(target.cities.reduce((
s, c) => s + c.population, 0), base); this.inflictCasualties(target, actual);
if (p.cities[0].intact) p.cities[0].population += actual; NetworkSocketSimulato
r.broadcastToClients("LOG_EVENT", { text: `" ${p.name} subverted ${actual}M
citizens from ${target.name}.` }); } }); // 3. Move Strikes into Flight Queue
players.forEach(p => { const intent = p.currentTurnIntent; if (intent &&
intent.actionId === "LAUNCH_STRIKE") { this.state.queue.push({ targetId:
intent.targetPlayerId, payloadId: intent.assets.payloadId, vehicleId:
intent.assets.vehicleId, ttl: ITEM_REGISTRY[intent.assets.vehicleId].travelDela
y }); } }); // 4. Detonate landing countdowns for (let i = this.state.queue.len
gth - 1; i >= 0; i--) { const m = this.state.queue[i]; if (m.ttl === 0) {
const target = players[m.targetId]; let intercepted = false; if
(target.inventory.sdi_laser > 0 && Math.random() < ITEM_REGISTRY.sdi_laser.succ
ess) intercepted = true;
if (shields.has(target.id) && Math.random() < ITEM_REGISTRY.abm_battery.success
) intercepted = true;
if (intercepted) {
NetworkSocketSimulator.broadcastToClients("LOG_EVENT", { text: !o8 Missile
intercepted clean out of sky above ${target.name}! });
} else {
const stats = ITEM_REGISTRY[m.payloadId];
const kills = Math.floor(Math.random() * (stats.maxKills - stats.minKills)) +
stats.minKills;
NetworkSocketSimulator.broadcastToClients("LOG_EVENT", { text: "o8 BOOM!