aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hacking_liars_dice.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-19 18:20:26 -0400
committerhistoria <[not public]>2026-06-19 18:20:26 -0400
commit0ea4eec5dd2122c6704216971eb1249276297867 (patch)
tree430fbbef04e837820f1d031c190f52ecd6112e25 /internal/game/hacking_liars_dice.go
parent3a58125d14bb307f861b38c6c5b0a63babde7e08 (diff)
downloadthehouseoficarus-0ea4eec5dd2122c6704216971eb1249276297867.tar.gz
shop fixes, 'toggle' completely removed, movement speed increased
Diffstat (limited to 'internal/game/hacking_liars_dice.go')
-rw-r--r--internal/game/hacking_liars_dice.go395
1 files changed, 0 insertions, 395 deletions
diff --git a/internal/game/hacking_liars_dice.go b/internal/game/hacking_liars_dice.go
deleted file mode 100644
index 12f76c1..0000000
--- a/internal/game/hacking_liars_dice.go
+++ /dev/null
@@ -1,395 +0,0 @@
-package game
-
-import (
- "fmt"
- "math/rand"
- "strconv"
- "strings"
-)
-
-type LiarsDiceGame struct {
- playerDice []int
- aiDice []int
- currentBid LiarsBid
- hasBid bool
- playerTurn bool
- round int
- gameOver bool
- won bool
- playerLost int
- completed bool
-}
-
-type LiarsBid struct {
- Quantity int
- Face int
-}
-
-func NewLiarsDiceGame() *LiarsDiceGame {
- return &LiarsDiceGame{
- playerDice: make([]int, 5),
- aiDice: make([]int, 5),
- }
-}
-
-func (l *LiarsDiceGame) Name() string {
- return "Signal Bluff"
-}
-
-func (l *LiarsDiceGame) Init(level int) string {
- l.playerDice = make([]int, 5)
- l.aiDice = make([]int, 5)
- l.round = 0
- l.gameOver = false
- l.playerTurn = true
- l.hasBid = false
- l.playerLost = 0
- l.completed = false
-
- var sb strings.Builder
- sb.WriteString("=== SIGNAL BLUFF ===\n")
- sb.WriteString("\n")
- sb.WriteString("You've connected to a secure channel running a bluffing protocol.\n")
- sb.WriteString("You and the system each have 5 signal fragments (dice). Each round,\n")
- sb.WriteString("fragments are scrambled. You see only yours. Take turns making claims\n")
- sb.WriteString("about the total count of a specific signal across ALL fragments.\n")
- sb.WriteString("\n")
- sb.WriteString("1s are wild -- they count as any signal.\n")
- sb.WriteString("\n")
- sb.WriteString("Commands:\n")
- sb.WriteString(" bid <qty> <face> - Claim at least N fragments show face F\n")
- sb.WriteString(" (e.g., 'bid 3 4' = \"at least three 4s\")\n")
- sb.WriteString(" call - Challenge the last bid (reveal all)\n")
- sb.WriteString(" exact - Claim the bid is exactly right\n")
- sb.WriteString(" status - Show your fragments and current bid\n")
- sb.WriteString(" jack out - Disconnect (forfeit)\n")
- sb.WriteString("\n")
- sb.WriteString(l.startRound())
- return sb.String()
-}
-
-func (l *LiarsDiceGame) HandleInput(input string) (string, bool, bool) {
- input = strings.TrimSpace(strings.ToLower(input))
- parts := strings.Fields(input)
-
- if len(parts) == 0 {
- return "Commands: bid <qty> <face>, call, exact, status, jack out", false, false
- }
-
- cmd := parts[0]
-
- switch cmd {
- case "bid", "b":
- if len(parts) < 3 {
- return "Usage: bid <quantity> <face> (e.g., bid 3 4)", false, false
- }
- qty, err1 := strconv.Atoi(parts[1])
- face, err2 := strconv.Atoi(parts[2])
- if err1 != nil || err2 != nil {
- return "Usage: bid <quantity> <face> (e.g., bid 3 4)", false, false
- }
- return l.doBid(qty, face)
- case "call", "c", "intercept", "liar":
- return l.doCall(false)
- case "exact", "e":
- return l.doExact()
- case "status":
- return l.doStatus(), false, false
- default:
- if len(parts) == 2 {
- qty, err1 := strconv.Atoi(parts[0])
- face, err2 := strconv.Atoi(parts[1])
- if err1 == nil && err2 == nil {
- return l.doBid(qty, face)
- }
- }
- return "Commands: bid <qty> <face>, call, exact, status, jack out", false, false
- }
-}
-
-func (l *LiarsDiceGame) doBid(qty, face int) (string, bool, bool) {
- if face < 1 || face > 6 {
- return "Face must be 1-6.", false, false
- }
- if qty < 1 {
- return "Quantity must be at least 1.", false, false
- }
- if !l.playerTurn {
- return "It's not your turn to bid. Use 'call' or 'exact'.", false, false
- }
-
- if l.hasBid {
- if qty < l.currentBid.Quantity || (qty == l.currentBid.Quantity && face <= l.currentBid.Face) {
- return "You must bid higher (higher quantity or same quantity with higher face).", false, false
- }
- }
-
- l.currentBid = LiarsBid{Quantity: qty, Face: face}
- l.hasBid = true
- l.playerTurn = false
-
- return l.aiTurn()
-}
-
-func (l *LiarsDiceGame) doCall(player bool) (string, bool, bool) {
- if !l.hasBid {
- return "No bid to call yet. Make a bid first.", false, false
- }
-
- var sb strings.Builder
- if player {
- sb.WriteString("System intercepts!\n")
- } else {
- sb.WriteString("You intercept!\n")
- }
-
- sb.WriteString(l.showAllDice())
- sb.WriteString(fmt.Sprintf("\nBid was: %d %s\n", l.currentBid.Quantity, faceNamePlural(l.currentBid.Face)))
-
- total := l.countFace(l.currentBid.Face)
- sb.WriteString(fmt.Sprintf("Actual count: %d (%d %ss + %d wild 1s)\n\n",
- total,
- l.countFaceNonWild(l.currentBid.Face),
- faceName(l.currentBid.Face),
- l.countFace(1)-l.countFaceNonWild(1)))
-
- if total >= l.currentBid.Quantity {
- sb.WriteString("The bid holds! ")
- if player {
- sb.WriteString("System loses a fragment.")
- l.aiDice = l.aiDice[:len(l.aiDice)-1]
- } else {
- sb.WriteString("You lose a fragment.")
- l.playerDice = l.playerDice[:len(l.playerDice)-1]
- l.playerLost++
- }
- } else {
- sb.WriteString("Bluff called! ")
- if player {
- sb.WriteString("You lose a fragment.")
- l.playerDice = l.playerDice[:len(l.playerDice)-1]
- l.playerLost++
- } else {
- sb.WriteString("System loses a fragment.")
- l.aiDice = l.aiDice[:len(l.aiDice)-1]
- }
- }
-
- return l.checkGameOver(sb)
-}
-
-func (l *LiarsDiceGame) doExact() (string, bool, bool) {
- if !l.hasBid {
- return "No bid to claim exact on. Make a bid first.", false, false
- }
-
- var sb strings.Builder
- sb.WriteString("You claim exact match!\n")
-
- sb.WriteString(l.showAllDice())
- total := l.countFace(l.currentBid.Face)
- sb.WriteString(fmt.Sprintf("\nBid was: %d %s\n", l.currentBid.Quantity, faceNamePlural(l.currentBid.Face)))
- sb.WriteString(fmt.Sprintf("Actual count: %d\n\n", total))
-
- if total == l.currentBid.Quantity {
- sb.WriteString("Exact match! You recover a fragment.\n")
- if len(l.playerDice) < 5 {
- l.playerDice = append(l.playerDice, rand.Intn(6)+1)
- }
- } else {
- sb.WriteString("Not an exact match. You lose a fragment.\n")
- l.playerDice = l.playerDice[:len(l.playerDice)-1]
- l.playerLost++
- }
-
- return l.checkGameOver(sb)
-}
-
-func (l *LiarsDiceGame) checkGameOver(sb strings.Builder) (string, bool, bool) {
- if len(l.playerDice) == 0 {
- sb.WriteString("\nYou've lost all your fragments. Connection terminated.")
- l.gameOver = true
- l.won = false
- l.completed = true
- return sb.String(), true, false
- }
- if len(l.aiDice) == 0 {
- sb.WriteString("\nSystem has no fragments left. You win!")
- l.gameOver = true
- l.won = true
- l.completed = true
- return sb.String(), true, true
- }
-
- sb.WriteString(l.startRound())
- return sb.String(), false, false
-}
-
-func (l *LiarsDiceGame) showAllDice() string {
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("Your fragments: %s\n", l.diceStr(l.playerDice)))
- sb.WriteString(fmt.Sprintf("System fragments: %s\n", l.diceStr(l.aiDice)))
- return sb.String()
-}
-
-func (l *LiarsDiceGame) diceStr(dice []int) string {
- var parts []string
- for _, d := range dice {
- parts = append(parts, fmt.Sprintf("[%d]", d))
- }
- return strings.Join(parts, " ")
-}
-
-func (l *LiarsDiceGame) doStatus() string {
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("--- Cycle %d ---\n", l.round))
- sb.WriteString(fmt.Sprintf("Your fragments: %s (You: %d, System: %d)\n",
- l.diceStr(l.playerDice), len(l.playerDice), len(l.aiDice)))
- if l.hasBid {
- who := "System"
- if !l.playerTurn {
- who = "System"
- } else {
- who = "you"
- }
- sb.WriteString(fmt.Sprintf("Current bid: %d %s (by %s)\n", l.currentBid.Quantity, faceNamePlural(l.currentBid.Face), who))
- }
- if l.playerTurn {
- sb.WriteString("Your turn: make a bid.")
- } else {
- sb.WriteString("Your turn: bid higher, call, or exact.")
- }
- return sb.String()
-}
-
-func (l *LiarsDiceGame) startRound() string {
- l.round++
- for i := range l.playerDice {
- l.playerDice[i] = rand.Intn(6) + 1
- }
- for i := range l.aiDice {
- l.aiDice[i] = rand.Intn(6) + 1
- }
- l.hasBid = false
- l.playerTurn = true
-
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("--- Cycle %d ---\n", l.round))
- sb.WriteString(fmt.Sprintf("Your fragments: %s (You: %d, System: %d)\n",
- l.diceStr(l.playerDice), len(l.playerDice), len(l.aiDice)))
- sb.WriteString("You go first. Make a bid.\n")
- return sb.String()
-}
-
-func (l *LiarsDiceGame) aiTurn() (string, bool, bool) {
- totalDice := len(l.playerDice) + len(l.aiDice)
-
- aiCount := l.countFace(l.currentBid.Face)
- unknownDice := len(l.playerDice)
- estimated := float64(aiCount) + float64(unknownDice)/3.0
-
- if float64(l.currentBid.Quantity) > estimated*1.5 {
- return l.doCall(true)
- }
-
- bestFace, bestCount := l.aiBestFace()
- newQty := l.currentBid.Quantity
- newFace := l.currentBid.Face
-
- if bestFace > newFace && bestCount >= newQty {
- newFace = bestFace
- } else {
- newQty++
- }
-
- if newQty > totalDice {
- return l.doCall(true)
- }
-
- l.currentBid = LiarsBid{Quantity: newQty, Face: newFace}
- l.playerTurn = true
-
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("System broadcasts: %d %s.\n", newQty, faceNamePlural(newFace)))
- sb.WriteString("Your turn: bid higher, call, or exact.\n")
- return sb.String(), false, false
-}
-
-func (l *LiarsDiceGame) aiBestFace() (face int, count int) {
- counts := make(map[int]int)
- for _, d := range l.aiDice {
- if d == 1 {
- continue
- }
- counts[d]++
- }
- wilds := 0
- for _, d := range l.aiDice {
- if d == 1 {
- wilds++
- }
- }
- best := 2
- bestN := counts[2] + wilds
- for f := 3; f <= 6; f++ {
- n := counts[f] + wilds
- if n > bestN || (n == bestN && f > best) {
- best = f
- bestN = n
- }
- }
- return best, bestN
-}
-
-func (l *LiarsDiceGame) countFace(face int) int {
- count := 0
- for _, d := range l.playerDice {
- if d == face || d == 1 {
- count++
- }
- }
- for _, d := range l.aiDice {
- if d == face || d == 1 {
- count++
- }
- }
- return count
-}
-
-func (l *LiarsDiceGame) countFaceNonWild(face int) int {
- count := 0
- for _, d := range l.playerDice {
- if d == face {
- count++
- }
- }
- for _, d := range l.aiDice {
- if d == face {
- count++
- }
- }
- return count
-}
-
-func faceName(f int) string {
- names := []string{"", "one", "two", "three", "four", "five", "six"}
- if f >= 1 && f <= 6 {
- return names[f]
- }
- return fmt.Sprint(f)
-}
-
-func faceNamePlural(f int) string {
- names := []string{"", "ones", "twos", "threes", "fours", "fives", "sixes"}
- if f >= 1 && f <= 6 {
- return names[f]
- }
- return fmt.Sprint(f)
-}
-
-func (l *LiarsDiceGame) BonusXP() int {
- if l.completed && l.won && l.playerLost == 0 {
- return 150
- }
- return 0
-}