mirror of
https://github.com/Crocmagnon/gordle.git
synced 2024-11-24 09:08:11 +01:00
complete game
This commit is contained in:
parent
89bea6d276
commit
e5463dd0bf
6 changed files with 79017 additions and 16 deletions
|
@ -1,2 +1,2 @@
|
||||||
go 1.20
|
go 1.21.0
|
||||||
golangci-lint 1.53
|
golangci-lint 1.53
|
||||||
|
|
|
@ -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
78855
contrib/pli07.txt
Normal file
File diff suppressed because it is too large
Load diff
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
||||||
module github.com/Crocmagnon/gordle
|
module github.com/Crocmagnon/gordle
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue