package scrabble import ( dawg "github.com/iliadenisov/dafsa" "scrabble-solver/board" "scrabble-solver/internal/encoding" ) // letterSet is a bit set over alphabet letter indexes (alphabets are at most 63 // letters, so a uint64 suffices). It encodes a square's cross-set: the letters that, // placed on the square, form a legal perpendicular word. type letterSet uint64 func (s letterSet) has(l byte) bool { return s&(letterSet(1)<= 0 && b.Filled(start-1, c) { start-- } for rr := start; rr < r; rr++ { above = append(above, encoding.Letter(b.At(rr, c))) } end := r for end+1 < b.Rows() && b.Filled(end+1, c) { end++ } for rr := r + 1; rr <= end; rr++ { below = append(below, encoding.Letter(b.At(rr, c))) } return above, below } // completers returns the letters X (< size) that complete a word when followed from // state: those whose arc leads directly to an accepting node. It is a single arc // enumeration — the deterministic cross-set primitive. func completers(cur *dawg.Cursor, state dawg.Node, size int) letterSet { var set letterSet lim := byte(size) cur.Arcs(state, func(a dawg.Arc) bool { if a.Final && a.Label < lim { set |= letterSet(1) << a.Label } return true }) return set } // walk follows word left to right from the cursor's root. func walk(cur *dawg.Cursor, word []byte) (dawg.Node, bool) { n := cur.Root() for _, l := range word { var ok bool if n, _, ok = cur.Next(n, l); !ok { return n, false } } return n, true } // dawgCrossSet returns the letters X for which above·X·below is a stored word. A right // extension (no tiles below) is deterministic — X just completes the prefix above. A // left extension (tiles below) is non-deterministic and must probe each X. func dawgCrossSet(cur *dawg.Cursor, above, below []byte, size int) letterSet { switch { case len(above) == 0 && len(below) == 0: return fullSet(size) case len(below) == 0: node, ok := walk(cur, above) if !ok { return 0 } return completers(cur, node, size) default: node := cur.Root() if len(above) > 0 { var ok bool if node, ok = walk(cur, above); !ok { return 0 } } var set letterSet for x := range size { m, final, ok := cur.Next(node, byte(x)) if !ok { continue } for _, l := range below { if m, final, ok = cur.Next(m, l); !ok { break } } if ok && final { set |= letterSet(1) << uint(x) } } return set } }