15c7959d96
A Go library that returns every legal play ranked by score and scores or validates plays, using the Appel-Jacobson DAWG algorithm over github.com/iliadenisov/dafsa v1.1.0. - DAWG move generation (across / down / both), full tournament scoring with a per-tile breakdown; public Solver: GenerateMoves (ranked), ScorePlay, ValidatePlay. - Rulesets: English Scrabble, Russian Scrabble, Эрудит (parameterizable Ruleset). - cmd/builddict (build the DAWG from the dictionaries submodule), cmd/stress (self-play benchmark), selfplay engine; brute-force test oracle. - A GADDAG was implemented, benchmarked and removed (the DAWG was smaller and faster for a scoring solver); see RESULTS.md and ALGORITHM.md.
58 lines
1.9 KiB
Go
58 lines
1.9 KiB
Go
// 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}
|
|
}
|