Rift Logic

Rift Logic

A small C++-style language for proving League claims from the game's own data. Build champions with items, calculate anything the game calculates, and write rules that argue for and against a matchup. Every result shows its working.

Program

Runs as you type · Ctrl/⌘ + Enter to re-run

Results

Cheatsheet

C++ you'd sketch on a whiteboard: statements end in ;, // and /* */ comments, auto works anywhere, and objects are copied on assignment. There are no pointers, headers or templates. Champion and item names are written without spaces or punctuation (KSante, MissFortune). For items, any unique start of the name works: Rabadon, Ludens, Sorc.

Making things

Champion a = Syndra(11);A champion at level 11. Syndra alone uses defaultLevel (11).
defaultLevel = 6;Change the level of bare champion names from here on.
a.items.add(Ludens, Sorc);Give items. a.items.remove(Sorc); takes one away.
Champion b = a.with(Rabadon);A copy with more items (items or ItemSets).
Champion c = a.at(16);A copy at another level.
a.Q.rank = 3;Choose a rank. Otherwise it's the highest possible at the level.
ItemSet s = {Ludens, Sorc};A set of items. s.add(x), s.remove(x), s + x.
TeamComp blue = {Ornn(11), Vi(11), a};A team. blue.add(x), blue[0], blue.size().
auto all = champions(Assassin);Every champion (of a class); items() is every finished item.

What a Champion can tell you

a.ap a.ad a.bonusad a.baseadAt its level, with its items (Rabadon's multiplier included).
a.hp a.bonushp a.armor a.mr a.manaAlso bonusarmor, bonusmr, basehp.
a.ehpphysical a.ehpmagicEffective health: hp × (1 + resist / 100).
a.haste a.ms a.as a.crit a.rangeAbility haste, move speed, attack speed, crit chance, attack range.
a.lethality a.magicpen a.magicpenpct a.armorpenpctPenetration.
a.aa a.dps a.gold a.level a.nameOne auto; auto-attack DPS with crit; item cost; level; name.
a.combo(Q, E, R, vs: t)Each ability's damage once, after t's resistances (omit vs for raw).
a.proc(Ludens, vs: t)An item's own damage formula, using a's stats.
a.is(Mage) a.has(stun)Class and tag checks (bool).
a.cdOf(untargetable) a.cdmaxOf(stun) a.rangeOf(dash)Cooldown (rank 1 / max rank) or range of whichever ability has that tag.
a.attack a.magic a.difficulty a.meleeRiot's 1–10 ratings; melee is true or false.

Abilities, items, sets

a.Q.damage a.Q.damage(vs: t)Main damage from the tooltip (parts the tooltip adds are summed), raw or after resistances.
a.Q.cd a.Q.cdbase a.Q.cost a.Q.range a.Q.rankCooldown after haste, before haste, mana, cast range, rank.
a.R.value("maxdamagecalc", vs: t)Any named formula or value. a.R.maxdamagecalc also works. The inspector lists them.
a.E.has(untargetable) a.E.nameTag check on one ability; its name.
Ludens.gold Ludens.ap Ludens.basedamageAn item's cost, stats and named values.
s.gold s.ap s.size() s.contains(Ludens)Totals and membership for an ItemSet.

Results

assert(a.Q.damage > b.Q.damage);Proved or refuted, with both sides' working.
assert(x, "label");Same, with your own title.
print(a.ap, a.Q.cd, "text");Show values and how they were calculated.
prove beats(Fizz(6), Syndra(6));Run your rules: proved, refuted, contested or no rule applies.
prove forall (Champion c : champions(Assassin)) beats(c, Syndra);The same claim for every champion in a list.

Code

double f(Champion c, Champion t) { return …; }Functions. Types: auto int double bool string Champion Item ItemSet TeamComp Ability.
if (…) { … } else { … }Also while, for (int i = 0; i < n; i++), break, continue.
for (Champion c : blue) { … }Loop over a TeamComp, ItemSet or list (copies; Champion& c to edit in place).
+ - * / % == != < <= > >= && || !Operators. 30% means 0.3. +=, ++ work. All arithmetic is decimal (2 / 5 is 0.4); int only truncates when stored.
min max abs floor ceil round sqrt powMaths.

Rules: the one addition to C++

rule dive(Champion x, Champion y) -> beats(x, y) [1] { return x.is(Assassin) && x.has(mobile); }An argument that beats(x, y) is true, when the body returns true. [1] is its strength (default 1).
rule stop(…) -> !beats(x, y) [2] { … }An argument against. The strongest argument wins; equal strengths on both sides make it contested.
beats(a, b)Use a claim inside any function or rule, as a bool.
Syndra.R.addTag(targeted);Add a tag the data missed. Every proof that uses it lists it as an assumption.
Syndra.W.removeTag(pull); Fizz.E.setCooldown(10); Fizz.addClass(Mage);Correct the data the same way.

A rule body written as return c1 && c2 && c3; is shown condition by condition, with the evidence for each.

Runes

Champion a = Zed(11).with(Electrocute, SuddenImpact, AdaptiveForce);Runes go in with() like items, or a.runes.add(Conqueror);. Shards: AdaptiveForce AttackSpeed AbilityHaste Health HealthScaling Armor MagicResist.
Conqueror.descriptionThe rune's text; its numbers are what the simulator uses. runes() lists every rune.
gameMinute = 25;For time-based runes (Gathering Storm, Conditioning). Default 20.

In stats: shards, Eyeball Collection and other stacking runes (full stacks, noted), Absolute Focus, Gathering Storm, Transcendence, Legend runes, Conditioning, Overgrowth, Revitalize, Jack of All Trades, Axiom Arcanist. In fights: Electrocute, Dark Harvest (a.souls = 10;), Conqueror, Lethal Tempo, Press the Attack, Fleet Footwork, Grasp, Aftershock, Aery, Comet, First Strike, Hail of Blades, Guardian, Deathfire Touch, Cheap Shot, Taste of Blood, Sudden Impact, Scorch, Second Wind, Bone Plating, Triumph, Last Stand, Coup de Grace, Cut Down. Values that range “by level” are interpolated evenly from level 1 to 18.

Healing and shielding

soraka.W.heal soraka.W.heal(target: x)Heal amount with heal & shield power, and what the target receives (Spirit Visage etc.).
lulu.E.shield(target: x)Same for shields.
soraka.W.healTargetWho it can reach, read from the tooltip: caster only, another ally, an ally or self, caster and one ally, every ally.
a.healpower a.healIn a.lifesteal a.omnivampOutgoing heal & shield power; extra healing received; vamp.

Fights over time

canKill(a, t, 3)Can a kill t within 3 seconds? t doesn't hit back unless fightBack: true.
canKill(a, t, 5, healers: soraka)With healers on t's side (a Champion or TeamComp). They only heal and shield.
timeToKill(a, t, healers: …, fightBack: true)Seconds until t dies (up to 60; within: 120 for longer).
Fight f = fight(blue, red, 10);Both sides fight for 10 seconds. A side is a Champion or TeamComp.
f.winner f.duration f.log1, 2 or 0 (both sides alive); when it ended; every event.
f.alive(x) f.hp(x) f.hpPercent(x) f.deathTime(x)How one champion ended up.
f.dealt(x) f.taken(x) f.healed(x) f.shielded(x) f.received(x)Damage and healing totals for one champion.
f.survivors(1) f.deaths(blue) f.totalHealing(2)Per side: 1, 2 or the TeamComp you passed in.
x.healPolicy = "save";Who x heals and shields: "lowest" health (default), "save" (whoever will die soonest at the current damage rate), "self", or a Champion to pocket.
Combo c = {Q, AA, E, AA, R}; c + WA custom sequence of abilities and auto attacks (AA).
a.perform(c, vs: t, wait: true)Runs it on t: .damage .time .killed .hpLeft .dps .steps. Waits for cooldowns unless wait: false; healers: and fightBack: work as in canKill.
x.target = y; x.passive = true; x.rotation = "QEWR";Focus a champion; only heal and shield; ability priority (default R, Q, E, W).

The simulator runs in 0.05s steps. Everyone uses an ability as soon as it's off cooldown (support abilities when an ally needs them; ultimate heals are saved for allies below 40% health) and otherwise auto-attacks at their attack speed, with runes, item passives, lifesteal, grievous wounds, shields, Zhonya's and Guardian Angel applied as they trigger.

Practice-tool target dummy

Dummy d = Dummy();The in-game dummy: 1000 health, 0 armor, 0 MR, level 1 (game file PracticeTool_TargetDummy, wiki Practice Tool).
Dummy(hp: 3000, resists: 50)After the cheats: Add 100 Max HP adds bonus health, Add 10 Resistances adds bonus armor and MR. armor:/mr: set them apart (not possible in game; the output says so).
x.Q.damage(vs: d) x.proc(Kraken, vs: d) x.perform(c, vs: d)Use it wherever a target Champion goes. It counts as a champion (Electrocute, Lord Dominik's bonus-health amp, …).
d.hp d.armor d.mr d.hitboxIts settings; radius grows 65 → 130 with bonus health.
r.killed r.killTimeIt can't die (stays at 1 health): these say when damage taken reached its health. After 3 s without damage it restores to full.
print(r.floatingText); r.stepDamage(2) r.hit(3)Every damage instance with exact value, floor and round, to compare with the numbers in game.
canDodge(d, …)An error: the dummy never moves.

What the numbers include

Included: base stats and growth, item stats, the runes and ~40 item passives listed above, every ability formula as the game stores it (per rank, per level, stat ratios), heals and shields from tooltips, haste on cooldowns, armor and MR with flat and percent penetration and lethality. Not included: positioning and movement, whether abilities land, crowd control stopping actions, mana, special ability mechanics beyond the tooltip formula (clones, stored damage, stacks), and item passives not listed (named in the assumptions whenever a build has one). A result holds for this model, and it lists every assumption the model made.

Inspector