Skip to content

Commit

Permalink
Before 0.2.0 alfa (#35)
Browse files Browse the repository at this point in the history
* Make list of TODO

* mod vendor

* Reset mistyped falg after race finish

* Fix: disable editor on startup

* add fixme comment

* wip readme

* update readme

* update notes.md

* Fix creating required files on first startup

* Update readme how to install

* Minor readme fix

* mod tidy, vendor

* Minor changes to main pkg

* Simplify file paths

* Minor changes to data pkg

* Minor changes to utils, views packages

* Add new demo.gif asset
  • Loading branch information
jan25 authored Dec 31, 2019
1 parent 04d5088 commit 69c813b
Show file tree
Hide file tree
Showing 64 changed files with 10,757 additions and 3,382 deletions.
55 changes: 23 additions & 32 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@

## Basic features

- Goal
- Given random paragraph, type as fast as possible
- A Race
- Given random paragraph, type it as fast as possible
- Improved typing skills can be infered from WPM, Accuracy metrics of each race

- Functions:
- On app startup it shows the race history
- Start new race
- Ctrl+s starts new race if ui is in stats mode
- Start a countdown in word view before race begins
- Ctrl+s starts new race if ui is in history mode(when stats view is active)
- Start a countdown in word view before race begins(TODO nice to have)
- When race begins, view is focused to word view to allow typing words
- There is a default maximum time a race can be in progress (2:30), after this time race automatically expires/ends
- There is a default maximum time a race can be in progress (2:30), after this time race automatically expires/ends(TODO nice to have)
- End race
- Ctrl+e ends a current race and switches ui to stats mode
- Race auto ends when all words are finished, and ui moves to stats mode
- Only if race was finished by typing all the words, will the stats for the race get added to stats view
- Ctrl+e ends a current race and switches ui to history mode
- Race auto ends when all words are finished, and ui moves to history mode
- Only if race was finished by typing all the words, will the stats for the race get added to history list
- Stats mode
- When no race is in progress, the stats view will show stats from recent races
- Stats are ordered in recent first order
- Scroll through historical stats
- When no race is in progress, the stats view will show history from past races
- Hisotry is ordered in recent first order
- We can scroll through historical stats
- We keep track of Words per minute and Accuracy for a race
- Controls view
- Displays help text for ui controls
- We can collapse or expand this view
- We can collapse or expand this view(TODO nice to have)
- Exit game
- Ctrl+c exits from racer ui to the command line
- Asks for confirmation Y/N before exiting
- Asks for confirmation Y/N before exiting(TODO nice to have)

```
+-------------------------------------------------------------------------+
Expand All @@ -47,43 +48,33 @@
```

- UI
- UI will always have 3 Views/Panes- paragraph, word, stats and controls
- UI will always have 4 Views/Panes- paragraph, word, stats and controls
- Paragraph View
- Shows a paragraph to type if race is in progress
- Highlights next word to type during a race
- White bg to indicate next word to type
- Red bg for the word indicates mistyping in word view
- Grey out this view if no race is in progress
- Grey out this view if no race is in progress(TODO nice to have)
- Word View
- This is a input box to type next target word in the given paragraph
- Greyed out if no race is in progress
- Greyed out if no race is in progress(TODO nice to have)
- Red bg for typed word indicates mistyped, otherwise default bg is applied
- Greyed out if no race in progress
- Show countdown before a race begins (1..2..3.. GO!)
- Show countdown before a race begins (1..2..3.. GO!) (TODO nice to have)
- Stats View
- This view shows the historical race stats in default mode
- This view shows the historical race stats in default mode(on app startup)
- Shows stats in recent first order. And we will be able to scroll through historical stats
- Highlights a stat row to show we can scroll
- In race mode, this view shows auto updating stat(WPM, Accuracy) for race in progress. In addition also shows a timer that counts down from 2:30
- In race mode, this view shows auto updating stat(WPM, Accuracy) for race in progress. In addition also shows a timer that counts down from 2:30 (TODO nice to have. timer could count upward until 2:30 mins)
- Controls View
- Used to show help text that displays the ui controls
- Ctrl+x can expand/collapse this view

- Data directory
- All data related to termracer is put under $HOME/termracer
- This directory contains
- samples
- log file
- racehistory file

TODO
- Known issues
- Only one paragraph support on master build
- Need to get rid of server and generate paragraphs on the fly

- need to arrange words properly in para view. algorithm to adjust spacing? just look for library
- How about a dashboard to submit book urls to generate samples?

- samples.json
- debug log file
- race history csv file


> Credits for the above ASCII art to https://github.com/astashov/tixi
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
# termracer

Practise your typing skills from within your terminal. termracer is inspired by various online typing tutor websites.

Your goal is to type a given paragraph as fast and accurate as possible, termracer will calculate your typing speed with words per minute and accuracy % metrics. You can also view your progress by viewing the past race results.
The Goal is to type a given paragraph as fast and accurate as possible, termracer will calculate your typing speed with words per minute and accuracy % metrics. You can also view your progress by viewing the past race results.

For each race, You'll be presented a paragraph randomly picked from a predefined pool of paragraphs.

![](https://github.com/jan25/termracer/blob/master/assets/example.gif)
![](https://github.com/jan25/termracer/blob/master/assets/demo.gif)

## Install

```
# download and install
$ go get -u github.com/jan25/termracer
# Download and install latest release
# You could use -o flag to put the termracer binary anywhere that is included in $PATH
$ go build -o $GOPATH/bin/termracer github.com/jan25/termracer/cmd
# run application
# Run application
# if $GOPATH/bin is in $PATH
$ termracer
# OR
$ $GOPATH/bin/termracer
$ termracer
# OR use
$ $GOPATH/bin/termracer
```

> Current version of termracer can't generate paragraphs and pick an interesting paragraph for a race. We only have one default paragraph that is used for all races. In upcoming versions, termracer will be able to choose a random and interesting paragraphs for you.
## Development

This application uses go modules. So, you could clone this repo under any
directory and build/test/run. As a helper we have Makefile in this repo, which will allow to build/test/run with single
command.
```
# Builds executable
$ make build

# Runs available tests
```
# Run available tests
$ make test
# Builds and runs executable
# Build and Run executable
$ make run
# Run in debug mode
# Build and Run in debug mode
$ make debug
```
## Note: The current master is in one single main package with global shared variables. So in-order to seperate concerns the project is in rewrite period, so at end of it we'll have components seperated into nicer modules/packages. Expect the finished rewrite by milestone 0.2.0-alpha scheduled on 01-12-2019 CET(May be delayed)
# Builds executable
$ make build
```

The design/features are written in [NOTES.md](https://github.com/jan25/termracer/blob/master/NOTES.md).
The design/features are written in [NOTES.md](https://github.com/jan25/termracer/blob/master/NOTES.md).
Binary file added assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/example.gif
Binary file not shown.
37 changes: 17 additions & 20 deletions cmd/appdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@ package main

import (
"log"

"github.com/jan25/gocui"
"github.com/jan25/termracer/config"
viewdata "github.com/jan25/termracer/views/data"
"github.com/jan25/termracer/views"
viewdata "github.com/jan25/termracer/views/data"
)

// View names
const (
statsName = "stats"
paraName = "para"
wordName = "word"
controlsName = "controls"
)

// View dimensions to setup the app UI
var (
paraW, paraH = 60, 8
wordW, wordH = 60, 2

statsW, statsH = 20, 6
controlsW, controlsH = 20, 4

// Offsets and padding
topX, topY = 1, 1
pad = 1
)
Expand All @@ -42,26 +44,22 @@ type AppData struct {

// initializeAppData creates views and initialises AppData
func initializeAppData(g *gocui.Gui) (*AppData, error) {
ad := &AppData{}

para := views.NewParagraphView(paraName, topX, topY, paraW, paraH)
ad.paragraph = para.Data

word := views.NewWordView(wordName, topX, topY+paraH+pad, wordW, wordH)
word.Data = para.Data // This Data shared between editor and paragraph views

word := views.NewWordView(wordName, topX, topY+paraH+pad, wordW, wordH, para.Data)
stats, err := views.NewStatsView(statsName, topX+paraW+pad, topY, statsW, statsH)
if err != nil {
return nil, err
}
ad.stats = stats.LiveRaceData
ad.history = stats.HistoryData

controls := views.NewControls(controlsName, topX+paraW+pad, topY+statsH+pad, controlsW, controlsH)
ad.controls = controls.Data

g.SetManager(para, word, stats, controls)

ad := &AppData{
paragraph: para.Data,
stats: stats.LiveRaceData,
history: stats.HistoryData,
controls: controls.Data,
}
return ad, nil
}

Expand All @@ -73,10 +71,10 @@ func (ad *AppData) OnRaceStart(g *gocui.Gui) error {
ad.stats.SetChannels(paraToStats, ad.updateUICh, ad.finishCh)
ad.paragraph.SetChannels(paraToStats, ad.updateUICh)

ad.stats.IsActive = !ad.stats.IsActive
ad.history.IsActive = !ad.history.IsActive
ad.stats.IsActive = true
ad.history.IsActive = false

ad.paragraph.StartRace(g, wordName)
ad.paragraph.StartRace(g)
if err := ad.stats.StartRace(); err != nil {
return err
}
Expand Down Expand Up @@ -104,7 +102,6 @@ func (ad *AppData) OnRaceStart(g *gocui.Gui) error {
// - when typing is finished
// - when user stops the race
func (ad *AppData) OnRaceFinish() error {
config.Logger.Info("OnRaceFinish()")
close(ad.finishCh)

if err := ad.paragraph.FinishRace(); err != nil {
Expand All @@ -115,8 +112,8 @@ func (ad *AppData) OnRaceFinish() error {
}
ad.controls.DefaultControls()

ad.stats.IsActive = !ad.stats.IsActive
ad.history.IsActive = !ad.history.IsActive
ad.stats.IsActive = false
ad.history.IsActive = true

return nil
}
Expand Down
12 changes: 6 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import (

"github.com/jan25/gocui"
"github.com/jan25/termracer/config"
db "github.com/jan25/termracer/data"
"github.com/jan25/termracer/data"
)

func main() {
// Flags
debug := flag.Bool("debug", false, "flag for debug mode")
flag.Parse()

// Ensure the required data files on local FS are present
if err := data.EnsureDataDirs(); err != nil {
log.Panicln(err)
}

// Setup logger
var err error
_, err = config.InitLogger(*debug)
Expand All @@ -22,11 +27,6 @@ func main() {
}
defer config.Logger.Sync()

// Ensure the required data files on local FS are present
if err := db.EnsureDataDirs(); err != nil {
log.Panicln(err)
}

gui, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
log.Panicln(err)
Expand Down
29 changes: 8 additions & 21 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ const (
// TopLevelDir is name of directory that stores data for termracer
TopLevelDir = "/termracer"

// SamplesDir is a sub directory to store paragraph samples data
SamplesDir = "/samples/use"

// SamplesJSONPath is a file storing sample paragraphs in JSON format
SamplesJSONPath = SamplesDir + "/samples.json"
SamplesJSONPath = "/samples.json"

// HistoryFile is name of file to store race history data
HistoryFile = "/racehistory.csv"
Expand All @@ -23,41 +20,31 @@ const (
MaxWordLen = 25
)

// GetSamplesUseDir returns path to samples/use
// directory in local filesystem
func GetSamplesUseDir() (string, error) {
// GetTopLevelDir returns full path to top level dir
func GetTopLevelDir() (string, error) {
home, err := utils.GetHomeDir()
if err != nil {
return "", err
}
return home + TopLevelDir + SamplesDir, nil
return home + TopLevelDir, nil
}

// GetSamplesFilePath returns absolute path to samples.json file
func GetSamplesFilePath() (string, error) {
home, err := utils.GetHomeDir()
tld, err := GetTopLevelDir()
if err != nil {
return "", err
}
return home + TopLevelDir + SamplesJSONPath, nil
return tld + SamplesJSONPath, nil
}

// GetHistoryFilePath returns path to race history file
func GetHistoryFilePath() (string, error) {
home, err := utils.GetHomeDir()
if err != nil {
return "", err
}
return home + TopLevelDir + HistoryFile, nil
}

// GetTopLevelDir returns full path to top level dir
func GetTopLevelDir() (string, error) {
home, err := utils.GetHomeDir()
tld, err := GetTopLevelDir()
if err != nil {
return "", err
}
return home + TopLevelDir, nil
return tld + HistoryFile, nil
}

// GetLogsFilePath is absolute path to log file
Expand Down
5 changes: 2 additions & 3 deletions config/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ func InitLogger(debug bool) (*zap.Logger, error) {
if err != nil {
return nil, err
}

cfg := zap.NewProductionConfig()
if err := utils.CreateFileIfNotExists(fpath); err != nil {
if err = utils.CreateFileIfNotExists(fpath); err != nil {
return nil, err
}

cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{fpath}
Logger, _ = cfg.Build()
return Logger, nil
Expand Down
Loading

0 comments on commit 69c813b

Please sign in to comment.