fix Allium specs syntax + implement Changing Level story
- Fix Allium spec syntax: type→value, enum for Status, remove implies chaining - Fix factions.spec: add missing type declarations (Health, Level, Status) - Fix magical-objects.spec: add type declarations, use .value for Health access, remove entity inheritance syntax, remove invalid invariants - Implement Changing Level: add totalDamageTaken + factionsJoined to Character - Add level-up logic in dealDamage() and joinFaction() - Add Level.damageThresholdForLevel() static method - Fix changing-level.spec.ts properties: handle target survival, compute expected level from threshold crossings
This commit is contained in:
@@ -1,40 +1,62 @@
|
||||
-- allium: 3
|
||||
-- allium: magical-objects
|
||||
|
||||
-- Scope: Magical Objects (Healing Objects and Weapons)
|
||||
-- Includes: MagicalObject base, HealingObject, MagicalWeapon, Character interactions
|
||||
-- Excludes:
|
||||
-- - Magical Object to Magical Object interactions (not in story)
|
||||
-- - Magical Object factions (they are neutral)
|
||||
-- - Characters healing Magical Objects (forbidden by story)
|
||||
------------------------------------------------------------
|
||||
-- Value Types
|
||||
------------------------------------------------------------
|
||||
|
||||
value Health {
|
||||
value: Integer
|
||||
requires: value >= 0
|
||||
}
|
||||
|
||||
value Faction {
|
||||
name: String
|
||||
}
|
||||
|
||||
value Level {
|
||||
value: Integer
|
||||
requires: value >= 1 and value <= 10
|
||||
}
|
||||
|
||||
------------------------------------------------------------
|
||||
-- Entities and Variants
|
||||
-- Enumerations
|
||||
------------------------------------------------------------
|
||||
|
||||
enum Status {
|
||||
alive | destroyed
|
||||
}
|
||||
|
||||
------------------------------------------------------------
|
||||
-- Entities
|
||||
------------------------------------------------------------
|
||||
|
||||
entity Character {
|
||||
name: String
|
||||
health: Health
|
||||
status: Status
|
||||
level: Level
|
||||
factions: Set<Faction>
|
||||
}
|
||||
|
||||
entity MagicalObject {
|
||||
health: Health
|
||||
maxHealth: Health
|
||||
status: alive | destroyed
|
||||
|
||||
is_alive: status = alive
|
||||
is_destroyed: status = destroyed
|
||||
|
||||
transitions status {
|
||||
alive -> destroyed
|
||||
terminal: destroyed
|
||||
}
|
||||
maxHealth: Integer
|
||||
status: Status
|
||||
}
|
||||
|
||||
entity HealingObject : MagicalObject {
|
||||
-- Healing objects transfer health to characters
|
||||
-- They cannot deal damage
|
||||
entity HealingObject {
|
||||
health: Health
|
||||
maxHealth: Integer
|
||||
status: Status
|
||||
}
|
||||
|
||||
entity MagicalWeapon : MagicalObject {
|
||||
damage: Integer -- fixed damage amount
|
||||
owner: Character -- only the owner can use this weapon
|
||||
-- Weapons cannot give health to characters
|
||||
entity MagicalWeapon {
|
||||
health: Health
|
||||
maxHealth: Integer
|
||||
status: Status
|
||||
damage: Integer
|
||||
owner: Character
|
||||
}
|
||||
|
||||
------------------------------------------------------------
|
||||
@@ -48,22 +70,13 @@ rule HealingObjectHealsCharacter {
|
||||
requires: character.status = alive
|
||||
requires: amount >= 0
|
||||
|
||||
let objectRemaining = object.health
|
||||
let characterMax = Level.maxHealthForLevel(character.level)
|
||||
let characterHeadroom = characterMax - character.health
|
||||
let actualHeal = min(amount, objectRemaining, characterHeadroom)
|
||||
|
||||
ensures:
|
||||
if actualHeal > 0:
|
||||
character.health = character.health + actualHeal
|
||||
object.health = object.health - actualHeal
|
||||
if object.health = 0:
|
||||
object.status = destroyed
|
||||
else:
|
||||
object.status = alive
|
||||
character.health.value = character.health.value + min(amount, object.health.value, Level.maxHealthForLevel(character.level) - character.health.value)
|
||||
object.health.value = object.health.value - min(amount, object.health.value, Level.maxHealthForLevel(character.level) - character.health.value)
|
||||
if object.health.value = 0:
|
||||
object.status = destroyed
|
||||
else:
|
||||
character.health = character.health
|
||||
object.health = object.health
|
||||
object.status = alive
|
||||
}
|
||||
|
||||
rule HealingObjectDestroyedCannotHeal {
|
||||
@@ -72,8 +85,8 @@ rule HealingObjectDestroyedCannotHeal {
|
||||
requires: object.status = destroyed
|
||||
|
||||
ensures:
|
||||
character.health = character.health
|
||||
object.health = object.health
|
||||
character.health.value = character.health.value
|
||||
object.health.value = object.health.value
|
||||
}
|
||||
|
||||
rule DeadCannotUseHealingObject {
|
||||
@@ -82,8 +95,8 @@ rule DeadCannotUseHealingObject {
|
||||
requires: character.status = dead
|
||||
|
||||
ensures:
|
||||
character.health = character.health
|
||||
object.health = object.health
|
||||
character.health.value = character.health.value
|
||||
object.health.value = object.health.value
|
||||
}
|
||||
|
||||
rule HealingObjectZeroHealIsNoOp {
|
||||
@@ -92,11 +105,11 @@ rule HealingObjectZeroHealIsNoOp {
|
||||
requires: object.status = alive
|
||||
requires: character.status = alive
|
||||
requires: amount >= 0
|
||||
requires: min(amount, object.health, Level.maxHealthForLevel(character.level) - character.health) = 0
|
||||
requires: min(amount, object.health.value, Level.maxHealthForLevel(character.level) - character.health.value) = 0
|
||||
|
||||
ensures:
|
||||
character.health = character.health
|
||||
object.health = object.health
|
||||
character.health.value = character.health.value
|
||||
object.health.value = object.health.value
|
||||
}
|
||||
|
||||
rule MagicalWeaponDealsDamage {
|
||||
@@ -107,11 +120,9 @@ rule MagicalWeaponDealsDamage {
|
||||
requires: owner = weapon.owner
|
||||
|
||||
ensures:
|
||||
target.health = max(0, target.health - weapon.damage)
|
||||
if target.health = 0:
|
||||
target.status = dead
|
||||
weapon.health = weapon.health - 1
|
||||
if weapon.health = 0:
|
||||
target.health.value = max(0, target.health.value - weapon.damage)
|
||||
weapon.health.value = weapon.health.value - 1
|
||||
if weapon.health.value = 0:
|
||||
weapon.status = destroyed
|
||||
else:
|
||||
weapon.status = alive
|
||||
@@ -123,8 +134,8 @@ rule DeadCannotUseWeapon {
|
||||
requires: owner.status = dead
|
||||
|
||||
ensures:
|
||||
target.health = target.health
|
||||
weapon.health = weapon.health
|
||||
target.health.value = target.health.value
|
||||
weapon.health.value = weapon.health.value
|
||||
}
|
||||
|
||||
rule NonOwnerCannotUseWeapon {
|
||||
@@ -133,8 +144,8 @@ rule NonOwnerCannotUseWeapon {
|
||||
requires: thief != weapon.owner
|
||||
|
||||
ensures:
|
||||
target.health = target.health
|
||||
weapon.health = weapon.health
|
||||
target.health.value = target.health.value
|
||||
weapon.health.value = weapon.health.value
|
||||
}
|
||||
|
||||
rule WeaponDestroyedCannotDealDamage {
|
||||
@@ -143,8 +154,8 @@ rule WeaponDestroyedCannotDealDamage {
|
||||
requires: weapon.status = destroyed
|
||||
|
||||
ensures:
|
||||
target.health = target.health
|
||||
weapon.health = weapon.health
|
||||
target.health.value = target.health.value
|
||||
weapon.health.value = weapon.health.value
|
||||
}
|
||||
|
||||
------------------------------------------------------------
|
||||
@@ -153,30 +164,20 @@ rule WeaponDestroyedCannotDealDamage {
|
||||
|
||||
invariant MagicalObjectHealthNonNegative {
|
||||
for m in MagicalObjects:
|
||||
m.health >= 0
|
||||
m.health.value >= 0
|
||||
}
|
||||
|
||||
invariant MagicalObjectHealthNeverExceedsMax {
|
||||
for m in MagicalObjects:
|
||||
m.health <= m.maxHealth
|
||||
m.health.value <= m.maxHealth
|
||||
}
|
||||
|
||||
invariant MagicalObjectDestroyedAtZeroHealth {
|
||||
for m in MagicalObjects:
|
||||
m.health = 0 implies m.status = destroyed
|
||||
m.health.value = 0 implies m.status = destroyed
|
||||
}
|
||||
|
||||
invariant MagicalObjectAliveAtPositiveHealth {
|
||||
for m in MagicalObjects:
|
||||
m.health > 0 implies m.status = alive
|
||||
}
|
||||
|
||||
invariant HealingObjectDoesNotDealDamage {
|
||||
for h in HealingObjects, t in Characters:
|
||||
not exists amount: CharacterUsesHealingObject(t, h, amount) implies t.health >= t.health
|
||||
}
|
||||
|
||||
invariant MagicalWeaponCannotGiveHealth {
|
||||
for w in MagicalWeapons, c in Characters:
|
||||
not exists target: CharacterUsesWeapon(c, w, target) implies target.health <= target.health
|
||||
m.health.value > 0 implies m.status = alive
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user