// Package rack represents a player's rack as per-letter tile counts plus blanks.
package rack

// Rack holds tile counts: one slot per alphabet letter index plus a final slot for
// blanks. Like a Go slice or map, a Rack value shares its underlying storage with its
// copies; use Clone for an independent rack. The move generator mutates a single Rack
// in place (removing a tile, recursing, putting it back).
type Rack struct {
	counts []int
}

// New returns an empty rack for an alphabet of the given size.
func New(alphabetSize int) Rack {
	return Rack{counts: make([]int, alphabetSize+1)}
}

func (r Rack) blankIdx() int { return len(r.counts) - 1 }

// Count returns how many tiles of the given letter index are on the rack.
func (r Rack) Count(letter byte) int { return r.counts[letter] }

// Has reports whether at least one tile of the given letter index is on the rack.
func (r Rack) Has(letter byte) bool { return r.counts[letter] > 0 }

// Blanks returns the number of blank tiles on the rack.
func (r Rack) Blanks() int { return r.counts[r.blankIdx()] }

// Total returns the number of tiles on the rack, blanks included.
func (r Rack) Total() int {
	n := 0
	for _, c := range r.counts {
		n += c
	}
	return n
}

// Empty reports whether the rack holds no tiles.
func (r Rack) Empty() bool { return r.Total() == 0 }

// Add puts a tile of the given letter index onto the rack.
func (r Rack) Add(letter byte) { r.counts[letter]++ }

// AddBlank puts a blank tile onto the rack.
func (r Rack) AddBlank() { r.counts[r.blankIdx()]++ }

// Remove takes one tile of the given letter index off the rack.
func (r Rack) Remove(letter byte) { r.counts[letter]-- }

// RemoveBlank takes one blank tile off the rack.
func (r Rack) RemoveBlank() { r.counts[r.blankIdx()]-- }

// Clone returns an independent copy of the rack.
func (r Rack) Clone() Rack {
	c := make([]int, len(r.counts))
	copy(c, r.counts)
	return Rack{counts: c}
}
