diff --git a/client/client.go b/client/client.go index cfb4bb5..1d4e462 100644 --- a/client/client.go +++ b/client/client.go @@ -104,6 +104,8 @@ func (c *Client) processMessage(message *messages.Message) { c.handleDiceRolled(message) case messages.GameOver: c.handleGameOverMessage(message) + case messages.Error: + c.handleErrorMessage(message) default: fmt.Println("Unknown message type:", message.Type) } @@ -175,7 +177,16 @@ func (c *Client) handleGameStartedMessage(message *messages.Message) { func (c *Client) handleGameOverMessage(message *messages.Message) { log.Println("Game over") - // TODO - display winner + players := make([]game.PlayerInfo, 0) + for _, player := range message.Players { + players = append(players, *player) + } + c.ioHandler.DisplayGameOver(players) +} + +func (c *Client) handleErrorMessage(message *messages.Message) { + log.Printf("Error from server: %s", message.ErrorMessage) + fmt.Printf("\nāš ļø Error: %s\n\n", message.ErrorMessage) } func (c *Client) handleUpdateScorecard(message *messages.Message) { diff --git a/client/console_io_handler.go b/client/console_io_handler.go index bd1c590..0ce5b08 100644 --- a/client/console_io_handler.go +++ b/client/console_io_handler.go @@ -98,6 +98,50 @@ func (c *ConsoleIOHandler) DisplayDice(dice []game.Dice) { fmt.Println() } +func (c *ConsoleIOHandler) DisplayGameOver(players []game.PlayerInfo) { + fmt.Println("\n" + strings.Repeat("=", 50)) + color.Set(color.FgYellow, color.Bold) + fmt.Println("GAME OVER!") + color.Unset() + fmt.Println(strings.Repeat("=", 50)) + + // Display final scoreboard + c.DisplayCurrentScoreboard(players) + + // Find winner(s) + maxScore := 0 + winners := []game.PlayerInfo{} + for _, player := range players { + score := game.CalculateTotalScore(player.ScoreCard) + if score > maxScore { + maxScore = score + winners = []game.PlayerInfo{player} + } else if score == maxScore { + winners = append(winners, player) + } + } + + // Display winner(s) + fmt.Println() + if len(winners) == 1 { + color.Set(color.FgGreen, color.Bold) + fmt.Printf("šŸ† Winner: %s with %d points! šŸ†\n", winners[0].Name, maxScore) + color.Unset() + } else { + color.Set(color.FgCyan, color.Bold) + fmt.Printf("šŸ† It's a tie! ") + for i, winner := range winners { + if i > 0 { + fmt.Print(" and ") + } + fmt.Print(winner.Name) + } + fmt.Printf(" with %d points! šŸ†\n", maxScore) + color.Unset() + } + fmt.Println(strings.Repeat("=", 50)) +} + func (c *ConsoleIOHandler) askJoinOrCreateRoom() ChoiceType { var joinOrCreate string prompt := &survey.Select{ diff --git a/client/iohandler.go b/client/iohandler.go index b80fce1..8954c3d 100644 --- a/client/iohandler.go +++ b/client/iohandler.go @@ -14,6 +14,7 @@ type IOHandler interface { ChooseCategory(*game.PlayerInfo, []game.Dice) game.ScoreCategory DisplayCurrentScoreboard([]game.PlayerInfo) DisplayDice([]game.Dice) + DisplayGameOver([]game.PlayerInfo) askJoinOrCreateRoom() ChoiceType askRoomName() string askRoomSelection([]string) string diff --git a/client/main b/client/main new file mode 100755 index 0000000..0e209b0 Binary files /dev/null and b/client/main differ diff --git a/client/mock_iohandler.go b/client/mock_iohandler.go index 61e8efb..0391b32 100644 --- a/client/mock_iohandler.go +++ b/client/mock_iohandler.go @@ -21,6 +21,11 @@ func (m *MockIOHandler) DisplayDice(dice []game.Dice) { m.displayedDice = append(m.displayedDice, dice) } +func (m *MockIOHandler) DisplayGameOver(players []game.PlayerInfo) { + // Mock implementation - just store for testing + m.displayedScoreboards = append(m.displayedScoreboards, players) +} + func (m *MockIOHandler) GetPlayerHoldInput(dice []game.Dice) []int { m.getHoldInputCalls = append(m.getHoldInputCalls, dice) return []int{1, 3} diff --git a/game/score.go b/game/score.go index 9b0fc44..d22334f 100644 --- a/game/score.go +++ b/game/score.go @@ -25,18 +25,28 @@ func calculateUpperSection(number int) ScoreCalculator { } func calculateThreeOfAKind(diceCounts []int) int { - for i, count := range diceCounts { + for _, count := range diceCounts { if count >= 3 { - return (i + 1) * 3 + // Return sum of all dice + total := 0 + for i, c := range diceCounts { + total += (i + 1) * c + } + return total } } return 0 } func calculateFourOfAKind(diceCounts []int) int { - for i, count := range diceCounts { + for _, count := range diceCounts { if count >= 4 { - return (i + 1) * 4 + // Return sum of all dice + total := 0 + for i, c := range diceCounts { + total += (i + 1) * c + } + return total } } return 0 @@ -113,8 +123,30 @@ func countDiceValues(dice []Dice) []int { func CalculateTotalScore(scoreCard ScoreCard) int { total := 0 - for _, score := range scoreCard.Scores { - total += score + + // Calculate Upper Section total + upperSectionTotal := 0 + upperSectionCategories := []ScoreCategory{Ones, Twos, Threes, Fours, Fives, Sixes} + for _, category := range upperSectionCategories { + upperSectionTotal += scoreCard.Scores[category] } + + // Add Upper Section bonus if >= 63 + const upperSectionBonusThreshold = 63 + const upperSectionBonus = 35 + total += upperSectionTotal + if upperSectionTotal >= upperSectionBonusThreshold { + total += upperSectionBonus + } + + // Add Lower Section scores + lowerSectionCategories := []ScoreCategory{ + ThreeOfAKind, FourOfAKind, FullHouse, + SmallStraight, LargeStraight, Yahtzee, Chance, + } + for _, category := range lowerSectionCategories { + total += scoreCard.Scores[category] + } + return total } diff --git a/messages/messages.go b/messages/messages.go index d635cf1..e2b236e 100644 --- a/messages/messages.go +++ b/messages/messages.go @@ -30,6 +30,7 @@ const ( WaitForPlayers RoomFull ServerJoin + Error ) type Message struct { @@ -42,4 +43,5 @@ type Message struct { Category game.ScoreCategory RoomID string RoomList []string + ErrorMessage string } diff --git a/server/gameplay_controller.go b/server/gameplay_controller.go index 75be87d..94083bf 100644 --- a/server/gameplay_controller.go +++ b/server/gameplay_controller.go @@ -108,9 +108,18 @@ func (gpc *GamePlayController) RerollDice(roomID string, player *Player, dice [] log.Println("Error getting room:", err.Error()) return } - if room.diceRolls >= game.NumberOfDice { - // TODO: Send error message - log.Println("Cannot reroll dice more than", game.NumberOfDice, "times") + if room.diceRolls >= game.MaxRolls { + errorMsg := fmt.Sprintf("Cannot reroll dice more than %d times", game.MaxRolls) + log.Println(errorMsg) + message := messages.Message{ + Type: messages.Error, + RoomID: roomID, + ErrorMessage: errorMsg, + } + err := player.Connection.Encode(&message) + if err != nil { + log.Println("Error encoding error message:", err.Error()) + } return } // rough implementation of rerolling dice diff --git a/server/main b/server/main new file mode 100755 index 0000000..b600f26 Binary files /dev/null and b/server/main differ