aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hacking_mastermind.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-19 13:28:06 -0400
committerhistoria <[not public]>2026-06-19 13:28:06 -0400
commit3a58125d14bb307f861b38c6c5b0a63babde7e08 (patch)
treebd89e866447d55e6dffd447d8ef5fabf9b8fbe9d /internal/game/hacking_mastermind.go
parent575a71dcfed3b9fa44af244836f638a23df05a9a (diff)
downloadthehouseoficarus-3a58125d14bb307f861b38c6c5b0a63babde7e08.tar.gz
feat: all skills kind of implemented, random prototype content added
Diffstat (limited to 'internal/game/hacking_mastermind.go')
-rw-r--r--internal/game/hacking_mastermind.go177
1 files changed, 177 insertions, 0 deletions
diff --git a/internal/game/hacking_mastermind.go b/internal/game/hacking_mastermind.go
new file mode 100644
index 0000000..3ab7cca
--- /dev/null
+++ b/internal/game/hacking_mastermind.go
@@ -0,0 +1,177 @@
+package game
+
+import (
+ "fmt"
+ "math/rand"
+ "strings"
+)
+
+type MastermindGame struct {
+ code [4]int
+ guesses []MastermindGuess
+ maxGuess int
+ gameOver bool
+ won bool
+}
+
+type MastermindGuess struct {
+ Digits [4]int
+ Exact int
+ Partial int
+}
+
+func NewMastermindGame() *MastermindGame {
+ return &MastermindGame{
+ maxGuess: 10,
+ }
+}
+
+func (m *MastermindGame) Name() string {
+ return "Code Breaker"
+}
+
+func (m *MastermindGame) Init(level int) string {
+ pool := []int{1, 2, 3, 4, 5, 6}
+ rand.Shuffle(len(pool), func(i, j int) { pool[i], pool[j] = pool[j], pool[i] })
+ copy(m.code[:], pool[:4])
+ m.guesses = nil
+ m.gameOver = false
+ m.won = false
+
+ var sb strings.Builder
+ sb.WriteString("=== CODE BREAKER ===\n")
+ sb.WriteString("\n")
+ sb.WriteString("The encryption module is running a 4-digit cipher using symbols 1-6.\n")
+ sb.WriteString("No symbol repeats. You have 10 attempts to crack the code.\n")
+ sb.WriteString("\n")
+ sb.WriteString("After each guess, you'll see:\n")
+ sb.WriteString(" {40}[X]{/} = correct symbol in correct position\n")
+ sb.WriteString(" {226}[O]{/} = correct symbol in wrong position\n")
+ sb.WriteString(" [ ] = symbol not in code\n")
+ sb.WriteString("\n")
+ sb.WriteString("Commands:\n")
+ sb.WriteString(" <4 digits> - Guess the code (e.g., 1234)\n")
+ sb.WriteString(" status - Show previous guesses\n")
+ sb.WriteString(" jack out - Disconnect (forfeit)\n")
+ return sb.String()
+}
+
+func (m *MastermindGame) HandleInput(input string) (string, bool, bool) {
+ input = strings.TrimSpace(input)
+ lower := strings.ToLower(strings.TrimSpace(input))
+
+ if lower == "status" {
+ return m.doStatus(), false, false
+ }
+
+ if len(input) != 4 {
+ return "Enter exactly 4 digits (1-6, no repeats).", false, false
+ }
+
+ var guess [4]int
+ seen := make(map[int]bool)
+ for i, ch := range input {
+ if ch < '1' || ch > '6' {
+ return "Each digit must be 1-6.", false, false
+ }
+ d := int(ch - '0')
+ if seen[d] {
+ return "No repeating digits allowed.", false, false
+ }
+ seen[d] = true
+ guess[i] = d
+ }
+
+ exact := 0
+ partial := 0
+ for i := 0; i < 4; i++ {
+ if guess[i] == m.code[i] {
+ exact++
+ } else {
+ for j := 0; j < 4; j++ {
+ if guess[i] == m.code[j] {
+ partial++
+ break
+ }
+ }
+ }
+ }
+
+ m.guesses = append(m.guesses, MastermindGuess{
+ Digits: guess,
+ Exact: exact,
+ Partial: partial,
+ })
+
+ var sb strings.Builder
+ sb.WriteString(fmt.Sprintf("Attempt %d/%d: ", len(m.guesses), m.maxGuess))
+ for i, d := range guess {
+ match := "[ ]"
+ if guess[i] == m.code[i] {
+ match = "{40}[X]{/}"
+ } else {
+ for j := 0; j < 4; j++ {
+ if guess[i] == m.code[j] {
+ match = "{226}[O]{/}"
+ break
+ }
+ }
+ }
+ sb.WriteString(fmt.Sprintf("%d%s ", d, match))
+ }
+ sb.WriteString(fmt.Sprintf(" (%d locked, %d found)", exact, partial))
+
+ if exact == 4 {
+ m.gameOver = true
+ m.won = true
+ sb.WriteString("\n\nCode cracked! The encryption module yields.\nConnection terminated.")
+ return sb.String(), true, true
+ }
+
+ if len(m.guesses) >= m.maxGuess {
+ m.gameOver = true
+ m.won = false
+ sb.WriteString(fmt.Sprintf("\n\nOut of attempts. The code was: %d %d %d %d.\nConnection terminated.",
+ m.code[0], m.code[1], m.code[2], m.code[3]))
+ return sb.String(), true, false
+ }
+
+ return sb.String(), false, false
+}
+
+func (m *MastermindGame) doStatus() string {
+ if len(m.guesses) == 0 {
+ return fmt.Sprintf("No guesses yet. Remaining: %d/%d", m.maxGuess, m.maxGuess)
+ }
+ var sb strings.Builder
+ sb.WriteString("=== Attempts ===\n")
+ for i, g := range m.guesses {
+ sb.WriteString(fmt.Sprintf(" %2d: %d %d %d %d -> ", i+1, g.Digits[0], g.Digits[1], g.Digits[2], g.Digits[3]))
+ for j, d := range g.Digits {
+ match := "[ ]"
+ if g.Digits[j] == m.code[j] {
+ match = "{40}[X]{/}"
+ } else {
+ found := false
+ for k := 0; k < 4; k++ {
+ if g.Digits[j] == m.code[k] {
+ found = true
+ break
+ }
+ }
+ if found {
+ match = "{226}[O]{/}"
+ }
+ }
+ _ = d
+ sb.WriteString(match)
+ }
+ sb.WriteString(fmt.Sprintf(" (%d locked, %d found)\n", g.Exact, g.Partial))
+ }
+ sb.WriteString(fmt.Sprintf("Remaining: %d/%d\n", m.maxGuess-len(m.guesses), m.maxGuess))
+ return sb.String()
+}
+
+func (m *MastermindGame) BonusXP() int {
+ return (m.maxGuess - len(m.guesses)) * 15
+}