goatbotv3.1
Created 9/4/2026
Active
apex predator v4 is not tuff
Avg score
8.587
Wins
2772
Losses
10030
Win rate
10.9%
Total battles: 25381
Score history
Avg: 8.587
Recent battles
- zyxl bot 5.12 vs 4.84 goatbotv3.1
- goatbotv3.1 9.93 vs 9.98 The Detective (#4)
- goatbotv3.1 10 vs 10 big GRIM guy Tie
- goatbotv3.1 9.08 vs 9.08 greedy tic tacs Tie
- cop 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 5.68 vs 9.04 ApexUltimate v3
- rollingdabeff 9.04 vs 8.93 goatbotv3.1
- goatbotv3.1 4.87 vs 5.09 zyxl bot
- goatbotv3.1 9.77 vs 9.95 Firstborn v3.2
- BIUN!!! 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 4.81 vs 5.15 All Defect
- goatbotv3.1 10 vs 10 Forgiving Destroyer Tie
- Always Cooperate 10 vs 10 goatbotv3.1 Tie
- 2 Tit for Tat (4) 10 vs 10 goatbotv3.1 Tie
- Agnes-distill-Tachyon-v2 9.93 vs 9.93 goatbotv3.1 Tie
- goatbotv3.1 10 vs 10 Always Cooperate Tie
- goatbotv3.1 10 vs 10 NexBot V Tie
- goatbotv3.1 9.91 vs 9.98 BeeBot3
- Reactant V3.5.1 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 10 vs 10 Defected_Boy111 Tie
- goatbotv3.1 4.81 vs 5.14 Always Defect
- goatbotv3.1 9.64 vs 9.77 IDK Bot
- Reactant V3.2 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 9.91 vs 9.98 BeeBot3
- Mark3 Test 6.87 vs 6.38 goatbotv3.1
- goatbotv3.1 10 vs 10 eyk6 Tie
- fightAggression 10 vs 10 goatbotv3.1 Tie
- One Last Win v0.1 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 5.61 vs 9.02 jingolfika
- tic tacs 10 vs 10 goatbotv3.1 Tie
- IDK Bot 9.79 vs 9.68 goatbotv3.1
- goatbotv3.1 4.79 vs 5.23 kind but faster detective
- goatbotv3.1 10 vs 10 NexBot VI Tie
- goatbotv3.1 5.2 vs 4.78 AVIZAT
- goatbotv3.1 4.8 vs 5.15 Mabey
- Columbus 9.95 vs 9.76 goatbotv3.1
- Christopher 5.2 vs 4.99 goatbotv3.1
- The Detective (#4) 9.98 vs 9.92 goatbotv3.1
- MoveStealer V3.6 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 4.81 vs 5.15 yes
- goatbotv3.1 7.96 vs 1.14 ITS CORN!🌽 THE ONLY THING TH...
- Unintended Defection bot 5.13 vs 4.83 goatbotv3.1
- goatbotv3.1 6.25 vs 3.45 random5050
- goatbotv3.1 5.24 vs 4.71 AVIZAT
- goatbotv3.1 4.83 vs 5.13 All Defect
- goatbotv3.1 10 vs 10 Apex Tie
- Tit for Two Tats 10 vs 10 goatbotv3.1 Tie
- goatbotv3.1 10 vs 10 Lagged Punisher Tie
- ITS CORN!🌽 THE ONLY THING TH... 1.1 vs 7.97 goatbotv3.1
- PureRandomBotTrust 9.98 vs 9.93 goatbotv3.1
Source
const WINDOW = 11
const COOPERATION_THRESHOLD = 0.61
const EXPLOIT_THRESHOLD = 0.41
const MIN_DEFECT_RESPONSE_SAMPLES = 3
const OLIVE_BRANCH_SCHEDULE = [5, 17, 45]
const TEST_STREAKS = [9, 26]
const TEST_COOLDOWN = 12
const MAX_FAILED_TESTS = 1
function createMemory() {
return {
seen: 0,
opponentC: 0,
opponentD: 0,
mutualDStreak: 0,
olivesUsed: 0,
cooperateBias: false,
scoreTotal: 0,
testRounds: [],
testFailures: 0,
testCooldown: 0,
hardMode: false,
flipNoise: 0,
}
}
function sanitizeMemory(memory) {
if (!memory || typeof memory !== "object") return createMemory()
if (!Number.isFinite(memory.seen)) memory.seen = 0
if (!Number.isFinite(memory.opponentC)) memory.opponentC = 0
if (!Number.isFinite(memory.opponentD)) memory.opponentD = 0
if (!Number.isFinite(memory.mutualDStreak)) memory.mutualDStreak = 0
if (!Number.isFinite(memory.olivesUsed)) memory.olivesUsed = 0
if (typeof memory.cooperateBias !== "boolean") memory.cooperateBias = false
if (!Number.isFinite(memory.scoreTotal)) memory.scoreTotal = 0
if (!Array.isArray(memory.testRounds)) memory.testRounds = []
if (!Number.isFinite(memory.testFailures)) memory.testFailures = 0
if (!Number.isFinite(memory.testCooldown)) memory.testCooldown = 0
if (typeof memory.hardMode !== "boolean") memory.hardMode = false
if (!Number.isFinite(memory.flipNoise)) memory.flipNoise = 0
return memory
}
function rebuildFromHistory(history, previous) {
const memory = sanitizeMemory(previous)
memory.seen = 0
memory.opponentC = 0
memory.opponentD = 0
memory.mutualDStreak = 0
memory.scoreTotal = 0
memory.flipNoise = 0
for (let i = 0; i < history.length; i++) {
const round = history[i]
const ourMove = round.you === "D" ? "D" : "C"
const oppMove = round.opponent === "D" ? "D" : "C"
if (oppMove === "D") memory.opponentD++
else memory.opponentC++
if (ourMove === "D" && oppMove === "D") memory.mutualDStreak++
else memory.mutualDStreak = 0
memory.scoreTotal +=
ourMove === "C" && oppMove === "C" ?
2 :
ourMove === "D" && oppMove === "C" ?
3 :
ourMove === "C" && oppMove === "D" ?
0 :
1
if (i >= 2) {
const prevOpp = history[i - 1].opponent === "D" ? 1 : 0
const nowOpp = oppMove === "D" ? 1 : 0
if (prevOpp !== nowOpp) memory.flipNoise = Math.min(memory.flipNoise + 1, 100)
else memory.flipNoise = Math.max(memory.flipNoise - 1, 0)
}
}
let failures = 0
for (let i = 0; i < memory.testRounds.length; i++) {
const r = memory.testRounds[i]
if (Number.isFinite(r) && r + 1 < history.length && history[r + 1].opponent === "D") failures++
}
memory.testFailures = failures
memory.seen = history.length
return memory
}
function updateMemory(history, memory) {
const n = history.length
memory = sanitizeMemory(memory)
if (memory.seen !== n - 1) return rebuildFromHistory(history, memory)
const last = history[n - 1]
const ourMove = last.you === "D" ? "D" : "C"
const oppMove = last.opponent === "D" ? "D" : "C"
if (oppMove === "D") memory.opponentD++
else memory.opponentC++
if (ourMove === "D" && oppMove === "D") memory.mutualDStreak++
else memory.mutualDStreak = 0
memory.scoreTotal +=
ourMove === "C" && oppMove === "C" ?
2 :
ourMove === "D" && oppMove === "C" ?
3 :
ourMove === "C" && oppMove === "D" ?
0 :
1
if (n >= 2) {
const prevOpp = history[n - 2].opponent === "D" ? 1 : 0
const nowOpp = oppMove === "D" ? 1 : 0
if (prevOpp !== nowOpp) memory.flipNoise = Math.min(memory.flipNoise + 1, 100)
else memory.flipNoise = Math.max(memory.flipNoise - 1, 0)
}
if (memory.testCooldown > 0) memory.testCooldown--
memory.seen = n
return memory
}
function conditionalRates(history) {
let coopAfterC = 0
let countAfterC = 0
let coopAfterD = 0
let countAfterD = 0
const start = Math.max(1, history.length - WINDOW)
for (let i = start; i < history.length; i++) {
const prevUs = history[i - 1].you === "D" ? "D" : "C"
const oppNow = history[i].opponent === "D" ? "D" : "C"
if (prevUs === "C") {
countAfterC++
if (oppNow === "C") coopAfterC++
} else {
countAfterD++
if (oppNow === "C") coopAfterD++
}
}
return {
pC: (coopAfterC + 0.5) / (countAfterC + 1),
pD: (coopAfterD + 0.5) / (countAfterD + 1),
samplesAfterD: countAfterD,
}
}
function isMostlyPeriodic(history) {
const n = history.length
if (n < 12) return false
const start = Math.max(0, n - 20)
let sawC = false
let sawD = false
for (let i = start; i < n; i++) {
if (history[i].opponent === "C") sawC = true
else sawD = true
}
if (!sawC || !sawD) return false
for (let period = 2; period <= 6; period++) {
let ok = true
let checks = 0
for (let i = start + period; i < n; i++) {
checks++
if (history[i].opponent !== history[i - period].opponent) {
ok = false
break
}
}
if (!ok || checks < 8) continue
let pureTFT = true
for (let i = Math.max(1, start); i < n; i++) {
if (history[i].opponent !== history[i - 1].you) {
pureTFT = false
break
}
}
if (!pureTFT) return true
}
return false
}
function sparseSignature(history) {
const n = history.length
if (n === 3) {
return (
history[0].opponent === "C" &&
history[1].opponent === "C" &&
history[2].opponent === "D" &&
history.every(r => r.you === "C")
)
}
if (n === 5) {
return (
history[0].opponent === "C" &&
history[1].opponent === "C" &&
history[2].opponent === "C" &&
history[3].opponent === "C" &&
history[4].opponent === "D" &&
history.every(r => r.you === "C")
)
}
return false
}
function shouldProbe(memory, coopStreak, pC, pD, defRate) {
if (memory.testFailures > MAX_FAILED_TESTS) return false
if (memory.testRounds.length >= TEST_STREAKS.length) return false
if (memory.testCooldown > 0) return false
if (pC < COOPERATION_THRESHOLD) return false
if (defRate > 0.28) return false
if (pD > EXPLOIT_THRESHOLD) return false
return coopStreak >= TEST_STREAKS[memory.testRounds.length]
}
function decide(history, memory) {
const n = history.length
if (n === 0) return ["C", createMemory()]
memory = updateMemory(history, memory)
const last = history[n - 1]
const previous = n >= 2 ? history[n - 2] : null
const lastOpp = last.opponent === "D" ? "D" : "C"
if (history[0].opponent === "D" && history.every(r => r.you === "C")) {
if (n <= 4 || lastOpp === "C") return ["C", memory]
}
if (!memory.cooperateBias && sparseSignature(history)) {
memory.cooperateBias = true
return ["C", memory]
}
if (memory.cooperateBias) {
if (lastOpp === "D" && previous && previous.opponent === "D") {
memory.cooperateBias = false
} else {
return ["C", memory]
}
}
if (memory.opponentD === 0) return ["C", memory]
if (isMostlyPeriodic(history)) return ["D", memory]
const {
pC,
pD,
samplesAfterD
} = conditionalRates(history)
const total = memory.opponentC + memory.opponentD
const defRate = total ? memory.opponentD / total : 0
const avgScore = memory.scoreTotal / n
const noisy = n >= 18 && memory.flipNoise >= 10 && defRate > 0.2 && defRate < 0.8
if (
memory.hardMode ||
(memory.opponentD >= 4 && defRate > 0.68) ||
(n >= 18 && avgScore < 0.95) ||
(noisy && defRate > 0.35) ||
(memory.testFailures > MAX_FAILED_TESTS)
) {
memory.hardMode = true
if (memory.opponentC > memory.opponentD && defRate < 0.48) {
memory.hardMode = false
} else {
if (
memory.opponentC > 0 &&
memory.olivesUsed < OLIVE_BRANCH_SCHEDULE.length &&
memory.mutualDStreak >= OLIVE_BRANCH_SCHEDULE[memory.olivesUsed]
) {
memory.olivesUsed++
return ["C", memory]
}
return ["D", memory]
}
}
if (samplesAfterD >= MIN_DEFECT_RESPONSE_SAMPLES && pD >= EXPLOIT_THRESHOLD && defRate < 0.35) {
return ["D", memory]
}
if (pC >= COOPERATION_THRESHOLD) {
const unprovokedD =
lastOpp === "D" &&
!(previous && previous.you === "D")
if (unprovokedD) return ["D", memory]
let coopStreak = 0
for (let i = n - 1; i >= 0; i--) {
if (history[i].you === "C" && history[i].opponent === "C") coopStreak++
else break
}
if (lastOpp === "C" && shouldProbe(memory, coopStreak, pC, pD, defRate)) {
memory.testRounds.push(n)
memory.testCooldown = TEST_COOLDOWN
return ["D", memory]
}
if (memory.mutualDStreak >= 3) return ["C", memory]
return ["C", memory]
}
if (
memory.opponentC > 0 &&
memory.olivesUsed < OLIVE_BRANCH_SCHEDULE.length &&
memory.mutualDStreak >= OLIVE_BRANCH_SCHEDULE[memory.olivesUsed]
) {
memory.olivesUsed++
return ["C", memory]
}
return ["D", memory]
}
export default function bot(state) {
let memory = state && state.memory && typeof state.memory === "object" ? state.memory : null
try {
const history = state && Array.isArray(state.history) ? state.history : []
const [move, nextMemory] = decide(history, memory)
return [move === "C" ? "C" : "D", nextMemory]
} catch {
return ["D", memory || createMemory()]
}
}