Skip to content

Instantly share code, notes, and snippets.

@mbutler
Last active February 17, 2025 18:59
Show Gist options
  • Save mbutler/c73b5b461120331b509234696bffc1d5 to your computer and use it in GitHub Desktop.
Save mbutler/c73b5b461120331b509234696bffc1d5 to your computer and use it in GitHub Desktop.
Attack and Cast Sequence Game
const iterations = 1000000 // number of simulated sequences. 1000000 is the right number
const sequenceLength = 7 // number of turns in each sequence
const orbCapacity = 7 // maximum number of orbs that can be held for each effect type
// types of orbs that can be produced or consumed
const effectTypes = [
'Piercing',
'Slashing',
'Crushing',
'Cleaving',
'Force',
'Poison',
'Necrotic',
'Psychic',
'Fire',
'Cold'
]
// Define a variety of abilities with cost (consumption), production, and damage values.
// These are sample numbers chosen to roughly follow our baseline idea:
// Generator moves deal ~100 damage and produce +1 orb; consumer moves use 1 orb and deliver ~150 damage.
/*
Ability Damage Calculation Formula:
-----------------------------------
Ability_Damage = Base_Damage + (Extra_Cost) × M
Where:
- Base_Damage is the default damage of the ability before any cost modifications.
- Extra_Cost is the number of additional orbs required beyond the base ability cost.
- M is the multiplier that depends on the type of ability:
- For consumer abilities (which spend orbs for high damage), M ≈ 50.
- For conversion abilities (which primarily shift orbs for combos), M ≈ 30.
This formula ensures that increasing an ability's cost results in a fair damage increase,
while keeping conversion abilities lower in direct damage since their true value comes from
enabling advanced combos.
*/
const abilities = [
// Base Weapon Attacks – Generators (produce a weapon orb, do baseline damage)
{ name: 'Dagger Strike', cost: {}, production: { Piercing: 1 }, damage: 10 },
{ name: 'Sword Slash', cost: {}, production: { Slashing: 1 }, damage: 10 },
{ name: 'Hammer Blow', cost: {}, production: { Crushing: 1 }, damage: 10 },
{ name: 'Axe Cleave', cost: {}, production: { Cleaving: 1 }, damage: 10 },
{ name: 'Spear Thrust', cost: {}, production: { Force: 1 }, damage: 10 },
{ name: 'Venom Fang', cost: {}, production: { Poison: 1 }, damage: 10 },
{ name: 'Soul Drain', cost: {}, production: { Necrotic: 1 }, damage: 10 },
{ name: 'Mind Shock', cost: {}, production: { Psychic: 1 }, damage: 10 },
{ name: 'Flame Brand', cost: {}, production: { Fire: 1 }, damage: 10 },
{ name: 'Glacial Jab', cost: {}, production: { Cold: 1 }, damage: 10 },
// Weapon Consumers – Use the produced orb to deliver enhanced damage
{ name: 'Power Dagger Strike', cost: { Piercing: 1 }, production: {}, damage: 15 },
{ name: 'Power Sword Slash', cost: { Slashing: 1 }, production: {}, damage: 15 },
{ name: 'Power Hammer Blow', cost: { Crushing: 1 }, production: {}, damage: 15 },
{ name: 'Power Axe Cleave', cost: { Cleaving: 1 }, production: {}, damage: 15 },
{ name: 'Power Spear Thrust', cost: { Force: 1 }, production: {}, damage: 15 },
{ name: 'Toxic Strike', cost: { Poison: 1 }, production: {}, damage: 15 },
{ name: 'Soul Rend', cost: { Necrotic: 1 }, production: {}, damage: 15 },
{ name: 'Mind Fracture', cost: { Psychic: 1 }, production: {}, damage: 15 },
{ name: 'Fire Slash', cost: { Fire: 1 }, production: {}, damage: 15 },
{ name: 'Icy Lance', cost: { Cold: 1 }, production: {}, damage: 15 },
// Augmented Abilities – Use the same weapon orb cost, but produce an ongoing effect orb instead
{ name: 'Venomous Dagger', cost: { Piercing: 1 }, production: { Poison: 1 }, damage: 14 },
{ name: 'Cursed Sword', cost: { Slashing: 1 }, production: { Necrotic: 1 }, damage: 14 },
{ name: 'Mad Hammer', cost: { Crushing: 1 }, production: { Psychic: 1 }, damage: 14 },
{ name: 'Savage Axe', cost: { Cleaving: 1 }, production: { Fire: 1 }, damage: 14 },
{ name: 'Cold Spear', cost: { Force: 1 }, production: { Cold: 1 }, damage: 14 },
{ name: 'Blighted Fang', cost: { Poison: 1 }, production: { Necrotic: 1 }, damage: 14 },
{ name: 'Doom Slash', cost: { Necrotic: 1 }, production: { Psychic: 1 }, damage: 14 },
{ name: 'Infernal Strike', cost: { Fire: 1 }, production: { Poison: 1 }, damage: 14 },
{ name: 'Glacial Crush', cost: { Cold: 1 }, production: { Psychic: 1 }, damage: 14 },
// Conversion Abilities (Weapon Conversions) – Switch one weapon orb type to another to set up combos
{ name: 'Refined Strike', cost: { Piercing: 1 }, production: { Slashing: 1 }, damage: 8 },
{ name: 'Reforging Swing', cost: { Slashing: 1 }, production: { Cleaving: 1 }, damage: 8 },
{ name: 'Brutal Shift', cost: { Crushing: 1 }, production: { Force: 1 }, damage: 8 },
{ name: 'Fluid Transition', cost: { Cleaving: 1 }, production: { Piercing: 1 }, damage: 8 },
{ name: 'Unsettling Twist', cost: { Force: 1 }, production: { Slashing: 1 }, damage: 8 },
{ name: 'Dark Infusion', cost: { Necrotic: 1 }, production: { Psychic: 1 }, damage: 8 },
{ name: 'Blazing Shift', cost: { Fire: 1 }, production: { Cleaving: 1 }, damage: 8 },
// Multi-Resource Heavy Hitters – Combo abilities that require both a weapon orb and an ongoing effect orb
{ name: 'Venom Blade', cost: { Piercing: 1, Poison: 1 }, production: {}, damage: 25 },
{ name: 'Bloodcurdling Slash', cost: { Slashing: 1, Fire: 1 }, production: {}, damage: 25 },
{ name: 'Grave Hammer', cost: { Crushing: 1, Necrotic: 1 }, production: {}, damage: 25 },
{ name: 'Mindbreaker Cleave', cost: { Cleaving: 1, Psychic: 1 }, production: {}, damage: 25 },
{ name: 'Cold Thrust', cost: { Force: 1, Cold: 1 }, production: {}, damage: 25 },
{ name: 'Abyssal Strike', cost: { Poison: 1, Necrotic: 1 }, production: {}, damage: 25 },
{ name: 'Infernal Wrath', cost: { Fire: 1, Poison: 1 }, production: {}, damage: 25 },
{ name: 'Frozen Execution', cost: { Cold: 1, Psychic: 1 }, production: {}, damage: 25 },
// Special Utility Abilities – Additional synergistic combos and unique effects
{ name: 'Serrated Assault', cost: { Slashing: 1, Piercing: 1 }, production: { Fire: 1 }, damage: 20 },
{ name: 'Crushing Overload', cost: { Crushing: 1, Cleaving: 1 }, production: { Psychic: 1 }, damage: 22 },
{ name: 'Venomous Onslaught', cost: { Piercing: 1, Slashing: 1, Poison: 1 }, production: {}, damage: 30 },
{ name: 'Cataclysmic Impact', cost: { Crushing: 1, Cleaving: 1, Force: 1 }, production: {}, damage: 35 },
{ name: 'Paralyzing Strike', cost: { Piercing: 1 }, production: { Psychic: 1 }, damage: 12 },
{ name: 'Blazing Execution', cost: { Fire: 1, Cleaving: 1 }, production: { Poison: 1 }, damage: 28 },
{ name: 'Frozen Mindbreak', cost: { Cold: 1, Psychic: 1 }, production: { Necrotic: 1 }, damage: 28 },
{ name: 'Doomblade Crush', cost: { Necrotic: 1, Crushing: 1 }, production: { Fire: 1 }, damage: 28 }
];
function getAbilityByName (name) {
return abilities.find(ability => ability.name === name)
}
// Function to test a specific sequence provided as an array of ability names
function testSequence (sequenceNames) {
// Map names to ability objects (ignoring any names not found)
const sequence = sequenceNames.map(name => getAbilityByName(name)).filter(ability => ability)
const result = simulateSequence(sequence)
console.log(`Test Sequence: ${sequenceNames.join(', ')}`)
console.log(`Total Damage: ${result.totalDamage}`)
console.log(`Detailed Log:`)
result.logs.forEach(log => console.log(log))
return result.totalDamage
}
// Checks if an ability can be executed given the current orb state
function canExecute (ability, orbState) {
for (const effect in ability.cost) {
if ((orbState[effect] || 0) < ability.cost[effect]) return false
}
return true
}
// Consumes orbs from the orb state according to an ability's cost
function consumeOrbs (ability, orbState) {
for (const effect in ability.cost) {
orbState[effect] -= ability.cost[effect]
}
}
// Produces orbs for an ability but respects the overall capacity
// If production would exceed orbCapacity, the excess is wasted (logged)
function produceOrbs (ability, orbState) {
let totalOrbs = effectTypes.reduce((sum, effect) => sum + (orbState[effect] || 0), 0)
let prodLog = ''
for (const effect in ability.production) {
const toAdd = ability.production[effect]
let available = orbCapacity - totalOrbs
let added = Math.min(toAdd, available)
orbState[effect] = (orbState[effect] || 0) + added
totalOrbs += added
if (added < toAdd) prodLog += `Wasted ${toAdd - added} ${effect} orb(s) due to capacity limit. `
}
return prodLog
}
// Simulate a sequence (combo) of abilities over a set number of turns.
// It logs each turn's action, the orb state, and damage applied.
function simulateSequence (sequence) {
// Initialize orb state for each effect type
let orbState = {}
for (const effect of effectTypes) {
orbState[effect] = 0
}
let totalDamage = 0
let logs = []
for (let i = 0; i < sequence.length; i++) {
const ability = sequence[i]
let turnLog = `Turn ${i + 1}: `
if (canExecute(ability, orbState)) {
consumeOrbs(ability, orbState)
const prodLog = produceOrbs(ability, orbState)
totalDamage += ability.damage
turnLog += `Executed ${ability.name} for ${ability.damage} damage. ${prodLog}`
}
else {
turnLog += `Skipped ${ability.name} (insufficient orbs)`
}
turnLog += ` | Orb State: ${JSON.stringify(orbState)}`
logs.push(turnLog)
}
return { totalDamage, logs, sequence: sequence.map(a => a.name) }
}
// Returns a random ability from our list
function getRandomAbility () {
const index = Math.floor(Math.random() * abilities.length)
return abilities[index]
}
// Just a list, not implemented in the simulation yet
const spells = [
// Protection Spells
{
name: "Aegis Barrier",
type: "Protection",
description: "Conjures a shimmering shield that blocks incoming damage for one turn.",
requiredSequence: ["blue", "green"],
duration: 1
},
{
name: "Reflective Ward",
type: "Protection",
description: "Creates a mirror-like aura that reflects spells back at their caster for one turn.",
requiredSequence: ["white", "purple"],
duration: 1
},
{
name: "Counter Hex",
type: "Protection",
description: "Nullifies any spells targeting the subject this turn.",
requiredSequence: ["white", "blue", "purple"],
duration: 0
},
// Summoning Spells
{
name: "Summon Sentinel",
type: "Summoning",
description: "Calls forth a stalwart guardian to defend and attack until destroyed.",
requiredSequence: ["green", "red", "purple"],
duration: -1 // Indefinite duration until the sentinel is defeated
},
{
name: "Summon Beast",
type: "Summoning",
description: "Summons a ferocious beast to fight for 3 turns.",
requiredSequence: ["red", "green", "blue"],
duration: 3
},
// Damaging Spells
{
name: "Arcane Missile",
type: "Damaging",
description: "Launches a focused bolt of elemental energy at the target.",
requiredSequence: ["white", "red"],
damage: 50,
duration: 0
},
{
name: "Lightning Fury",
type: "Damaging",
description: "Strikes the target with a searing bolt of lightning, bypassing resistances.",
requiredSequence: ["white", "red", "purple"],
damage: 100,
duration: 0
},
{
name: "Flame Burst",
type: "Damaging",
description: "Engulfs the target in a sudden explosion of fire.",
requiredSequence: ["red", "green"],
damage: 120,
duration: 0
},
// Enchantment Spells (Buffs & Debuffs)
{
name: "Adrenaline Surge",
type: "Enchantment",
description: "Boosts the target's attack power for 3 turns.",
requiredSequence: ["white", "blue", "purple"],
duration: 3
},
{
name: "Weaken Curse",
type: "Enchantment",
description: "Saps the target’s strength, reducing their damage output for 3 turns.",
requiredSequence: ["green", "purple", "red"],
duration: 3
},
{
name: "Elemental Ward",
type: "Enchantment",
description: "Grants resistance to a specific damage type for 3 turns.",
requiredSequence: ["blue", "green", "red"],
duration: 3
},
{
name: "Vulnerability Hex",
type: "Enchantment",
description: "Increases the target's susceptibility to damage for 3 turns.",
requiredSequence: ["purple", "red", "white"],
duration: 3
},
// Dispel Spell
{
name: "Dispel Aura",
type: "Dispel",
description: "Dispels all active spells and ongoing effects immediately.",
requiredSequence: ["white", "blue", "green", "purple"],
duration: 0
}
]
// Main simulation runner.
// It builds random sequences (of a given length) over many iterations and logs the results.
function runSimulation (iterations, sequenceLength) {
let results = []
for (let i = 0; i < iterations; i++) {
let sequence = []
for (let j = 0; j < sequenceLength; j++) {
sequence.push(getRandomAbility())
}
const result = simulateSequence(sequence)
results.push(result)
}
// Sort combos by totalDamage descending
results.sort((a, b) => b.totalDamage - a.totalDamage)
return results
}
console.log(`Running simulation with ${iterations} iterations and sequence length ${sequenceLength}\n`)
const results = runSimulation(iterations, sequenceLength)
// sort results by totalDamage descending
results.sort((a, b) => b.totalDamage - a.totalDamage)
// Log the top 10 combos by damage output
/*
console.log('Top 10 combos:')
for (let i = 0; i < Math.min(10, results.length); i++) {
console.log(`Rank ${i + 1}: Total Damage = ${results[i].totalDamage}`)
console.log(`Sequence: ${results[i].sequence.join(', ')}`)
console.log('Detailed Log:')
results[i].logs.forEach(log => console.log(log))
console.log('------------------------------')
}
*/
console.log('Best combo:')
console.log(`Rank 1: Total Damage = ${results[0].totalDamage}`)
console.log(`Sequence: ${results[0].sequence.join(', ')}\n\n\n`)
// Log the least 10 combos by damage output
/*
console.log('Least 10 combos:')
for (let i = results.length-1; i > results.length-11; i--) {
console.log(`Rank ${results.length-i}: Total Damage = ${results[i].totalDamage}`)
console.log(`Sequence: ${results[i].sequence.join(', ')}`)
console.log('Detailed Log:')
results[i].logs.forEach(log => console.log(log))
console.log('------------------------------')
}
*/
console.log('Worst combo:')
console.log(`Rank ${results.length}: Total Damage = ${results[results.length-1].totalDamage}`)
console.log(`Sequence: ${results[results.length-1].sequence.join(', ')}\n\n\n`)
//Log the average damage output
let totalDamage = 0
for (let i = 0; i < results.length; i++) {
totalDamage += results[i].totalDamage
}
console.log(`Average Damage = ${totalDamage/results.length}`)
//Log the median damage output
console.log(`Median Damage = ${results[iterations/2].totalDamage}`)
console.log(`Sequence: ${results[iterations/2].sequence.join(', ')}\n\n\n`)
//Log an arbitrary sequence
console.log(`Damage = ${results[500].totalDamage}`)
console.log(`Sequence: ${results[500].sequence.join(', ')}\n\n\n`)
// make an array from Axe Cleave, Hammer Blow, Spear Thrust, Dagger Strike, Cataclysmic Impact, Axe Cleave, Savage Axe
const mySequence = ['Axe Cleave', 'Hammer Blow', 'Spear Thrust', 'Dagger Strike', 'Cataclysmic Impact', 'Axe Cleave', 'Savage Axe']
testSequence(mySequence)
@mbutler
Copy link
Author

mbutler commented Feb 17, 2025

Below is a comprehensive, detailed explanation of the game’s rules and mechanics—the product of merging an idle combat system with a dual-layer ability/spell augmentation system.


1. Overview

In this game you’re in control of a fighter whose combat prowess comes from two interconnected systems:

  • Weapon Attacks: These are your base abilities that represent physical strikes—like dagger stabs, sword slashes, or hammer blows. Each attack produces a “weapon orb” (of a specific type such as Piercing, Slashing, Crushing, Cleaving, or Impaling) and deals direct damage.

  • Augmented Spellcasting: Every ability also has an optional “augment” mode. Instead of producing a standard weapon orb, you can choose to “augment” an ability. In doing so, you forgo the normal orb to instead produce an “ongoing effect orb” (such as Poison, Necrotic, Psychic, Bleeding, or Corrosive). More importantly, each augment is associated with an elemental input—one of the five elements: air, water, earth, fire, or spirit. By inputting a sequence of these elements over successive turns, you build up a spell. Once the sequence matches a predefined pattern, the spell is cast, granting powerful defensive, offensive, summoning, or utility effects.

Key twist: When a spell is cast, all elemental augments are cleared from your current chain, resetting your spell progress.


2. Turn Action Economy

Each turn, you get exactly one action. You must choose from one of the following:

  • Play a Normal Ability: Use one of your ability cards in its standard mode. This produces a weapon orb (adding to your pool of physical attack resources) and deals its designated direct damage.

  • Augment an Ability: Instead of the normal attack, you choose to play the ability in its augmented mode. In this case, you forgo the immediate weapon orb and damage; instead, you generate an ongoing effect orb and—critically—input one elemental value (air, water, earth, fire, or spirit) that contributes to a spell sequence. You build this elemental sequence over several turns if you want to cast a spell.

  • Activation (Implicit): Once your augment sequence reaches the required pattern for a spell (for example, a specific series like [air, water, earth] might trigger a protective spell), the spell is automatically cast. When that happens, all elemental augments you’ve accumulated are removed from your chain, and the spell’s effect is applied.

This design forces a strategic trade-off. On any turn you decide between immediate, reliable damage (normal ability) or investing in longer-term, high-impact spell effects (augmenting). Both paths have their advantages, and the interplay is at the heart of the game’s tactical depth.


3. The Orb System

Your resources come in the form of “orbs,” which are tracked separately by type:

Weapon Orbs

Produced by standard (non-augmented) abilities. They are associated with the following damage types:

  • Piercing
  • Slashing
  • Crushing
  • Cleaving
  • Impaling

Each weapon orb represents a physical attack and is used when executing enhanced “consumer” abilities or when combining for special combo moves.

Ongoing Effect Orbs

Produced when you choose an augmented mode. They represent status or over-time effects:

  • Poison
  • Necrotic
  • Psychic
  • Bleeding
  • Corrosive

These orbs set up the possibility for “combo” moves—special abilities that require both a weapon orb and an ongoing orb (for example, a “Venom Blade” might require a dagger’s piercing orb plus a poison orb to unleash extra damage with additional effects).


4. Abilities & Augments

Your ability cards (or “attacks”) come in several flavors:

  • Base Attacks:
    Examples include “Dagger Strike,” “Sword Slash,” etc. They cost nothing to activate, produce a specific weapon orb, and deal a standard amount of damage (e.g., 100 damage).

  • Weapon Consumers:
    These abilities consume an existing weapon orb to deliver an enhanced, more powerful attack (e.g., “Power Dagger Strike” that costs a Piercing orb and deals 150 damage).

  • Augmented Attacks:
    These are the same ability cards, but you choose to play them in augment mode. Instead of producing a weapon orb, they produce an ongoing effect orb (e.g., “Venomous Dagger” consumes a Piercing orb and produces a Poison orb, dealing slightly altered damage). In augment mode, the ability also “registers” an elemental input as part of your spellcasting chain.

  • Conversion Abilities:
    Some abilities allow you to convert one type of orb into another (for instance, shifting a Piercing orb to become a Slashing orb) to set up synergy for combos.

  • Multi-Resource Combos (Heavy Hitters):
    These require a combination of orbs—one from the weapon category and one from the ongoing category—to execute a devastating combo (like “Venom Blade” that needs both a Piercing orb and a Poison orb). Their names often reference the underlying components.


5. Spells as Augments

The spell system is integrated into your ability augment choices. Here’s how it works:

  • Elemental Input:
    When playing an ability in augment mode, you choose to use its augment option. This lets you input one element from the set [air, water, earth, fire, spirit]. These inputs are recorded in order across turns.

  • Spell Sequences:
    Each spell is defined by a required sequence of elements. For example, a spell might require the sequence [air, water, earth]. When your augment chain matches this sequence, the spell is triggered automatically.

  • Spell Effects:
    Spells come in various types:

    • Protection Spells: Provide shields, reflect spells, or nullify enemy magic for a turn.
    • Summoning Spells: Bring forth allies (like guardians or beasts) that fight alongside you.
    • Damaging Spells: Directly deal damage, often with additional effects such as bypassing resistances.
    • Enchantment Spells: Provide buffs to you or debuffs to your opponent, such as increased attack power or lowered defenses, as well as resistances and vulnerabilities.
  • Reset Mechanic:
    Once a spell is successfully cast (i.e., the required elemental sequence is completed and its effect is applied), all accumulated elemental augments are immediately removed. This “reset” prevents spell chains from becoming overly long and forces you to re-invest in augmenting future abilities.


6. Collecting and Sequencing Abilities

  • Collection/Deck Building:
    Abilities are collected over time. You might obtain them via progression, loot drops, or other means. You then arrange your chosen abilities in a sequence (or hotbar) that reflects your preferred playstyle.

  • Pre-Planning vs. On-the-Fly Choice:
    You can design your deck to favor fast, high-damage physical attacks or invest in augment chains for strategic spellcasting. Some abilities may even allow you to mix both modes to create hybrid strategies.

  • Execution Flow:
    In each battle, your pre-determined sequence of abilities is executed turn by turn. For every ability, you decide whether to use its normal (weapon) mode or its augmented (spellcasting) mode. The overall performance depends on your ordering and the trade-offs between immediate damage and building up powerful spells.


7. Combo Synergy and Tactical Depth

The game rewards players who:

  • Plan Effective Sequences:
    The order in which you play abilities affects orb flow, combo potential, and spell readiness.

  • Exploit Synergy:
    Certain combos require matching weapon orbs with corresponding ongoing effect orbs. For example, a “Venom Blade” might only trigger if you’ve successfully generated both a Piercing orb from a dagger attack and a Poison orb from an augmented version of that ability.

  • Balance Immediate vs. Long-Term Payoffs:
    Normal abilities offer steady, reliable damage. Augmented abilities sacrifice some of that reliability for the chance to build up spell effects that might, for instance, shield you from damage, summon reinforcements, or debilitate your enemy.


8. Summary of Gameplay Flow

  1. Setup:

    • Build a deck or sequence of abilities from your collection.
    • Choose a mix of normal and augmented modes based on your strategy.
  2. Turn Structure:

    • On each turn, choose one action:
      • Play a normal ability (generates a weapon orb, deals direct damage).
      • Use an ability in augment mode (generates an ongoing orb and inputs an element into your spell chain).
    • The game automatically resolves the sequence turn by turn.
  3. Spell Casting:

    • As you augment abilities, an elemental sequence builds.
    • When your sequence matches a defined spell’s required pattern, the spell is cast immediately.
    • All elemental inputs (augments) are then cleared from your current chain, and the spell’s effect (defense, summoning, damage, or buff/debuff) is applied.
  4. Resolution:

    • Abilities resolve in order, taking into account available orb resources.
    • Special combo abilities may trigger if the required orb types are present.
    • The interplay of direct damage and spell effects determines the battle outcome.

9. Conclusion

This game blends fast-paced, idle combat with deep tactical decision-making. You must balance the immediacy of physical attacks against the long-term benefits of spellcasting. Every turn forces you to choose: secure direct damage or invest in building a spell that might turn the tide of battle. Your success hinges on the strategic ordering of abilities, efficient orb management, and clever use of elemental augments to unleash powerful, combo-driven spells.

This multifaceted approach creates an engaging, evolving combat narrative that rewards planning, adaptability, and creative synergy—ensuring every battle feels like a dynamic and epic clash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment