Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wpm #22

Merged
merged 2 commits into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,26 @@ impl<'a> Toipe {
// stop the timer
let ended_at = Instant::now();

let (final_chars_typed_correctly, final_uncorrected_errors) =
input.iter().zip(original_text.iter()).fold(
(0, 0),
|(total_chars_typed_correctly, total_uncorrected_errors),
(typed_char, orig_char)| {
if typed_char == orig_char {
(total_chars_typed_correctly + 1, total_uncorrected_errors)
} else {
(total_chars_typed_correctly, total_uncorrected_errors + 1)
}
},
);

let results = ToipeResults {
num_words: self.words.len(),
num_chars_typed,
num_chars_text: input.len(),
num_errors,
total_words: self.words.len(),
total_chars_typed: num_chars_typed,
total_chars_in_text: input.len(),
total_char_errors: num_errors,
final_chars_typed_correctly,
final_uncorrected_errors,
started_at,
ended_at,
};
Expand Down Expand Up @@ -267,12 +282,12 @@ impl<'a> Toipe {
],
&[Text::from(format!(
"Mistakes: {} out of {} characters",
results.num_errors, results.num_chars_text
results.total_char_errors, results.total_chars_in_text
))],
&[Text::from(format!(
"Took {}s for {} words",
results.duration().as_secs(),
self.config.num_words,
results.total_words,
))],
&[
Text::from("Speed: "),
Expand All @@ -281,11 +296,10 @@ impl<'a> Toipe {
],
])?;
self.tui.display_lines_bottom(&[&[
Text::from("Press "),
Text::from("r").with_color(color::Blue),
Text::from(" to restart, "),
Text::from("q").with_color(color::Blue),
Text::from(" to quit."),
Text::from("ctrl-r").with_color(color::Blue),
Text::from(" to restart, ").with_faint(),
Text::from("ctrl-c").with_color(color::Blue),
Text::from(" to quit ").with_faint(),
]])?;
// no cursor on results page
self.tui.hide_cursor()?;
Expand All @@ -296,9 +310,9 @@ impl<'a> Toipe {
while to_restart.is_none() {
match keys.next().unwrap()? {
// press 'r' to restart
Key::Char('r') => to_restart = Some(true),
Key::Ctrl('r') => to_restart = Some(true),
// press 'q' to quit
Key::Char('q') => to_restart = Some(false),
Key::Ctrl('c') => to_restart = Some(false),
_ => {}
}
}
Expand Down
46 changes: 25 additions & 21 deletions src/results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ use std::time::{Duration, Instant};
/// Stores stats from a typing test.
#[derive(Clone)]
pub struct ToipeResults {
pub num_words: usize,
pub num_chars_typed: usize,
pub num_chars_text: usize,
pub num_errors: usize,
/// number of words in given text
pub total_words: usize,
/// number of chars typed including those typed before being cleared
/// (by backspace or ctrl-w)
pub total_chars_typed: usize,
/// number of chars in given text
pub total_chars_in_text: usize,
/// number of wrongly typed characters including those that were cleared
/// (by backspace or ctrl-w)
pub total_char_errors: usize,
/// number of chars in given text that were correctly typed at the end of the test
pub final_chars_typed_correctly: usize,
/// number of chars in given text that were wrongly typed at the end of the test
pub final_uncorrected_errors: usize,
pub started_at: Instant,
pub ended_at: Instant,
}

impl ToipeResults {
/// Number of correctly typed letters
pub fn num_correct_chars(&self) -> usize {
self.num_chars_typed - self.num_errors
}

/// Duration of the test.
///
/// i.e., the time between the user pressing the first key and them
Expand All @@ -28,23 +33,22 @@ impl ToipeResults {

/// Percentage of letters that were typed correctly.
pub fn accuracy(&self) -> f64 {
self.num_correct_chars() as f64 / self.num_chars_typed as f64
}

/// Speed in (correctly typed) characters per minute.
pub fn cpm(&self) -> f64 {
self.num_correct_chars() as f64 / (self.duration().as_secs_f64() / 60.0)
(self.total_chars_typed as isize - self.total_char_errors as isize) as f64
/ self.total_chars_typed as f64
}

/// Speed in (correctly typed) words per minute.
///
/// Measured as `cpm / (chars per word)` where `chars per word` is
/// measured as `(number of chars) / (number of words)`.
/// Measured as (number of correctly typed chars / 5 - number of uncorrected errors) / minute
///
/// A "word" is considered to be 5 chars because:
/// - chars/letters are typed, not whole words
/// - a sentence with small words won't be disproportionately favoured
///
/// Note: this is only an approximation because "correctly typed
/// words" is ambiguous when there could be a mistake in only one or
/// two letters of a word.
/// Uncorrected errors are penalized to encourage correcting errors.
pub fn wpm(&self) -> f64 {
self.cpm() / (self.num_chars_text as f64 / self.num_words as f64)
(self.final_chars_typed_correctly as f64 / 5.0 - self.final_uncorrected_errors as f64)
.max(0.0) as f64
/ (self.duration().as_secs_f64() / 60.0)
}
}