← poker-tutor
import Foundation
import PokerCore
import PokerAI
/// Measures each parameter's effect on its own.
///
/// "Expert" was authored from poker theory and then measured backwards, so rather than keep
/// guessing which knob was wrong, this changes exactly one at a time against an otherwise
/// identical opponent and reports what each is worth. Anything that measures negative is not a
/// skill; it is a leak with a flattering name.
func runAblation(hands: Int, seed: UInt64, personality: Personality, seats: Int) {
let band = personality.skillRange
let base = personality.parameters(skill: band.lowerBound)
let top = personality.parameters(skill: band.upperBound)
let knobs: [(String, (inout BotParameters) -> Void)] = [
("openRange", { $0.openRange = top.openRange }),
("threeBetRange", { $0.threeBetRange = top.threeBetRange }),
("callVsRaiseRange", { $0.callVsRaiseRange = top.callVsRaiseRange }),
("blindDefenseRange", { $0.blindDefenseRange = top.blindDefenseRange }),
("foldToThreeBet", { $0.foldToThreeBet = top.foldToThreeBet }),
("cbetFrequency", { $0.cbetFrequency = top.cbetFrequency }),
("bluffFrequency", { $0.bluffFrequency = top.bluffFrequency }),
("aggression", { $0.aggression = top.aggression }),
("callSlack", { $0.callSlack = top.callSlack }),
("valueThreshold", { $0.valueThreshold = top.valueThreshold }),
("raiseThreshold", { $0.raiseThreshold = top.raiseThreshold }),
("betSizing", { $0.betSizing = top.betSizing }),
("sizingNoise", { $0.sizingNoise = top.sizingNoise }),
("limpTendency", { $0.limpTendency = top.limpTendency }),
("pushFoldDepth", { $0.pushFoldDepth = top.pushFoldDepth }),
("adaptation", { $0.adaptation = top.adaptation }),
("readError", { $0.readError = top.readError }),
]
print("\(personality.name): each parameter moved from its skill-\(band.lowerBound) value to its")
print("skill-\(band.upperBound) value, one at a time, against a table of the unchanged version.")
print("\(seats)-handed, \(hands * seats) hands each. Positive means the change earns money.")
print("")
var rows: [(String, Double)] = []
for (name, apply) in knobs {
var params = base
apply(¶ms)
guard !equal(params, base) else { continue }
var hero = Bot(id: "hero", personality: personality, skill: band.lowerBound, seed: 11)
hero.parameters = params
let r = Shootout.ring(hero, versus: personality, villainSkill: band.lowerBound,
seats: seats, hands: hands, seed: seed)
rows.append((name, r.bigBlindsPer100))
}
// Everything at once, for comparison with the sum of the parts.
var full = Bot(id: "hero", personality: personality, skill: band.upperBound, seed: 11)
full.parameters = top
let all = Shootout.ring(full, versus: personality, villainSkill: band.lowerBound,
seats: seats, hands: hands, seed: seed)
for (name, value) in rows.sorted(by: { $0.1 > $1.1 }) {
let bar = String(repeating: value >= 0 ? "+" : "-", count: min(30, Int(abs(value))))
print(String(format: " %-20@ %+8.2f %@", name as NSString, value, bar))
}
print("")
print(String(format: " %-20@ %+8.2f", "all together" as NSString, all.bigBlindsPer100))
}
private func equal(_ a: BotParameters, _ b: BotParameters) -> Bool {
a.openRange == b.openRange && a.threeBetRange == b.threeBetRange
&& a.callVsRaiseRange == b.callVsRaiseRange && a.blindDefenseRange == b.blindDefenseRange
&& a.foldToThreeBet == b.foldToThreeBet && a.cbetFrequency == b.cbetFrequency
&& a.bluffFrequency == b.bluffFrequency && a.aggression == b.aggression
&& a.callSlack == b.callSlack && a.valueThreshold == b.valueThreshold
&& a.raiseThreshold == b.raiseThreshold && a.betSizing == b.betSizing
&& a.sizingNoise == b.sizingNoise && a.limpTendency == b.limpTendency
&& a.pushFoldDepth == b.pushFoldDepth && a.adaptation == b.adaptation
&& a.readError == b.readError
}