Skip to content

Commit 89e72d8

Browse files
authored
feat: rework accuracy (#1)
counting past mistakes even after correcting them to provide more accurate accuracy (thanks @fuh-Q)
2 parents 4c82541 + 126a742 commit 89e72d8

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

src/main.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,15 @@ fn run(
5757
// the paragraph isn't finished yet
5858
let mut info = vec![];
5959
if game.start_time.is_some() {
60+
let acc = game.accuracy();
61+
let acc_str = if acc.is_nan() {
62+
"ACC: --".to_string()
63+
} else {
64+
format!("ACC: {:.2}%", acc)
65+
};
66+
6067
info.push(format!("WPM: {:.2}", game.wpm()));
61-
info.push(format!("ACC: {:.2}%", game.accuracy()))
68+
info.push(acc_str);
6269
} else {
6370
info.push("WPM: --".to_string());
6471
info.push("ACC: --".to_string());

src/typing.rs

+25-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
pub mod typing {
2-
use rand_word;
32
use ratatui::{
43
style::{Color, Style},
54
text::Span,
65
};
7-
use std::time::SystemTime;
6+
use std::{collections::HashSet, time::SystemTime};
87

98
#[derive(Debug)]
109
pub struct TypingGame {
@@ -13,51 +12,69 @@ pub mod typing {
1312
pub start_time: Option<SystemTime>,
1413
pub end_time: Option<SystemTime>,
1514
pub word_count: usize,
15+
pub mistakes: u32,
16+
pub mistake_indices: HashSet<usize>,
1617
}
1718

1819
impl TypingGame {
1920
pub fn new(words: usize) -> Self {
2021
let mut goal = rand_word::new(words);
22+
let cap = goal.len();
23+
2124
while goal.contains("º") {
2225
goal = rand_word::new(words);
2326
}
27+
2428
TypingGame {
2529
goal,
2630
current: "".to_string(),
2731
start_time: None,
2832
end_time: None,
2933
word_count: words,
34+
mistakes: 0,
35+
mistake_indices: HashSet::with_capacity(cap),
3036
}
3137
}
3238

3339
pub fn reset(&mut self) {
3440
self.current = "".to_string();
3541
self.start_time = None;
42+
43+
self.mistake_indices.clear();
44+
self.mistakes = 0;
3645
}
3746

3847
pub fn new_goal(&mut self) {
3948
self.goal = TypingGame::new(self.word_count).goal;
4049
}
4150

42-
pub fn accuracy(&self) -> f64 {
51+
pub fn accuracy(&mut self) -> f32 {
4352
let mut correct_count = 0;
4453
let goal_chars = self.goal.chars().collect::<Vec<char>>();
4554

4655
for (i, ch) in self.current.char_indices() {
4756
if goal_chars[i] == ch {
4857
correct_count += 1;
58+
} else if !self.mistake_indices.contains(&i) {
59+
self.mistakes += 1;
60+
self.mistake_indices.insert(i);
4961
}
5062
}
51-
correct_count as f64 * 100f64 / self.current.len() as f64
63+
64+
let correct = u32::checked_sub(correct_count, self.mistakes).unwrap_or(0);
65+
66+
// average correctly typed characters + correct but PREVIOUSLY incorrect characters
67+
// then change it into a percentage
68+
((correct + correct_count) as f32 / 2f32 * 100f32) as f32 / self.current.len() as f32
5269
}
5370

54-
pub fn wpm(&self) -> f64 {
55-
self.current.split_whitespace().count() as f64
71+
pub fn wpm(&self) -> f32 {
72+
self.current.split_whitespace().count() as f32
5673
/ (SystemTime::now()
5774
.duration_since(self.start_time.unwrap())
5875
.unwrap()
59-
.as_secs_f64()
60-
/ 60f64)
76+
.as_secs_f32()
77+
/ 60f32)
6178
}
6279

6380
pub fn curr_spans(&self) -> Vec<Span> {

0 commit comments

Comments
 (0)