Skip to content

Commit

Permalink
feat: scoring graph with avg score per second
Browse files Browse the repository at this point in the history
  • Loading branch information
SilenLoc committed May 1, 2023
1 parent e20c17e commit d5079b6
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/random/letters.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rand::Rng;
use rand::prelude::Distribution;
use rand::Rng;

pub fn random_letters_inner(max: u32) -> String {
let rng = rand::thread_rng();
Expand All @@ -26,4 +26,4 @@ impl Distribution<u8> for Alpha {
}
}
}
}
}
98 changes: 97 additions & 1 deletion src/scoring/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,114 @@
use egui::Ui;
use std::{
ops::Div,
time::{Duration, Instant},
};

use egui::{plot::Plot, Ui};

use crate::settings::TFSetting;
use egui::plot::{Line, PlotPoints};

#[derive(serde::Deserialize, serde::Serialize, Default)]
pub struct Score {
score: u128,
score_per_duration: ScorePerDuration,
score_plot_state: Vec<[f64; 2]>,
}

impl Score {
pub fn render_scoring(&mut self, ui: &mut Ui) {
ui.heading(format!("{}", self.score));
ui.horizontal_top(|ui| {
let score_points: PlotPoints = PlotPoints::from(self.score_plot_state.clone());

let line = Line::new(score_points);
Plot::new("my_plot")
.auto_bounds_x()
.auto_bounds_y()
.width(400.0)
.height(100.0)
.show(ui, |plot_ui| plot_ui.line(line));

ui.label(format!(
"elapsed seconds {}",
self.score_per_duration.elapsed.as_secs_f64()
));
ui.label(format!(
"average score per second {}",
self.score_per_duration.avg
));
});
}

pub fn started_to_type(&mut self) {
self.score_per_duration.ready()
}

pub fn set(&mut self) {
self.score_per_duration.set(self.score);
}

pub fn won(&mut self, settings: &TFSetting) {
self.score += (settings.level.out_size * settings.size) as u128;

self.score_plot_state.push([
self.score_plot_state.len() as f64,
self.score_per_duration.avg,
]);
}
}

#[derive(serde::Deserialize, serde::Serialize)]
pub struct ScorePerDuration {
elapsed: Duration,
#[serde(skip)]
start: Time,
state: f64,
history: Vec<f64>,
avg: f64,
}

impl Default for ScorePerDuration {
fn default() -> Self {
Self {
elapsed: Duration::from_millis(0),
start: Time(Instant::now()),
state: 0.0,
avg: 0.0,
history: vec![],
}
}
}

impl ScorePerDuration {
pub fn ready(&mut self) {
self.start = Time::now();
}

pub fn set(&mut self, score: u128) {
self.elapsed = self.start.0.elapsed();

self.state = (score as f64).div(self.elapsed.as_secs_f64());

self.history.push(self.state);
self.avg = average(&self.history);
}
}

struct Time(Instant);

impl Default for Time {
fn default() -> Self {
Time::now()
}
}

impl Time {
pub fn now() -> Self {
Self(Instant::now())
}
}

fn average(history: &Vec<f64>) -> f64 {
history.iter().sum::<f64>() / history.len() as f64
}
58 changes: 47 additions & 11 deletions src/typewriter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ use crate::{scoring::Score, settings::TFSetting};
pub struct TypeState {
input: String,
challenge: String,
#[serde(skip)]
state: State,
}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
enum State {
Started,
Typing,
Won,
Reset,
None,
}

impl Default for State {
fn default() -> Self {
Self::None
}
}

pub trait Challenge {
Expand All @@ -16,16 +32,7 @@ pub trait Challenge {
impl TypeState {
pub fn render(&mut self, ui: &mut Ui, score: &mut Score, settings: &mut TFSetting) {
if settings.level_changed() {
self.challenge = settings.provide_next_string().to_challenge();
self.input.clear();
}

//win condition
if self.input.eq(&self.challenge) {
self.challenge.clear();
self.input.clear();
self.challenge = settings.provide_next_string().to_challenge();
score.won(settings)
self.state = State::Reset
}

let challenge_text = RichText::new(self.challenge.to_string()).size(45.0);
Expand All @@ -35,12 +42,41 @@ impl TypeState {
ui.heading(input_text);

ui.separator();

ui.horizontal_top(|ui| {
ui.text_edit_multiline(&mut self.input);

if ui.button("new").clicked() {
self.state = State::Reset
}
});

match self.state {
State::Started => {
score.started_to_type();
self.state = State::Typing
}
State::Typing => {
//win condition
if self.input.eq(&self.challenge) && !self.input.is_empty() {
self.state = State::Won;
}
}
State::Won => {
self.state = State::Reset;
score.won(settings);
score.set();
}
State::Reset => {
self.challenge = settings.provide_next_string().to_challenge();
self.input.clear();
self.state = State::None
}
});
State::None => {
if !self.input.is_empty() {
self.state = State::Started
}
}
}
}
}

0 comments on commit d5079b6

Please sign in to comment.