package main import ( "fmt" tea "github.com/charmbracelet/bubbletea" "os" "tildegit.org/jakew/wordle/game" "tildegit.org/jakew/wordle/game/score" "tildegit.org/jakew/wordle/game/util" "tildegit.org/jakew/wordle/game/words" "unicode" ) type model struct { board *game.Board buffer []rune error string } func initialModel() model { m := model{ board: game.NewBoard(), } lastGame, err := score.GetLastGame() if err != nil { m.error = fmt.Sprintf("Could not load last game: %v", err.Error()) return m } if lastGame != nil { m.error = fmt.Sprintf("Come back tomorrow for a new word") m.board = &game.Board{ Guesses: [6]game.Guess{}, CurrentGuess: lastGame.Score, Solution: "", Done: true, Day: lastGame.Day, } for i, line := range lastGame.Lines { m.board.Guesses[i] = game.Guess{ Letters: []rune(line.Word), Correct: line.Correct, Present: line.Present, } } } return m } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: m.error = "" switch msg.Type { case tea.KeyCtrlC: return m, tea.Quit case tea.KeyBackspace: if !m.board.Done && len(m.buffer) > 0 { m.buffer = m.buffer[:len(m.buffer)-1] } case tea.KeyEnter: if m.board.Done { return m, nil } if len(m.buffer) != 5 { m.error = "Word must be 5 long" } else if !words.IsValid(string(m.buffer)) { m.error = "Word not in dictionary" } else { msg := m.board.AddWord(string(m.buffer)) if msg != "" { m.error = msg return m, nil } // clear input m.buffer = []rune{} // save if win if m.board.Done { err := score.SaveBoard(m.board) if err != nil { m.error = fmt.Sprintf("Something went wrong saving the game: %v", err.Error()) } } } default: if m.board.Done || len(msg.Runes) < 1 { return m, nil } r := unicode.ToLower(msg.Runes[0]) if r >= 'a' && r <= 'z' && len(m.buffer) < 5 { m.buffer = append(m.buffer, r) } } } return m, nil } func (m model) View() string { // header s := headerStyle.Render(fmt.Sprintf("WORDLE #%d", m.board.Day)) + "\n" for i, guess := range m.board.Guesses { line := " " for j := 0; j < 5; j++ { if i < m.board.CurrentGuess { style := charEmptyStyle if util.ContainsInt(guess.Correct, j) { style = charCorrectStyle } else if util.ContainsInt(guess.Present, j) { style = charWrongPositionStyle } line += style.Render(string(unicode.ToUpper(guess.Letters[j])) + " ") } else { line += charEmptyStyle.Render("_ ") } } s += lineStyle.Render(line) + "\n" } if !m.board.Done { footer := "> " for i := 0; i < 5; i++ { if i < len(m.buffer) { footer += string(unicode.ToUpper(m.buffer[i])) } else { footer += "_" } footer += " " } footer += "<" s += promptStyle.Render(footer) + "\n" } else { msg := "" if m.board.CurrentGuess >= 6 { msg += fmt.Sprintf("Oof! The word was %s.", m.board.Solution) } else { msg += fmt.Sprintf("Congrats! You guessed in %d.", m.board.CurrentGuess) } msg += " Come back tomorrow for the next word. (Press CTRL+C to exit)" s += promptStyle.Render(msg) + "\n" } if m.error != "" { s += errorStyle.Render(m.error) + "\n" } return s } func main() { p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { fmt.Printf("Something has gone wrong: %v", err) os.Exit(1) } }