Skip to content

Commit c7ee5d7

Browse files
Add secret mode to Rock Paper Scissors game with additional options
1 parent 6885609 commit c7ee5d7

3 files changed

Lines changed: 108 additions & 25 deletions

File tree

cmd/rockpaperscissors.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"github.com/spf13/cobra"
1414
)
1515

16+
var secretMode bool
17+
1618
// rootCmd represents the base command when called without any subcommands
1719
var rockPaperScissorsCmd = &cobra.Command{
1820
Use: "rockpaperscissors",
@@ -21,6 +23,11 @@ var rockPaperScissorsCmd = &cobra.Command{
2123
You can choose from rock, paper, or scissors. The computer will randomly choose its move and the winner will be determined based on the rules of the game.`,
2224
Run: func(cmd *cobra.Command, args []string) {
2325
input := userPrompt.New(os.Stdin, os.Stdout, os.Stderr)
24-
rockpaperscissors.PlayGame(input)
26+
rockpaperscissors.PlayGame(input, secretMode)
2527
},
2628
}
29+
30+
func init() {
31+
rockPaperScissorsCmd.Flags().BoolVar(&secretMode, "spock", false, "Enable secret game mode")
32+
rootCmd.AddCommand(rockPaperScissorsCmd)
33+
}

internal/rockpaperscissors/rockpaperscissors.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99

1010
// Game options
1111
var (
12-
options = []string{"rock", "paper", "scissors", "exit"}
12+
standardOptions = []string{"rock", "paper", "scissors", "exit"}
13+
secretOptions = []string{"rock", "paper", "scissors", "lizard", "spock", "exit"}
1314
)
1415

1516
// Game represents a single game of Rock Paper Scissors.
@@ -32,14 +33,16 @@ type Game struct {
3233
GameOverMessage string
3334
// GamesPlayed tracks the number of games played
3435
GamesPlayed int
36+
// SecretMode indicates if the game is in secret mode
37+
SecretMode bool
3538
}
3639

3740
// Prompter defines an interface for getting user input
3841
type Prompter interface {
3942
Select(prompt, defaultValue string, options []string) (int, error)
4043
}
4144

42-
func NewGame(bestOf int) *Game {
45+
func NewGame(bestOf int, secretMode bool) *Game {
4346
if bestOf%2 == 0 {
4447
bestOf++ // Ensure we have an odd number for "best of"
4548
}
@@ -53,6 +56,7 @@ func NewGame(bestOf int) *Game {
5356
BestOf: bestOf,
5457
GameOver: false,
5558
GameOverMessage: "",
59+
SecretMode: secretMode,
5660
}
5761
}
5862

@@ -78,6 +82,10 @@ func (g *Game) Play(playerChoice string) {
7882
// getComputerChoice returns the choice made by the computer.
7983
func (g *Game) getComputerChoice() string {
8084
rand.Seed(time.Now().UnixNano())
85+
options := standardOptions
86+
if g.SecretMode {
87+
options = secretOptions
88+
}
8189
// Only use the game options excluding "exit"
8290
choices := options[:len(options)-1]
8391
return choices[rand.Intn(len(choices))]
@@ -88,10 +96,19 @@ func (g *Game) getWinner() string {
8896
if g.PlayerChoice == g.ComputerChoice {
8997
return "draw"
9098
}
91-
if (g.PlayerChoice == "rock" && g.ComputerChoice == "scissors") ||
92-
(g.PlayerChoice == "paper" && g.ComputerChoice == "rock") ||
93-
(g.PlayerChoice == "scissors" && g.ComputerChoice == "paper") {
94-
return "player"
99+
100+
winningMoves := map[string][]string{
101+
"rock": {"scissors", "lizard"},
102+
"paper": {"rock", "spock"},
103+
"scissors": {"paper", "lizard"},
104+
"lizard": {"paper", "spock"},
105+
"spock": {"rock", "scissors"},
106+
}
107+
108+
for _, beatenChoice := range winningMoves[g.PlayerChoice] {
109+
if g.ComputerChoice == beatenChoice {
110+
return "player"
111+
}
95112
}
96113
return "computer"
97114
}
@@ -143,7 +160,7 @@ func (g *Game) getRoundResultMessage() string {
143160
}
144161

145162
// PlayGame plays a game of Rock Paper Scissors.
146-
func PlayGame(prompter Prompter) {
163+
func PlayGame(prompter Prompter, secretMode bool) {
147164
// Get the number of rounds from the user
148165
roundOptions := []string{"3", "5", "7", "9"}
149166
roundIndex, err := prompter.Select("How many rounds would you like to play (best of)?", "3", roundOptions)
@@ -156,12 +173,20 @@ func PlayGame(prompter Prompter) {
156173
bestOf = parseInt(roundOptions[roundIndex])
157174
}
158175

159-
game := NewGame(bestOf)
176+
game := NewGame(bestOf, secretMode)
160177
fmt.Printf("Playing best of %d games\n", bestOf)
178+
if secretMode {
179+
fmt.Println("🖖 Secret mode activated: Rock Paper Scissors Lizard Spock!")
180+
}
161181

162182
for !game.GameOver {
163183
fmt.Printf("\nCurrent score - Player: %d, Computer: %d\n", game.PlayerScore, game.ComputerScore)
164-
184+
185+
options := standardOptions
186+
if secretMode {
187+
options = secretOptions
188+
}
189+
165190
// Get player choice using prompter
166191
playerChoiceIndex, err := prompter.Select("Choose your move", "rock", options)
167192
if err != nil {

internal/rockpaperscissors/rockpaperscissors_test.go

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,52 @@ package rockpaperscissors
22

33
import (
44
"fmt"
5+
"strings"
56
"testing"
67
)
78

89
func TestNewGame(t *testing.T) {
910
tests := []struct {
1011
name string
1112
bestOf int
13+
secretMode bool
1214
wantBestOf int
1315
wantGameOver bool
1416
wantGamesPlayed int
17+
wantSecretMode bool
1518
}{
1619
{
1720
name: "New game with odd number of rounds",
1821
bestOf: 3,
22+
secretMode: false,
1923
wantBestOf: 3,
2024
wantGameOver: false,
2125
wantGamesPlayed: 0,
26+
wantSecretMode: false,
2227
},
2328
{
2429
name: "New game with even number of rounds should increment to odd",
2530
bestOf: 4,
31+
secretMode: false,
2632
wantBestOf: 5,
2733
wantGameOver: false,
2834
wantGamesPlayed: 0,
35+
wantSecretMode: false,
36+
},
37+
{
38+
name: "New game with secret mode enabled",
39+
bestOf: 3,
40+
secretMode: true,
41+
wantBestOf: 3,
42+
wantGameOver: false,
43+
wantGamesPlayed: 0,
44+
wantSecretMode: true,
2945
},
3046
}
3147

3248
for _, tt := range tests {
3349
t.Run(tt.name, func(t *testing.T) {
34-
game := NewGame(tt.bestOf)
50+
game := NewGame(tt.bestOf, tt.secretMode)
3551
if game.BestOf != tt.wantBestOf {
3652
t.Errorf("NewGame() BestOf = %v, want %v", game.BestOf, tt.wantBestOf)
3753
}
@@ -40,6 +56,9 @@ func TestNewGame(t *testing.T) {
4056
}
4157
if game.GamesPlayed != tt.wantGamesPlayed {
4258
t.Errorf("NewGame() GamesPlayed = %v, want %v", game.GamesPlayed, tt.wantGamesPlayed)
59+
if game.SecretMode != tt.wantSecretMode {
60+
t.Errorf("NewGame() SecretMode = %v, want %v", game.SecretMode, tt.wantSecretMode)
61+
}
4362
}
4463
})
4564
}
@@ -50,69 +69,94 @@ func TestGame_getWinner(t *testing.T) {
5069
name string
5170
playerChoice string
5271
computerChoice string
72+
secretMode bool
5373
want string
5474
}{
5575
{
5676
name: "Player wins with rock vs scissors",
5777
playerChoice: "rock",
5878
computerChoice: "scissors",
79+
secretMode: false,
5980
want: "player",
6081
},
6182
{
6283
name: "Player wins with paper vs rock",
6384
playerChoice: "paper",
6485
computerChoice: "rock",
86+
secretMode: false,
6587
want: "player",
6688
},
6789
{
6890
name: "Player wins with scissors vs paper",
6991
playerChoice: "scissors",
7092
computerChoice: "paper",
93+
secretMode: false,
7194
want: "player",
7295
},
7396
{
7497
name: "Player loses with rock vs paper",
7598
playerChoice: "rock",
7699
computerChoice: "paper",
100+
secretMode: false,
77101
want: "computer",
78102
},
79103
{
80104
name: "Player loses with paper vs scissors",
81105
playerChoice: "paper",
82106
computerChoice: "scissors",
107+
secretMode: false,
83108
want: "computer",
84109
},
85110
{
86111
name: "Player loses with scissors vs rock",
87112
playerChoice: "scissors",
88113
computerChoice: "rock",
114+
secretMode: false,
89115
want: "computer",
90116
},
91117
{
92118
name: "Draw with same choices (rock)",
93119
playerChoice: "rock",
94120
computerChoice: "rock",
121+
secretMode: false,
95122
want: "draw",
96123
},
97124
{
98125
name: "Draw with same choices (paper)",
99126
playerChoice: "paper",
100127
computerChoice: "paper",
128+
secretMode: false,
101129
want: "draw",
102130
},
103131
{
104132
name: "Draw with same choices (scissors)",
105133
playerChoice: "scissors",
106134
computerChoice: "scissors",
135+
secretMode: false,
107136
want: "draw",
108137
},
138+
{
139+
name: "Secret mode - Player wins - rock beats lizard",
140+
playerChoice: "rock",
141+
computerChoice: "lizard",
142+
secretMode: true,
143+
want: "player",
144+
},
145+
{
146+
name: "Secret mode - Computer wins - spock beats rock",
147+
playerChoice: "rock",
148+
computerChoice: "spock",
149+
secretMode: true,
150+
want: "computer",
151+
},
109152
}
110153

111154
for _, tt := range tests {
112155
t.Run(tt.name, func(t *testing.T) {
113156
g := &Game{
114157
PlayerChoice: tt.playerChoice,
115158
ComputerChoice: tt.computerChoice,
159+
SecretMode: tt.secretMode,
116160
}
117161
if got := g.getWinner(); got != tt.want {
118162
t.Errorf("Game.getWinner() = %v, want %v", got, tt.want)
@@ -126,6 +170,7 @@ func TestGame_Play(t *testing.T) {
126170
name string
127171
bestOf int
128172
moves []string
173+
secretMode bool
129174
wantGameOver bool
130175
wantPlayerScore int
131176
wantComputerScore int
@@ -134,6 +179,7 @@ func TestGame_Play(t *testing.T) {
134179
name: "Game ends when player chooses exit",
135180
bestOf: 3,
136181
moves: []string{"exit"},
182+
secretMode: false,
137183
wantGameOver: true,
138184
wantPlayerScore: 0,
139185
wantComputerScore: 0,
@@ -142,6 +188,7 @@ func TestGame_Play(t *testing.T) {
142188
name: "Game continues for valid moves",
143189
bestOf: 3,
144190
moves: []string{"rock"},
191+
secretMode: false,
145192
wantGameOver: false,
146193
wantPlayerScore: 0, // Score will depend on random computer choice
147194
wantComputerScore: 0, // Score will depend on random computer choice
@@ -150,7 +197,7 @@ func TestGame_Play(t *testing.T) {
150197

151198
for _, tt := range tests {
152199
t.Run(tt.name, func(t *testing.T) {
153-
g := NewGame(tt.bestOf)
200+
g := NewGame(tt.bestOf, tt.secretMode)
154201
for _, move := range tt.moves {
155202
g.Play(move)
156203
}
@@ -302,7 +349,7 @@ func TestGame_getGameOverMessage(t *testing.T) {
302349
if got == "" {
303350
t.Error("getGameOverMessage() returned empty string")
304351
}
305-
if !contains(got, tt.wantMsgContains) {
352+
if !strings.Contains(got, tt.wantMsgContains) {
306353
t.Errorf("getGameOverMessage() = %v, want it to contain %v", got, tt.wantMsgContains)
307354
}
308355
})
@@ -337,43 +384,47 @@ func (m *mockPromptSequence) Select(prompt, defaultValue string, options []strin
337384

338385
func TestPlayGame(t *testing.T) {
339386
tests := []struct {
340-
name string
341-
prompter Prompter
342-
wantGameOver bool
387+
name string
388+
prompter Prompter
389+
secretMode bool
343390
}{
344391
{
345-
name: "Complete game sequence",
392+
name: "Complete game sequence - standard mode",
393+
prompter: &mockPromptSequence{
394+
returns: []int{1, 0, 0, 0}, // Select 5 rounds, then rock three times
395+
errors: []error{nil, nil, nil, nil},
396+
},
397+
secretMode: false,
398+
},
399+
{
400+
name: "Complete game sequence - secret mode",
346401
prompter: &mockPromptSequence{
347402
returns: []int{1, 0, 0, 0}, // Select 5 rounds, then rock three times
348403
errors: []error{nil, nil, nil, nil},
349404
},
405+
secretMode: true,
350406
},
351407
{
352408
name: "Error on rounds selection",
353409
prompter: &mockPromptSequence{
354410
returns: []int{0},
355411
errors: []error{fmt.Errorf("mock error")},
356412
},
413+
secretMode: false,
357414
},
358415
{
359416
name: "Error on move selection",
360417
prompter: &mockPromptSequence{
361418
returns: []int{0, 0},
362419
errors: []error{nil, fmt.Errorf("mock error")},
363420
},
364-
},
365-
{
366-
name: "Invalid round index",
367-
prompter: &mockPromptSequence{
368-
returns: []int{99, 3}, // Invalid round index, then exit
369-
errors: []error{nil, nil},
370-
},
421+
secretMode: false,
371422
},
372423
}
373424

374425
for _, tt := range tests {
375426
t.Run(tt.name, func(t *testing.T) {
376-
PlayGame(tt.prompter)
427+
PlayGame(tt.prompter, tt.secretMode)
377428
})
378429
}
379430
}

0 commit comments

Comments
 (0)