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:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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, _)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user