Skip to content

Commit afddad7

Browse files
committed
fix(engine): add input validation found during dogfooding
- Validate category names in Score() against AllCategories - Validate dice indices in Hold() are within 0-4 range - Skip zero-value dice in counts() to prevent false Yahtzee on unrolled dice - Panic on NewGame with 0 players to prevent index-out-of-range
1 parent 6d4614c commit afddad7

3 files changed

Lines changed: 23 additions & 1 deletion

File tree

engine/category.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,14 @@ var AllCategories = []Category{
2626

2727
var UpperCategories = []Category{Ones, Twos, Threes, Fours, Fives, Sixes}
2828

29+
func IsValidCategory(c Category) bool {
30+
for _, cat := range AllCategories {
31+
if cat == c {
32+
return true
33+
}
34+
}
35+
return false
36+
}
37+
2938
const UpperBonusThreshold = 63
3039
const UpperBonusValue = 35

engine/game.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type PlayerState struct {
5353
}
5454

5555
func NewGame(playerNames []string, src rand.Source) *Game {
56+
if len(playerNames) == 0 {
57+
panic("NewGame requires at least 1 player")
58+
}
5659
if src == nil {
5760
src = rand.NewSource(time.Now().UnixNano())
5861
}
@@ -95,6 +98,11 @@ func (g *Game) Hold(indices []int) error {
9598
if g.RollCount >= MaxRolls {
9699
return errors.New("cannot hold: max rolls reached")
97100
}
101+
for _, idx := range indices {
102+
if idx < 0 || idx > 4 {
103+
return fmt.Errorf("cannot hold: index %d out of range (0-4)", idx)
104+
}
105+
}
98106
g.Dice = Reroll(g.Dice, indices, g.rng)
99107
g.RollCount++
100108
if g.RollCount >= MaxRolls {
@@ -110,6 +118,9 @@ func (g *Game) Score(category Category) error {
110118
if g.RollCount == 0 {
111119
return errors.New("cannot score: must roll first")
112120
}
121+
if !IsValidCategory(category) {
122+
return fmt.Errorf("cannot score: invalid category %q", category)
123+
}
113124
player := &g.Players[g.Current]
114125
if player.Scorecard.IsFilled(category) {
115126
return fmt.Errorf("cannot score: category %s already filled", category)

engine/scoring.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ func sum(dice [5]int) int {
7373
func counts(dice [5]int) map[int]int {
7474
m := make(map[int]int)
7575
for _, d := range dice {
76-
m[d]++
76+
if d > 0 {
77+
m[d]++
78+
}
7779
}
7880
return m
7981
}

0 commit comments

Comments
 (0)