Consolidate all .allium specs into specs/

- Move .pi/specs/ files into specs/ (healing, factions, merged magical-objects)
- Move src/*.allium files into specs/ (levels, changing-level)
- Delete .pi/specs/ directory
- Document specs/ convention in AGENTS.md
This commit is contained in:
2026-06-15 08:16:12 +01:00
parent 39839dc594
commit 31984bbd9d
7 changed files with 7 additions and 175 deletions
+120
View File
@@ -0,0 +1,120 @@
-- allium: 3
-- allium: changing-level
------------------------------------------------------------
-- Value Types
------------------------------------------------------------
value Faction {
name: String
}
value Health {
value: Integer
requires: value >= 0
}
value Level {
value: Integer
requires: value >= 1 and value <= 10
}
value Damage {
value: Integer
requires: value >= 0
}
------------------------------------------------------------
-- Enumerations
------------------------------------------------------------
enum Status {
alive | dead
}
------------------------------------------------------------
-- Entities
------------------------------------------------------------
entity Character {
name: String
health: Health
status: Status
level: Level
factions: Set<Faction>
totalDamageTaken: Damage
factionsJoined: Set<Faction>
}
------------------------------------------------------------
-- Rules
------------------------------------------------------------
rule DamageIsAccumulated {
when: Character.dealDamage(attacker, target, damage)
requires: target.status = alive
ensures: target.totalDamageTaken.value = old(target.totalDamageTaken.value) + damage.value
}
rule LevelUpFromDamage {
when: Character.dealDamage(attacker, target, damage)
requires: target.status = alive
requires: old(target.totalDamageTaken.value) + damage.value >= 1000 * (target.level.value + 1) * (target.level.value + 2) / 2
ensures: target.level.value = target.level.value + 1
ensures: target.totalDamageTaken.value = old(target.totalDamageTaken.value) + damage.value
}
rule LevelUpFromFaction {
when: Character.joinFaction(character, faction)
requires: character.status = alive
requires: old(character.factionsJoined.size) + 1 >= 3 * (character.level.value + 1)
ensures: character.level.value = character.level.value + 1
ensures: character.factionsJoined = old(character.factionsJoined) + {faction}
}
rule MaxLevelCappedOnDamage {
when: Character.dealDamage(attacker, target, damage)
requires: target.status = alive
requires: target.level.value = 10
ensures: target.level.value = 10
}
rule MaxLevelCappedOnFaction {
when: Character.joinFaction(character, faction)
requires: character.status = alive
requires: character.level.value = 10
ensures: character.level.value = 10
}
rule DeadCannotLevelUpFromDamage {
when: Character.dealDamage(attacker, target, damage)
requires: target.status = dead
ensures: target.level.value = old(target.level.value)
ensures: target.totalDamageTaken.value = old(target.totalDamageTaken.value)
}
rule DeadCannotLevelUpFromFaction {
when: Character.joinFaction(character, faction)
requires: character.status = dead
ensures: character.level.value = old(character.level.value)
}
------------------------------------------------------------
-- Invariants
------------------------------------------------------------
invariant LevelBounded {
for c in Characters:
c.level.value >= 1 and c.level.value <= 10
}
invariant DamageTotalNonNegative {
for c in Characters:
c.totalDamageTaken.value >= 0
}
invariant FactionsJoinedNonNegative {
for c in Characters:
c.factionsJoined.size >= 0
}
+105
View File
@@ -0,0 +1,105 @@
-- allium: 3
-- allium: factions
------------------------------------------------------------
-- Value Types
------------------------------------------------------------
value Faction {
name: String
}
value Health {
value: Integer
requires: value >= 0
}
value Level {
value: Integer
requires: value >= 1 and value <= 10
}
enum Status {
alive | dead
}
------------------------------------------------------------
-- Entities
------------------------------------------------------------
entity Character {
name: String
health: Health
status: Status
level: Level
factions: Set<Faction>
}
------------------------------------------------------------
-- Rules
------------------------------------------------------------
rule JoinFaction {
when: Character.joinFaction(character, faction)
requires: character.status = alive
ensures: character.factions = old(character.factions) + {faction}
}
rule LeaveFaction {
when: Character.leaveFaction(character, faction)
requires: character.status = alive
requires: faction in character.factions
ensures: character.factions = old(character.factions) - {faction}
}
rule AllyDamageForbidden {
when: Character.dealDamage(attacker, target, damage)
requires: attacker.isAllyOf(target)
ensures:
target.health.value = old(target.health.value)
target.status = old(target.status)
}
rule AllyHealAllowed {
when: Character.healAlly(healer, ally, amount)
requires: healer.status = alive
requires: ally.status = alive
requires: healer.isAllyOf(ally)
ensures: ally.health.value = min(ally.health.value + amount, maxHealthForLevel(ally.level))
}
rule NonAllyHealForbidden {
when: Character.healAlly(healer, ally, amount)
requires: not healer.isAllyOf(ally)
ensures:
ally.health.value = old(ally.health.value)
ally.status = old(ally.status)
}
rule DeadCannotJoinFaction {
when: Character.joinFaction(character, faction)
requires: character.status = dead
ensures: character.factions = old(character.factions)
}
rule DeadCannotLeaveFaction {
when: Character.leaveFaction(character, faction)
requires: character.status = dead
ensures: character.factions = old(character.factions)
}
------------------------------------------------------------
-- Invariants
------------------------------------------------------------
invariant FactionsAlwaysValid {
for c in Characters:
for f in c.factions:
f.name.length > 0
}
invariant SelfNotAlly {
for c in Characters:
not c.isAllyOf(c)
}
+44
View File
@@ -0,0 +1,44 @@
-- allium: 3
-- allium: healing
------------------------------------------------------------
-- Entities and Variants
------------------------------------------------------------
entity Character {
name: String
health: Health
status: alive | dead
level: Level
factions: Set<Faction>
}
------------------------------------------------------------
-- Rules
------------------------------------------------------------
rule SelfHealIncreasesHealth {
when: CharacterHealsSelf(character, amount)
requires: character.status = alive
ensures: character.health = min(character.health + amount, maxHealthForLevel(character.level))
}
------------------------------------------------------------
-- Invariants
------------------------------------------------------------
invariant HealthNonNegative {
for c in Characters:
c.health >= 0
}
invariant HealthNeverExceedsLevelCap {
for c in Characters:
c.health <= maxHealthForLevel(c.level)
}
invariant DeadCannotHeal {
for c in Characters:
c.status = dead implies not CharacterHealsSelf(c, _)
}
+44
View File
@@ -0,0 +1,44 @@
-- allium: 3
-- allium: levels
------------------------------------------------------------
-- Rules
------------------------------------------------------------
rule LevelDiff {
for attacker in Characters, target in Characters:
diff = target.level - attacker.level
}
rule HighLevelTargetModifier {
when: CharacterDealsDamage(attacker, target, baseDamage)
requires: target.level - attacker.level >= 5
ensures: actualDamage = floor(baseDamage * 0.5)
}
rule LowLevelTargetModifier {
when: CharacterDealsDamage(attacker, target, baseDamage)
requires: attacker.level - target.level >= 5
ensures: actualDamage = floor(baseDamage * 1.5)
}
rule CloseLevelNoModifier {
when: CharacterDealsDamage(attacker, target, baseDamage)
requires: abs(target.level - attacker.level) < 5
ensures: actualDamage = baseDamage
}
------------------------------------------------------------
-- Invariants
------------------------------------------------------------
invariant DamageModifierComputationComplete {
for a in Characters, t in Characters, d in NonNegativeIntegers:
let diff = t.level - a.level
let actualDamage =
if diff >= 5 then floor(d * 0.5)
else if diff <= -5 then floor(d * 1.5)
else d
a.dealDamage(t, d) implies t.health = old(t.health) - actualDamage
}
+6
View File
@@ -136,6 +136,8 @@ rule DeadCannotUseWeapon {
ensures:
target.health.value = target.health.value
weapon.health.value = weapon.health.value
weapon.status = weapon.status
target.status = target.status
}
rule NonOwnerCannotUseWeapon {
@@ -146,6 +148,8 @@ rule NonOwnerCannotUseWeapon {
ensures:
target.health.value = target.health.value
weapon.health.value = weapon.health.value
weapon.status = weapon.status
target.status = target.status
}
rule WeaponDestroyedCannotDealDamage {
@@ -156,6 +160,8 @@ rule WeaponDestroyedCannotDealDamage {
ensures:
target.health.value = target.health.value
weapon.health.value = weapon.health.value
weapon.status = weapon.status
target.status = target.status
}
------------------------------------------------------------