Skip to content

Commit

Permalink
Merge pull request #1 from CaffeinatedTech/bubbletea
Browse files Browse the repository at this point in the history
Added bubbletea
  • Loading branch information
CaffeinatedTech committed Apr 21, 2024
2 parents 5f570f1 + fdc199d commit 5caef02
Show file tree
Hide file tree
Showing 8 changed files with 521 additions and 205 deletions.
48 changes: 48 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Created by https://www.toptal.com/developers/gitignore/api/vim,go
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,go

### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work

### Vim ###
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

# End of https://www.toptal.com/developers/gitignore/api/vim,go
246 changes: 212 additions & 34 deletions algo_kata.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
package main

import "flag"
import "fmt"
import "strings"
import "strconv"
import "time"
import "github.com/charmbracelet/bubbles/textinput"
import tea "github.com/charmbracelet/bubbletea"

type languages []string

func (l *languages) String() string {
return fmt.Sprint(*l)
}
func (l *languages) Set(value string) error {
for _, lang := range strings.Split(value, ",") {
*l = append(*l, lang)
}
return nil
type Language struct {
Name string `mapstructure:"name"`
Selected bool `mapstructure:"selected"`
}

type Algorithm struct {
Name string `mapstructure:"name"`
Type string `mapstructure:"type"`
Sorted bool `mapstructure:"sorted"`
Name string `mapstructure:"name"`
Type string `mapstructure:"type"`
Sorted bool `mapstructure:"sorted"`
Selected bool `mapstructure:"selected"`
}

type Session struct {
Algorithms []Algorithm
Languages []string
Languages []Language
Num int
}

Expand All @@ -36,24 +31,207 @@ type Result struct {
Correct bool
}

type State int64

const (
CHECK_CONFIG State = iota // Check the configuration toml file
WELCOME // Show the welcome screen, and allow selecting languages, and algorithms
QUESTION_COUNT // Ask the user for the number of questions they want to practice
INTERMISSION // Present the next question's language, and wait for user to be ready
QUESTION // Show the question, and await the response
COMPLETE // Show the final score table, and quit
)

type Config struct {
Algorithms []Algorithm
Languages []Language
QuestionCount int
}

type statusMsg int
type stateMsg State
type configMsg Config
type errMsg struct{ err error }

func (e errMsg) Error() string { return e.err.Error() }

type nextQuestion struct {
algorithm Algorithm
language string
array []int
expectedResult int
expectedResultIndex int
startTime time.Time
}

type model struct {
state State
session Session
results []Result
nextQuestion nextQuestion
lastAnswerCorrect string
cursor int
num_ti textinput.Model
answer_ti textinput.Model
}

func (m model) Init() tea.Cmd {
return m.checkConfig
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "q" || msg.String() == "esc" || msg.String() == "ctrl+c" {
return m, tea.Quit
}
if m.state == WELCOME {
optionCount := len(m.session.Languages) + len(m.session.Algorithms)
switch msg.String() {
case "j", "down":
m.cursor++
if m.cursor >= optionCount {
m.cursor = 0
}
case "k", "up":
m.cursor--
if m.cursor < 0 {
m.cursor = 0
}
case " ":
if m.cursor < len(m.session.Languages) {
m.session.Languages[m.cursor].Selected = !m.session.Languages[m.cursor].Selected
} else {
m.session.Algorithms[m.cursor-len(m.session.Languages)].Selected = !m.session.Algorithms[m.cursor-len(m.session.Languages)].Selected
}
case "enter":
// Validate the options. Must have at least one language, and at least one algorithm.
if m.validateOptions() {
m.state = QUESTION_COUNT
}
}
} else if m.state == QUESTION_COUNT {
switch msg.String() {
case "enter":
m.session.Num, _ = strconv.Atoi(m.num_ti.Value())
if m.session.Num > m.countChecked() {
m.session.Num = m.countChecked()
} else if m.session.Num < 1 {
m.session.Num = 1
}
saveConfig(m.session.Algorithms, m.session.Languages, m.session.Num)
// Decide the next question and language, and generate the random array
nextAlgorithm, nextLanguage := m.randomAlgorithmAndLang()
m.nextQuestion = nextQuestion{algorithm: nextAlgorithm, language: nextLanguage}
m.nextQuestion.array = randomArray(10, nextAlgorithm.Sorted)
m.nextQuestion.expectedResult, m.nextQuestion.expectedResultIndex = expectedResult(m.nextQuestion.array)
m.state = INTERMISSION
}
} else if m.state == INTERMISSION {
switch msg.String() {
case "enter":
m.nextQuestion.startTime = time.Now()
m.state = QUESTION
}
} else if m.state == QUESTION {
switch msg.String() {
case "enter":
// Check the answer
correct := answerCheck(m.nextQuestion.array, m.nextQuestion.expectedResultIndex, m.nextQuestion.algorithm.Type, m.answer_ti.Value())
m.lastAnswerCorrect = green("Correct")
if !correct {
m.lastAnswerCorrect = red("Incorrect")
}
elapsedTime := time.Since(m.nextQuestion.startTime)
m.results = append(m.results, Result{
Algorithm: m.nextQuestion.algorithm.Name,
Language: m.nextQuestion.language,
Time: elapsedTime,
Correct: correct,
})
if len(m.results) >= m.session.Num {
m.state = COMPLETE
} else {
nextAlgorithm, nextLanguage := m.randomAlgorithmAndLang()
m.nextQuestion = nextQuestion{algorithm: nextAlgorithm, language: nextLanguage}
m.nextQuestion.array = randomArray(10, nextAlgorithm.Sorted)
m.nextQuestion.expectedResult, m.nextQuestion.expectedResultIndex = expectedResult(m.nextQuestion.array)
m.answer_ti.SetValue("")
m.state = INTERMISSION
}

}
} else if m.state == COMPLETE {
return m, tea.Quit
}
case errMsg:
return m, tea.Quit
case configMsg:
m.session.Languages = msg.Languages
m.session.Algorithms = msg.Algorithms
m.session.Num = msg.QuestionCount
m.state = WELCOME
return m, nil
}
if m.state == QUESTION_COUNT {
var cmd tea.Cmd
m.num_ti, cmd = m.num_ti.Update(msg)
return m, cmd
}
if m.state == QUESTION {
var cmd tea.Cmd
m.answer_ti, cmd = m.answer_ti.Update(msg)
return m, cmd
}
return m, nil
}

func (m model) View() string {
switch m.state {
case WELCOME:
return m.welcomMessage()
case QUESTION_COUNT:
max_count := m.countChecked()
return fmt.Sprintf(
"How many questions will you do? (max: %d)\n\n%s\n\n%s",
max_count,
m.num_ti.View(),
"(esc to quit)",
) + "\n"
case INTERMISSION:
return m.intermissionMessage()
case QUESTION:
msg := m.questionMessage()
return fmt.Sprintf(
"%s\n\nYour answer: %s\n\n%s",
msg,
m.answer_ti.View(),
"(esc to quit)",
) + "\n"
case COMPLETE:
return printResults(m.results)
}
return ""
}

func main() {
var languagesFlag languages
flag.Var(&languagesFlag, "languages", "A comma separated list of languages to practice.")
flag.Var(&languagesFlag, "l", "A comma separated list of languages to practice.")
flag.Parse()

checkConfig()
algos, languages := getConfig()
if len(languagesFlag) > 0 {
languages = languagesFlag
num_ti := textinput.New()
num_ti.Focus()
num_ti.CharLimit = 2
num_ti.Width = 5
answer_ti := textinput.New()
answer_ti.Focus()
answer_ti.Width = 50

initialModel := model{
state: CHECK_CONFIG,
cursor: 0,
num_ti: num_ti,
answer_ti: answer_ti,
}
p := tea.NewProgram(initialModel)
if _, err := p.Run(); err != nil {
fmt.Println("Error starting the program", err)
}
welcomMessage(algos, languages)
num := askForNumber()
s := Session{Algorithms: algos, Languages: languages, Num: num}
s = checkPracticeNumer(s)
fmt.Println("Starting session with", s.Num, "questions.")
results := runTheSession(s)

// Print out the results
printResults(results)

}
Loading

0 comments on commit 5caef02

Please sign in to comment.