-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.go
188 lines (174 loc) · 4.57 KB
/
game.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package blackjack
import (
"bufio"
"errors"
"io"
"log"
"math/rand"
"strings"
"time"
)
var (
// ErrNoWinner is returned when no player wins the game.
ErrNoWinner = errors.New("no game winner")
)
// Game represents the deck and dicard pile of cards.
type Game struct {
deck Deck
discard DiscardPile
players []Player
}
// StartGame runs the game loop
func StartGame(read io.Reader) {
cardsDeck := NewDeck(13)
game := Game{
deck: cardsDeck,
discard: make(DiscardPile, len(cardsDeck)),
players: []Player{{name: "Human"}, {name: "CPU"}},
}
defer func() {
log.Printf("\nTotal rounds played: %d.\n", game.Rounds())
}()
human := &game.players[0]
cpu := &game.players[1]
// Remove timestamp from logger
log.SetFlags(0)
log.Printf("%s", ShowPlayersStatsAndCards(game.players))
reader := bufio.NewReader(read)
for {
// Deal card to CPU player
cpu.UpdateCardsInHand(game.DealCardFromDeck())
// Show 1 hidden and 1 face-up card of CPU player
// Deal card to human player
human.UpdateCardsInHand(game.DealCardFromDeck())
// Show all face-up card to human player
// 'hit' or 'stay' loop start until both CPU & human
// says 'stay' or deck is empty
humanInput := true
cpuInput := true
for cpuInput || humanInput {
log.Printf("%s", ShowPlayersStatsAndCards(game.players))
if humanInput {
log.Printf("\n%s", "Enter h ('hit') or s ('stay') or q ('quit'):")
input, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
switch strings.ToLower(strings.TrimRight(input, "\n")) {
case "h", "hit":
human.UpdateCardsInHand(game.DealCardFromDeck())
case "s", "stay":
humanInput = false
case "q", "quit":
return
}
}
if cpuInput {
switch CPUHit(*cpu) {
case true:
cpu.UpdateCardsInHand(game.DealCardFromDeck())
default:
cpuInput = false
}
}
}
// Find and reveal winner
// Update rounds win or lose for both players
name, err := game.RoundWinner()
switch err {
case ErrNoWinner:
log.Println(err.Error())
default:
log.Printf("\nPlayer %s wins the round.\n", name)
}
game.RoundReset()
// Check if user wants to quit
log.Printf("\n%s", "Enter q ('quit'):")
input, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
switch strings.ToLower(strings.TrimRight(input, "\n")) {
case "q", "quit":
return
}
}
}
// DealCardFromDeck returns a card from deck and also moves it to discard pile
// if all cards in discard pile, then move from discard pile to deck
func (game *Game) DealCardFromDeck() Card {
rand.Seed(time.Now().UnixNano())
cardIndex := -1
if CheckDiscardPileFull(game.discard) {
ResetDiscardPile(game.discard)
}
for CardExistsInDiscardPile(cardIndex, game.discard) {
cardIndex = rand.Intn(len(game.deck))
}
MoveCardToDiscardPile(cardIndex, game.discard)
return game.deck[cardIndex]
}
// RoundWinner is used to find the game round winner
func (game *Game) RoundWinner() (string, error) {
var highScore uint
var winner *Player
for index := range game.players {
player := &game.players[index]
if player.currentScore > highScore && player.currentScore <= 21 {
highScore = player.currentScore
winner = player
}
// Since there is only one winner, we intentionally update
// the loss for all players in this loop. If we successfully
// find the winner we deduct its loss and increments its wins.
player.loss++
}
if winner == nil {
return "", ErrNoWinner
}
winner.loss--
winner.wins++
return winner.name, nil
}
// ShowPlayersStatsAndCards returns string with all players'
// stats and cards in hand.
func ShowPlayersStatsAndCards(players []Player) string {
var output strings.Builder
for _, player := range players {
output.WriteString(player.ShowCardsInHand())
output.WriteString(player.ShowStats())
}
return output.String()
}
// CPUHit chooses where hit or stay for a
// CPU player. If true then hit else stay.
func CPUHit(p Player) bool {
if p.currentScore >= 21 {
return false
}
if len(p.cardsInHand) >= 2 && p.currentScore >= 17 {
return false
}
return true
}
// RoundReset reset the cards in hand
// for all players and their score.
func (game *Game) RoundReset() {
for index := range game.players {
player := &game.players[index]
player.ResetCardsInHand()
player.ResetCurrentScore()
}
}
// Rounds returns the number of rounds played
func (game *Game) Rounds() uint {
var rounds uint
for _, player := range game.players {
// All players will have played same number
// of rounds. Hence, we check only for one
// players loss & wins.
rounds = player.loss + player.wins
break
}
return rounds
}