210 lines
4.0 KiB
Go
210 lines
4.0 KiB
Go
package types
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
const (
|
|
GUESS_COUNT_LIMIT int = 6
|
|
GUESS_WORD_LENGTH int = 5
|
|
|
|
ALPHABET_COUNT int = 26
|
|
)
|
|
|
|
type GuessStateType int
|
|
|
|
const (
|
|
GuessStateNotGuessed GuessStateType = iota
|
|
GuessStateWrongChar
|
|
GuessStateWrongPos
|
|
GuessStateCorrect
|
|
)
|
|
|
|
type GuessChar struct {
|
|
Char rune
|
|
State GuessStateType
|
|
}
|
|
|
|
func (self GuessChar) View() string {
|
|
style := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("#FFFFFF"))
|
|
switch self.State {
|
|
case GuessStateWrongChar:
|
|
style = style.Background(lipgloss.Color("#3a3a3c"))
|
|
case GuessStateWrongPos:
|
|
style = style.Background(lipgloss.Color("#b59f3b"))
|
|
case GuessStateCorrect:
|
|
style = style.Background(lipgloss.Color("#538d4e"))
|
|
}
|
|
return style.Render(string(self.Char))
|
|
}
|
|
|
|
type Guess struct {
|
|
Data [GUESS_WORD_LENGTH]GuessChar
|
|
}
|
|
|
|
type guessLeftType struct {
|
|
Rune rune
|
|
Idx int
|
|
}
|
|
|
|
func NewGuess(guess, ans string) Guess {
|
|
var ret Guess
|
|
|
|
var ansLeft []rune
|
|
var guessLeft []guessLeftType
|
|
for i := range ret.Data {
|
|
ret.Data[i] = GuessChar{
|
|
Char: rune(guess[i]),
|
|
State: GuessStateWrongChar,
|
|
}
|
|
if guess[i] == ans[i] {
|
|
ret.Data[i].State = GuessStateCorrect
|
|
} else {
|
|
ansLeft = append(ansLeft, rune(ans[i]))
|
|
guessLeft = append(guessLeft, guessLeftType{
|
|
Rune: rune(guess[i]),
|
|
Idx: i,
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.Slice(ansLeft, func(i, j int) bool {
|
|
return int32(ansLeft[i]) < int32(ansLeft[j])
|
|
})
|
|
|
|
sort.Slice(guessLeft, func(i, j int) bool {
|
|
return int32(guessLeft[i].Rune) < int32(guessLeft[j].Rune)
|
|
})
|
|
|
|
i, j := 0, 0
|
|
for i < len(ansLeft) && j < len(guessLeft) {
|
|
if int32(ansLeft[i]) == int32(guessLeft[j].Rune) {
|
|
ret.Data[guessLeft[j].Idx].State = GuessStateWrongPos
|
|
i++
|
|
j++
|
|
} else {
|
|
if int32(ansLeft[i]) < int32(guessLeft[j].Rune) {
|
|
i++
|
|
} else {
|
|
j++
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (self Guess) View() string {
|
|
var b strings.Builder
|
|
for _, ch := range self.Data {
|
|
b.WriteString(ch.View())
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
type WordleState struct {
|
|
History map[UsernameType][GUESS_COUNT_LIMIT]Guess
|
|
CurrentGuess map[UsernameType]string
|
|
|
|
Round int
|
|
GameEnd bool
|
|
}
|
|
|
|
func NewWordleState() WordleState {
|
|
return WordleState{
|
|
History: make(map[UsernameType][GUESS_COUNT_LIMIT]Guess),
|
|
CurrentGuess: make(map[UsernameType]string),
|
|
|
|
Round: 0,
|
|
GameEnd: false,
|
|
}
|
|
}
|
|
|
|
func (self *WordleState) View() string {
|
|
var b strings.Builder
|
|
|
|
col := []string{}
|
|
for user := range self.History {
|
|
col = append(col, user)
|
|
}
|
|
|
|
sort.Slice(col, func(i, j int) bool {
|
|
return col[i] < col[j]
|
|
})
|
|
|
|
fmt.Fprintf(&b, " ")
|
|
for _, user := range col {
|
|
fmt.Fprintf(&b, "%5s ", user)
|
|
}
|
|
b.WriteRune('\n')
|
|
|
|
table := make([][]string, GUESS_COUNT_LIMIT)
|
|
for i := range table {
|
|
table[i] = make([]string, len(col))
|
|
}
|
|
for j, user := range col {
|
|
for i, guess := range self.History[user] {
|
|
table[i][j] = guess.View()
|
|
}
|
|
}
|
|
|
|
for i, row := range table {
|
|
fmt.Fprintf(&b, "%1d ", i+1)
|
|
for _, e := range row {
|
|
b.WriteString(e + " ")
|
|
}
|
|
b.WriteRune('\n')
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
type WordBank struct {
|
|
Data [ALPHABET_COUNT]GuessStateType
|
|
}
|
|
|
|
func NewWordBank(state WordleState) WordBank {
|
|
// FIXME: breaks when enter ^A-Z
|
|
var ret WordBank
|
|
for _, guesses := range state.History {
|
|
for i := 0; i < state.Round; i++ {
|
|
for _, ch := range guesses[i].Data {
|
|
ret.Data[int32(ch.Char)-'A'] = max(
|
|
ret.Data[int32(ch.Char)-'A'],
|
|
ch.State)
|
|
}
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (self WordBank) View() string {
|
|
var b strings.Builder
|
|
|
|
for i, state := range self.Data {
|
|
style := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("#FFFFFF"))
|
|
switch state {
|
|
case GuessStateNotGuessed:
|
|
style = style.Background(lipgloss.Color("#2c3032"))
|
|
case GuessStateWrongChar:
|
|
style = style.Background(lipgloss.Color("#3a3a3c"))
|
|
case GuessStateWrongPos:
|
|
style = style.Background(lipgloss.Color("#b59f3b"))
|
|
case GuessStateCorrect:
|
|
style = style.Background(lipgloss.Color("#538d4e"))
|
|
}
|
|
b.WriteString(style.Render(string('A' + i)))
|
|
if i%10 == 9 {
|
|
b.WriteRune('\n')
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|