-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbeesolver.go
143 lines (128 loc) Β· 3.63 KB
/
beesolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package main
import (
"errors"
"flag"
"fmt"
"os"
"time"
)
func main() {
var dictionaryPath = flag.String("dict", "", "Path to a custom dictionary")
var wordsOutput = flag.Bool("words-output", true, "Default on. When off, the solution's words are hidden")
var help = flag.Bool("help", false, "Print the usage and exit")
flag.Usage = beeUsage
flag.Parse()
// if the help flag was passed in, handle that first
flag.Visit(func(f *flag.Flag) {
if f.Name == "help" {
usageAndExit(nil)
}
})
if *help {
usageAndExit(nil)
}
if err := validate(*dictionaryPath); err != nil {
usageAndExit(err)
}
required := rune(flag.Arg(0)[0])
others := flag.Arg(1)
puzzle, err := NewPuzzle(required, others)
if err != nil {
usageAndExit(err)
}
start := time.Now()
var dictionary Dictionary
var dictionaryName string
if *dictionaryPath == "" {
dictionary, err = NewDictionary()
dictionaryName = "(default)"
} else {
dictionary, err = NewDictionaryFromPath(*dictionaryPath)
dictionaryName = *dictionaryPath
}
if err != nil {
usageAndExit(err)
}
elapsedDictionary := time.Since(start)
fmt.Println("π")
fmt.Println("Hello and welcome to Spelling Bee Solver")
fmt.Println("ππ")
fmt.Println("πππ")
fmt.Println("Required Letter: ", string(required))
fmt.Println("Other Letters: ", others)
fmt.Println("Dictionary: ", dictionaryName)
fmt.Println("Dictionary words: ", len(dictionary.Words()))
fmt.Println("Solving now")
fmt.Println("ππππ")
solver := Solver{dictionary, puzzle}
start = time.Now()
solutions := solver.Solve()
elapsedSolve := time.Since(start)
fmt.Println("πππππ")
fmt.Println("Solved!")
fmt.Println()
fmt.Println(" Words:", len(solutions))
numPangrams := 0
for _, solution := range solutions {
if solution.IsPangram() {
numPangrams++
}
}
fmt.Println(" Pangrams:", numPangrams)
fmt.Printf(" Time loading dictionary: %d ms\n", elapsedDictionary.Milliseconds())
fmt.Printf(" Time solving: %d ms\n", elapsedSolve.Milliseconds())
fmt.Println("ππππππ")
if *wordsOutput {
for _, solution := range solutions {
if solution.IsPangram() {
fmt.Println(solution.Word(), " π³")
} else {
fmt.Println(solution.Word())
}
}
}
}
func validate(dictionaryPath string) error {
if flag.NArg() != 2 {
return fmt.Errorf("requires 2 arguments: REQUIRED_LETTER OTHER_LETTERS. Got %d", flag.NArg())
}
if len(flag.Arg(0)) != 1 {
return fmt.Errorf("REQUIRED_LETTER must be a single character. Got '%s'", flag.Arg(0))
}
requiredLetter := rune(flag.Arg(0)[0])
otherLetters := flag.Arg(1)
if len(otherLetters) != 6 {
return fmt.Errorf("OTHER_LETTERS must be exactly 6 letters, found: %d", len(otherLetters))
}
letters := make(map[rune]bool)
for _, ch := range otherLetters {
_, exists := letters[ch]
if exists {
return fmt.Errorf("OTHER_LETTERS cannot have any duplicates, found: '%s'", string(ch))
}
letters[ch] = true
}
if _, exists := letters[requiredLetter]; exists {
return fmt.Errorf("OTHER_LETTERS cannot contain the REQUIRED_LETTER: '%s'", string(requiredLetter))
}
if dictionaryPath != "" {
if _, err := os.Stat(dictionaryPath); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("dictionary file does not exist: %s", dictionaryPath)
}
}
return nil
}
func beeUsage() {
fmt.Println("Usage: beesolver REQUIRED_LETTER OTHER_LETTERS")
fmt.Println(" REQUIRED_LETTER The letter required in all words")
fmt.Println(" OTHER_LETTERS The 6 other allowed letters")
flag.PrintDefaults()
}
func usageAndExit(err error) {
flag.Usage()
if err != nil {
fmt.Println()
fmt.Println(err.Error())
}
os.Exit(1)
}