wordle/main.go

182 lines
3.9 KiB
Go

package main
import (
"bufio"
"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"
"tildegit.org/jakew/wordle/update"
"unicode"
)
type model struct {
board *game.Board
buffer []rune
error string
}
func initialModel() model {
m := model{
board: game.NewBoard(),
buffer: []rune{},
}
lastGame, err := score.GetLastGame()
if err != nil {
m.error = fmt.Sprintf("Could not load last game: %v", err.Error())
return m
}
if lastGame != nil && lastGame.Day == m.board.Day {
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 done
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 || m.board.CurrentGuess == -1 {
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 == -1 {
if m.board.Solution != "" {
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() {
updateMsg := update.CheckVersion(update.GetGiteaRelease("tildegit.org", "jakew", "wordle"), BuildVersion)
if updateMsg != "" {
fmt.Println(fmt.Sprintf("%v. Press enter to continue...", updateMsg))
bufio.NewReader(os.Stdin).ReadBytes('\n')
}
p := tea.NewProgram(initialModel())
if err := p.Start(); err != nil {
fmt.Printf("Something has gone wrong: %v", err)
os.Exit(1)
}
}