all features implemented
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// swift-tools-version: 5.9
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "DieHammerEngine",
|
||||
products: [
|
||||
.library(
|
||||
name: "DieHammerEngine",
|
||||
targets: ["DieHammerEngine"]
|
||||
),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "DieHammerEngine"
|
||||
),
|
||||
.testTarget(
|
||||
name: "DieHammerEngineTests",
|
||||
dependencies: ["DieHammerEngine"]
|
||||
),
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,205 @@
|
||||
public struct Dice {
|
||||
|
||||
// MARK: Private
|
||||
private var d6: Int {
|
||||
Int.random(in: 1...6)
|
||||
}
|
||||
|
||||
// MARK: Enums
|
||||
public enum DiceType: Int, CaseIterable {
|
||||
case one = 1
|
||||
case two = 2
|
||||
case three = 3
|
||||
case four = 4
|
||||
case five = 5
|
||||
case six = 6
|
||||
}
|
||||
|
||||
public enum RollModifiers: Int {
|
||||
case negative = -1
|
||||
case none = 0
|
||||
case positive = 1
|
||||
|
||||
public func add() -> RollModifiers {
|
||||
switch self {
|
||||
case .negative: return .none
|
||||
case .none: return .positive
|
||||
case .positive: return .positive
|
||||
}
|
||||
}
|
||||
|
||||
public func remove() -> RollModifiers {
|
||||
switch self {
|
||||
case .negative: return .negative
|
||||
case .none: return .negative
|
||||
case .positive: return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Variables
|
||||
public var one = 0
|
||||
public var two = 0
|
||||
public var three = 0
|
||||
public var four = 0
|
||||
public var five = 0
|
||||
public var six = 0
|
||||
|
||||
public init() {}
|
||||
|
||||
// MARK: Roll controllers
|
||||
public mutating func roll(howMany: Int) {
|
||||
clear()
|
||||
var y = howMany
|
||||
while y > 0 {
|
||||
switch d6 {
|
||||
case 1: one += 1
|
||||
case 2: two += 1
|
||||
case 3: three += 1
|
||||
case 4: four += 1
|
||||
case 5: five += 1
|
||||
default: six += 1
|
||||
}
|
||||
y -= 1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Calculations
|
||||
public func total(cutoff: Int, modifier: RollModifiers) -> Int {
|
||||
var total = six
|
||||
|
||||
switch modifier {
|
||||
case .none:
|
||||
switch cutoff {
|
||||
case 5: total += five
|
||||
case 4: total += four + five
|
||||
case 3: total += three + four + five
|
||||
case 2: total += two + three + four + five
|
||||
default: break
|
||||
}
|
||||
case .negative:
|
||||
switch cutoff {
|
||||
case 4: total += five
|
||||
case 3: total += four + five
|
||||
case 2: total += three + four + five
|
||||
case 1: total += two + three + four + five
|
||||
default: break
|
||||
}
|
||||
case .positive:
|
||||
switch cutoff {
|
||||
case 6: total += five
|
||||
case 5: total += four + five
|
||||
case 4: total += three + four + five
|
||||
case 3, 2: total += two + three + four + five
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
public func hit(cutoff: Int, modifier: Int) -> Int {
|
||||
total(cutoff: cutoff, modifier: RollModifiers(rawValue: modifier) ?? .none)
|
||||
}
|
||||
|
||||
public func wound(cutoff: Int, modifier: Int) -> Int {
|
||||
total(cutoff: cutoff, modifier: RollModifiers(rawValue: modifier) ?? .none)
|
||||
}
|
||||
|
||||
public func armour(save: Int, armourPenetration: Int, invulnerable: Bool) -> Int {
|
||||
var total = 0
|
||||
switch invulnerable ? save : (save + armourPenetration) {
|
||||
case 7...: total = six + five + four + three + two + one
|
||||
case 6: total = five + four + three + two + one
|
||||
case 5: total = four + three + two + one
|
||||
case 4: total = three + two + one
|
||||
case 3: total = two + one
|
||||
default: total = one
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// MARK: Utilities
|
||||
public mutating func clear() {
|
||||
one = 0; two = 0; three = 0; four = 0; five = 0; six = 0
|
||||
}
|
||||
|
||||
public mutating func add(pool: Dice) {
|
||||
one += pool.one; two += pool.two; three += pool.three
|
||||
four += pool.four; five += pool.five; six += pool.six
|
||||
}
|
||||
|
||||
public func value(which: DiceType) -> Int {
|
||||
switch which {
|
||||
case .one: return one
|
||||
case .two: return two
|
||||
case .three: return three
|
||||
case .four: return four
|
||||
case .five: return five
|
||||
case .six: return six
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func increment(die: DiceType) {
|
||||
switch die {
|
||||
case .one: one += 1
|
||||
case .two: two += 1
|
||||
case .three: three += 1
|
||||
case .four: four += 1
|
||||
case .five: five += 1
|
||||
case .six: six += 1
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func decrement(die: DiceType) {
|
||||
switch die {
|
||||
case .one: if one > 0 { one -= 1 }
|
||||
case .two: if two > 0 { two -= 1 }
|
||||
case .three: if three > 0 { three -= 1 }
|
||||
case .four: if four > 0 { four -= 1 }
|
||||
case .five: if five > 0 { five -= 1 }
|
||||
case .six: if six > 0 { six -= 1 }
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func reroll(dice: DiceType) {
|
||||
var rerolls = Dice()
|
||||
switch dice {
|
||||
case .one: rerolls.roll(howMany: one); one = 0; self.add(pool: rerolls)
|
||||
case .two: rerolls.roll(howMany: two); two = 0; self.add(pool: rerolls)
|
||||
case .three: rerolls.roll(howMany: three); three = 0; self.add(pool: rerolls)
|
||||
case .four: rerolls.roll(howMany: four); four = 0; self.add(pool: rerolls)
|
||||
case .five: rerolls.roll(howMany: five); five = 0; self.add(pool: rerolls)
|
||||
case .six: rerolls.roll(howMany: six); six = 0; self.add(pool: rerolls)
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func rerollBelow(cutoff: Int) {
|
||||
var count = 0
|
||||
for faceValue in 1..<cutoff {
|
||||
guard let face = DiceType(rawValue: faceValue) else { continue }
|
||||
count += value(which: face)
|
||||
switch face {
|
||||
case .one: one = 0
|
||||
case .two: two = 0
|
||||
case .three: three = 0
|
||||
case .four: four = 0
|
||||
case .five: five = 0
|
||||
case .six: six = 0
|
||||
}
|
||||
}
|
||||
var rerolls = Dice()
|
||||
rerolls.roll(howMany: count)
|
||||
add(pool: rerolls)
|
||||
}
|
||||
|
||||
public mutating func blowup(dice: DiceType) {
|
||||
var rolls = Dice()
|
||||
if dice == .six {
|
||||
rolls.roll(howMany: six)
|
||||
self.add(pool: rolls)
|
||||
} else if dice == .five {
|
||||
rolls.roll(howMany: six + five)
|
||||
self.add(pool: rolls)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
public func woundCutoff(strength: Int, toughness: Int) -> Int {
|
||||
let ratio = Float(strength) / Float(toughness)
|
||||
if ratio >= 2.0 { return 2 }
|
||||
else if ratio > 1.0 { return 3 }
|
||||
else if ratio == 1.0 { return 4 }
|
||||
else if ratio <= 0.5 { return 6 }
|
||||
else { return 5 }
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import Testing
|
||||
@testable import DieHammerEngine
|
||||
|
||||
@Suite("Dice")
|
||||
struct DiceTests {
|
||||
|
||||
func oneEach() -> Dice {
|
||||
var d = Dice()
|
||||
d.one = 1; d.two = 1; d.three = 1; d.four = 1; d.five = 1; d.six = 1
|
||||
return d
|
||||
}
|
||||
|
||||
@Test("roll preserves total count")
|
||||
func rollCount() {
|
||||
var d = Dice()
|
||||
d.roll(howMany: 10)
|
||||
let total = d.one + d.two + d.three + d.four + d.five + d.six
|
||||
#expect(total == 10)
|
||||
}
|
||||
|
||||
@Test("hit counts dice at or above cutoff (no modifier)")
|
||||
func hitNoModifier() {
|
||||
let d = oneEach()
|
||||
#expect(d.hit(cutoff: 4, modifier: 0) == 3) // 4,5,6
|
||||
#expect(d.hit(cutoff: 6, modifier: 0) == 1) // 6 only
|
||||
}
|
||||
|
||||
@Test("armour: AP worsens save (higher switch value = more failures)")
|
||||
func armourAP() {
|
||||
let d = oneEach()
|
||||
// 3+ save, AP-1 → effective 4+: dice 1,2,3 fail = 3
|
||||
#expect(d.armour(save: 3, armourPenetration: 1, invulnerable: false) == 3)
|
||||
}
|
||||
|
||||
@Test("armour: invulnerable ignores AP")
|
||||
func armourInvulnerable() {
|
||||
let d = oneEach()
|
||||
let withInvuln = d.armour(save: 4, armourPenetration: 5, invulnerable: true)
|
||||
let noAP = d.armour(save: 4, armourPenetration: 0, invulnerable: false)
|
||||
#expect(withInvuln == noAP)
|
||||
}
|
||||
}
|
||||
|
||||
@Suite("WoundCutoff")
|
||||
struct WoundCutoffTests {
|
||||
|
||||
@Test("S double T: wound on 2+")
|
||||
func doubleStrength() {
|
||||
#expect(woundCutoff(strength: 10, toughness: 5) == 2)
|
||||
}
|
||||
|
||||
@Test("S greater than T: wound on 3+")
|
||||
func strengthHigher() {
|
||||
#expect(woundCutoff(strength: 7, toughness: 5) == 3)
|
||||
}
|
||||
|
||||
@Test("S equals T: wound on 4+")
|
||||
func equal() {
|
||||
#expect(woundCutoff(strength: 5, toughness: 5) == 4)
|
||||
}
|
||||
|
||||
@Test("T greater than S: wound on 5+")
|
||||
func toughnessHigher() {
|
||||
#expect(woundCutoff(strength: 3, toughness: 5) == 5)
|
||||
}
|
||||
|
||||
@Test("T double S: wound on 6+")
|
||||
func doubleToughness() {
|
||||
#expect(woundCutoff(strength: 2, toughness: 5) == 6)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user