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() }