complete game

This commit is contained in:
Gabriel Augendre 2023-08-12 00:40:50 +02:00
parent 89bea6d276
commit e5463dd0bf
6 changed files with 79017 additions and 16 deletions

View file

@ -1,2 +1,2 @@
go 1.20 go 1.21.0
golangci-lint 1.53 golangci-lint 1.53

View file

@ -1,4 +1,87 @@
//nolint:forbidigo
package main package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"strings"
"github.com/Crocmagnon/gordle/lib/gordle"
)
const (
defaultWordLength = 5
defaultMaxAttempts = 5
)
func main() { func main() {
wordLength := flag.Int("l", defaultWordLength, "word length")
dictionary := flag.String("f", "", "dictionary file")
maxAttempts := flag.Int("m", defaultMaxAttempts, "maximum number of attempts")
flag.Parse()
if !validateInput(dictionary, wordLength, maxAttempts) {
os.Exit(1)
}
file, err := os.Open(*dictionary)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
word, err := gordle.PickWord(file, *wordLength)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Printf("length %d ; you have %d attempts\n", *wordLength, *maxAttempts)
scanner := bufio.NewScanner(os.Stdin)
game := gordle.New(*maxAttempts, word)
for !game.Over() && scanner.Scan() {
var feedback gordle.FullFeedback
text := strings.ToUpper(scanner.Text())
feedback, err = game.TryWord(text)
fmt.Println(feedback)
switch {
case errors.Is(err, gordle.ErrGameWon):
fmt.Println("you won")
case errors.Is(err, gordle.ErrGameLost):
fmt.Println("you lost")
}
}
}
func validateInput(dictionary *string, wordLength *int, maxAttempts *int) bool {
ok := true //nolint:varnamelen
if *dictionary == "" {
fmt.Println("dictionary file is mandatory")
ok = false
}
if *wordLength <= 0 {
fmt.Println("word length must be positive")
ok = false
}
if *maxAttempts <= 0 {
fmt.Println("max attempts must be positive")
ok = false
}
return ok
} }

78855
contrib/pli07.txt Normal file

File diff suppressed because it is too large Load diff

2
go.mod
View file

@ -1,3 +1,3 @@
module github.com/Crocmagnon/gordle module github.com/Crocmagnon/gordle
go 1.20 go 1.21

View file

@ -4,11 +4,40 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"io"
"math/rand" "math/rand"
"strings" "strings"
) )
var ErrWordNotFound = errors.New("word not found with requested length") var (
ErrWordNotFound = errors.New("word not found with requested length")
ErrGameWon = errors.New("won game")
ErrGameLost = errors.New("game over")
)
type (
Feedback string
FullFeedback []Feedback
)
func (ff FullFeedback) Wins() bool {
for _, f := range ff {
if f != FeedbackCorrect {
return false
}
}
return true
}
func (ff FullFeedback) String() string {
str := strings.Builder{}
for _, f := range ff {
str.WriteString(string(f))
}
return str.String()
}
const ( const (
FeedbackNotInWord = Feedback("N") FeedbackNotInWord = Feedback("N")
@ -16,9 +45,43 @@ const (
FeedbackCorrect = Feedback("C") FeedbackCorrect = Feedback("C")
) )
type Feedback string type Game struct {
maxAttempts int
currentAttempt int
pickedWord string
over bool
}
func GetWord(reader *strings.Reader, wordLength int) (string, error) { func New(maxAttempts int, pickedWord string) *Game {
g := &Game{
maxAttempts: maxAttempts,
pickedWord: pickedWord,
}
return g
}
func (g *Game) TryWord(word string) (FullFeedback, error) {
feedback := CheckWord(g.pickedWord, word)
if feedback.Wins() {
g.over = true
return feedback, ErrGameWon
}
g.currentAttempt++
if g.currentAttempt >= g.maxAttempts {
g.over = true
return feedback, ErrGameLost
}
return feedback, nil
}
func (g *Game) Over() bool {
return g.over
}
func PickWord(reader io.Reader, wordLength int) (string, error) {
var candidates []string var candidates []string
scanner := bufio.NewScanner(reader) scanner := bufio.NewScanner(reader)
@ -43,7 +106,7 @@ func GetWord(reader *strings.Reader, wordLength int) (string, error) {
return candidates[rnd], nil return candidates[rnd], nil
} }
func CheckWord(word, input string) []Feedback { func CheckWord(word, input string) FullFeedback {
wordRunes := []rune(word) wordRunes := []rune(word)
inputRunes := []rune(input) inputRunes := []rune(input)
res := []Feedback{} res := []Feedback{}

View file

@ -9,7 +9,7 @@ import (
"github.com/Crocmagnon/gordle/lib/gordle" "github.com/Crocmagnon/gordle/lib/gordle"
) )
func TestGetWord(t *testing.T) { func TestPickWord(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
@ -26,7 +26,7 @@ func TestGetWord(t *testing.T) {
t.Run(test.want, func(t *testing.T) { t.Run(test.want, func(t *testing.T) {
t.Parallel() t.Parallel()
input := "test\ntests" input := "test\ntests"
got, err := gordle.GetWord(strings.NewReader(input), test.length) got, err := gordle.PickWord(strings.NewReader(input), test.length)
if !errors.Is(err, test.wantErr) { if !errors.Is(err, test.wantErr) {
t.Fatalf("got err %q want %q", test.wantErr, err) t.Fatalf("got err %q want %q", test.wantErr, err)
} }
@ -38,26 +38,26 @@ func TestGetWord(t *testing.T) {
} }
} }
func TestTryWord(t *testing.T) { func TestCheckWord(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
word string word string
input string input string
want []gordle.Feedback want gordle.FullFeedback
}{ }{
{"empty", "", "", []gordle.Feedback{}}, {"empty", "", "", gordle.FullFeedback{}},
{"missing input", "i", "", []gordle.Feedback{gordle.FeedbackNotInWord}}, {"missing input", "i", "", gordle.FullFeedback{gordle.FeedbackNotInWord}},
{"correct single", "i", "i", []gordle.Feedback{gordle.FeedbackCorrect}}, {"correct single", "i", "i", gordle.FullFeedback{gordle.FeedbackCorrect}},
{"incorrect place", "ab", "ba", []gordle.Feedback{gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace}}, {"incorrect place", "ab", "ba", gordle.FullFeedback{gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace}},
{ {
"some correct some incorrect", "aba", "baa", "some correct some incorrect", "aba", "baa",
[]gordle.Feedback{gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace, gordle.FeedbackCorrect}, gordle.FullFeedback{gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace, gordle.FeedbackCorrect},
}, },
{ {
"complex", "testing", "xsesing", "complex", "testing", "xsesing",
[]gordle.Feedback{ gordle.FullFeedback{
gordle.FeedbackNotInWord, gordle.FeedbackNotInWord,
gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace,
gordle.FeedbackWrongPlace, gordle.FeedbackWrongPlace,