diff options
| author | Charles <sircharlesaze@gmail.com> | 2020-06-11 15:04:32 +0200 |
|---|---|---|
| committer | Charles <sircharlesaze@gmail.com> | 2020-06-11 17:49:25 +0200 |
| commit | 86044397b8fac9298295cc22b40413d8f37025e3 (patch) | |
| tree | 13ea1366b2bcc564fb053d3998c458427058a8e0 /src | |
| parent | 6e47e69d45d30481e98c4fba5542c195b0fb830e (diff) | |
| download | rutikmer-86044397b8fac9298295cc22b40413d8f37025e3.tar.gz rutikmer-86044397b8fac9298295cc22b40413d8f37025e3.tar.bz2 rutikmer-86044397b8fac9298295cc22b40413d8f37025e3.zip | |
Added history with csv file parsing and writing
Diffstat (limited to 'src')
| -rw-r--r-- | src/history.rs | 77 | ||||
| -rw-r--r-- | src/main.rs | 32 | ||||
| -rw-r--r-- | src/scramble.rs | 133 | ||||
| -rw-r--r-- | src/shuffle.rs | 102 | ||||
| -rw-r--r-- | src/text.rs | 51 |
5 files changed, 269 insertions, 126 deletions
diff --git a/src/history.rs b/src/history.rs new file mode 100644 index 0000000..6b02b85 --- /dev/null +++ b/src/history.rs @@ -0,0 +1,77 @@ +use std::time::Duration; +use std::str; +use std::num; +use std::fmt; + +use csv; +use chrono; +use chrono::prelude::*; + +use super::scramble::Scramble; + + +pub struct SolveTime(Duration); + +impl str::FromStr for SolveTime { + type Err = num::ParseIntError; + // fmt: mm:ss.lll (l = millisecond) + fn from_str(s: &str) -> Result<Self, Self::Err> { + let minute: u64 = s[..2].parse()?; + let second: u64 = s[3..5].parse()?; + let millis: u64 = s[6..].parse()?; + Ok(SolveTime(Duration::from_secs(minute * 60_u64 + second) + Duration::from_millis(millis))) + } +} + +impl fmt::Display for SolveTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:0>2}:{:0>2}.{:0>3}", + self.0.as_secs() / 60, + self.0.as_secs() % 60, + self.0.as_millis() % 1000) + } +} + +struct Entry { + time: SolveTime, + scramble: Scramble, + date: chrono::DateTime<Utc>, +} + +pub struct History(Vec<Entry>); + +const VEC_START_SIZE: usize = 200; + +impl History { + pub fn from_csv(file_path: &str) -> History { + let mut history = History(Vec::with_capacity(VEC_START_SIZE)); + let mut reader = csv::Reader::from_path(file_path).unwrap(); + for result in reader.records() { + if let Ok(record) = result { + history.0.push(Entry{ + time: record[0].parse::<SolveTime>().unwrap(), + scramble: record[1].parse::<Scramble>().unwrap(), + date: record[2].parse::<chrono::DateTime<Utc>>().unwrap() + }) + } + } + history + } + + pub fn save_csv(&self, file_path: &str) { + let mut writter = csv::Writer::from_path(file_path).unwrap(); + writter.write_record(&["time", "scramble", "date"]).unwrap(); + for entry in &self.0 { + writter.write_record(&[ + entry.time.to_string(), + entry.scramble.to_string(), + entry.date.to_string() + ]).unwrap(); + } + writter.flush().unwrap(); + } + + pub fn summary(&self, n: usize) -> Vec<String> { + self.0.iter().skip(self.0.len() - n).map(|Entry{time, ..}| time.to_string()).collect() + } +} diff --git a/src/main.rs b/src/main.rs index 26b5468..e106cb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,21 @@ use sdl2::pixels::Color; use sdl2::rect::Rect; pub mod time; -pub mod shuffle; +pub mod scramble; pub mod text; +pub mod history; + +use scramble::Scramble; +use history::History; const WIDTH: u32 = 640; const HEIGHT: u32 = 480; const TITLE: &str = "rutikmer"; +const FONT_SIZE: u32 = 40; + +const GREEN: Color = Color::RGB(0x1B, 0x5E, 0x20); +const ORANGE: Color = Color::RGB(0xEF, 0x6C, 0x00); +const BLACK: Color = Color::RGB(0x00, 0x00, 0x00); fn main() { let sdl = sdl2::init().unwrap(); @@ -21,7 +30,8 @@ fn main() { .resizable() .build() .unwrap(); - let font = ttf.load_font("font/FiraMono-Regular.ttf", 40).unwrap(); + let font = ttf.load_font("font/FiraMono-Regular.ttf", FONT_SIZE as u16).unwrap(); + let hist = History::from_csv("history.csv"); let mut canvas = window.into_canvas().build().unwrap(); let mut event_pump = sdl.event_pump().unwrap(); @@ -29,9 +39,9 @@ fn main() { let mut timer = time::Timer::new(); let timer_rect = Rect::new(10, 10, 100, 40); - let mut text_factory = text::Factory::new(&font, &tex_creator, Color::RGB(0, 0, 0)); + let mut text_factory = text::Factory::new(&font, &tex_creator, BLACK, FONT_SIZE); - let mut shuffle_str = shuffle::Move::string_sequence(10); + let mut scramble_str = Scramble::new_rand(10).to_string(); 'running: loop { for e in event_pump.poll_iter() { @@ -43,7 +53,7 @@ fn main() { time::State::Active => timer.stop(), time::State::Inactive => { timer.idle(); - shuffle_str = shuffle::Move::string_sequence(10); + scramble_str = Scramble::new_rand(10).to_string(); }, } } @@ -59,9 +69,9 @@ fn main() { } } let bg_color = match timer.state { - time::State::Idle => Color::RGB(100, 100, 0), - time::State::Active => Color::RGB(0, 100, 0), - time::State::Inactive => Color::RGB(0, 0, 0), + time::State::Idle => ORANGE, + time::State::Active => GREEN, + time::State::Inactive => BLACK, }; canvas.set_draw_color(bg_color); text_factory.set_bg(bg_color); @@ -72,7 +82,11 @@ fn main() { } if timer.state == time::State::Inactive { - canvas.copy(&text_factory.from_string(&shuffle_str), None, Rect::new(10, 100, 500, 40)).unwrap(); + canvas.copy(&text_factory.from_string(&scramble_str), None, Rect::new(10, 100, 500, 40)).unwrap(); + let sum = hist.summary(3); + for (i, s) in sum.iter().enumerate() { + canvas.copy(&text_factory.from_string(&s), None, Rect::new(10, 200 + 40 * (i as i32), 200, 30)).unwrap(); + } } canvas.present(); diff --git a/src/scramble.rs b/src/scramble.rs new file mode 100644 index 0000000..abee6a6 --- /dev/null +++ b/src/scramble.rs @@ -0,0 +1,133 @@ +use std::fmt; +use std::str; + +use rand::{ + distributions::{Distribution, Standard}, + Rng +}; + +pub struct Scramble(Vec<Move>); + +impl Scramble { + pub fn new_rand(n: usize) -> Scramble { + let mut sequence: Vec<Move> = Vec::with_capacity(n); + + while sequence.len() != n { + let direction = rand::random::<Direction>(); + let modifier = rand::random::<Modifier>(); + + if let Some(l) = sequence.last() { + if l.direction == direction { + continue; + } + } + sequence.push(Move { direction, modifier }); + } + Scramble(sequence) + } +} + +impl fmt::Display for Scramble { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0.iter().map(|m| m.to_string()).collect::<Vec<String>>().join(" ")) + } +} + +impl str::FromStr for Scramble { + type Err = &'static str; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let strs = s.split(" "); + let mut scramble = Scramble(Vec::new()); + for s in strs { + scramble.0.push(s.parse()?); + } + Ok(scramble) + } +} + +impl str::FromStr for Move { + type Err = &'static str; + fn from_str(s: &str) -> Result<Self, Self::Err> { + use Direction::*; + use Modifier::*; + + let mut cs = s.chars(); + let direction = match cs.next() { + Some('F') => Front, + Some('B') => Back, + Some('D') => Down, + Some('U') => Up, + Some('R') => Right, + Some('L') => Left, + Some(_) => return Err("Move direction isn't valid"), + None => return Err("Move format is empty"), + }; + let modifier = match cs.next() { + Some('\'') => Prime, + Some('2') => Twice, + Some(_) => return Err("Move modifier isn't valid"), + None => No, + }; + if let Some(_) = cs.next() { + return Err("Unexpected character in move"); + } + Ok(Move{ direction, modifier }) + } +} + +#[derive(PartialEq)] +enum Direction { Front, Back, Down, Up, Right, Left, } + +enum Modifier { No, Twice, Prime, } + +struct Move { + direction: Direction, + modifier: Modifier, +} + +// https://stackoverflow.com/questions/48490049 +impl Distribution<Direction> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Direction { + use Direction::*; + match rng.gen_range(0, 6) { + 0 => Front, + 1 => Back, + 2 => Down, + 3 => Up, + 4 => Right, + _ => Left, + } + } +} + +impl Distribution<Modifier> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Modifier { + use Modifier::*; + match rng.gen_range(0, 3) { + 0 => No, + 1 => Twice, + _ => Prime, + } + } +} + +impl fmt::Display for Move { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Direction::*; + use Modifier::*; + let letter = match self.direction { + Front => "F", + Back => "B", + Down => "D", + Up => "U", + Right => "R", + Left => "L", + }; + let modifier = match self.modifier { + No => "", + Twice => "2", + Prime => "'", + }; + write!(f, "{}{}", letter, modifier) + } +} diff --git a/src/shuffle.rs b/src/shuffle.rs deleted file mode 100644 index 7f8f88f..0000000 --- a/src/shuffle.rs +++ /dev/null @@ -1,102 +0,0 @@ -use rand::{ - distributions::{Distribution, Standard}, - Rng -}; - -#[derive(PartialEq)] -enum MoveDirection { - Front, - Back, - Down, - Up, - Right, - Left, -} - -enum MoveModifier { - None, - Twice, - Prime, -} - -pub struct Move { - direction: MoveDirection, - modifier: MoveModifier, -} - - -// https://stackoverflow.com/questions/48490049 -impl Distribution<MoveDirection> for Standard { - fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> MoveDirection { - use MoveDirection::*; - - match rng.gen_range(0, 6) { - 0 => Front, - 1 => Back, - 2 => Down, - 3 => Up, - 4 => Right, - _ => Left, - } - } -} - -impl Distribution<MoveModifier> for Standard { - fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> MoveModifier { - use MoveModifier::*; - - match rng.gen_range(0, 3) { - 0 => None, - 1 => Twice, - _ => Prime, - } - } -} - -impl Move { - pub fn sequence(n: usize) -> Vec<Move> { - let mut sequence: Vec<Move> = Vec::with_capacity(n); - - while sequence.len() != n { - let direction = rand::random::<MoveDirection>(); - let modifier = rand::random::<MoveModifier>(); - - if let Some(l) = sequence.last() { - if l.direction == direction { - continue; - } - } - sequence.push(Move { direction, modifier }); - } - sequence - } - - pub fn string_sequence(n: usize) -> String { - let seq = Move::sequence(n); - seq.iter().map(|m| m.to_string() + " ").collect::<Vec<String>>().join(" ") - } -} - -use std::fmt; - -impl fmt::Display for Move { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use MoveDirection::*; - use MoveModifier::*; - - let letter = match self.direction { - Front => "F", - Back => "B", - Down => "D", - Up => "U", - Right => "R", - Left => "L", - }; - let modifier = match self.modifier { - None => "", - Twice => "2", - Prime => "'", - }; - write!(f, "{}{}", letter, modifier) - } -} diff --git a/src/text.rs b/src/text.rs index a27ca49..e62db3d 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,6 +1,7 @@ use sdl2::ttf; -use sdl2::render::{Texture, TextureCreator}; +use sdl2::render::{Canvas, Texture, TextureCreator}; use sdl2::pixels::Color; +use sdl2::rect::Rect; const WHITE: Color = Color::RGB(255, 255, 255); @@ -8,18 +9,20 @@ pub struct Factory<'a, T> { font: &'a ttf::Font<'a, 'a>, creator: &'a TextureCreator<T>, bg: Color, + font_size: u32, } impl<'a, T> Factory<'a, T> { pub fn new(font: &'a ttf::Font, - creator: &'a TextureCreator<T>, - bg: Color + creator: &'a TextureCreator<T>, + bg: Color, + font_size: u32 ) -> Factory<'a, T> { - Factory { font, creator, bg } + Factory { font, creator, bg, font_size } } - pub fn from_string(&self, s: &String) -> Texture + pub fn from_string(&self, s: &str) -> Texture { let surface = self.font.render(s).shaded(WHITE, self.bg).unwrap(); self.creator.create_texture_from_surface(&surface).unwrap() @@ -30,14 +33,32 @@ impl<'a, T> Factory<'a, T> { } } +struct Frame<'a> { + width: u32, + height: u32, + texture: Texture<'a>, +} + +impl<'a> Frame<'a> { + fn new<T>(s: &'a str, factory: &'a Factory<T>) -> Frame<'a> { + Frame{ + width: s.len() as u32 * factory.font_size, + height: factory.font_size, + texture: factory.from_string(s) + } + } -// pub trait TextTexture { -// fn to_texture<T>(&self, factory: &Factory<T>) -> Texture; -// } -// -// impl TextTexture for String { -// fn to_texture<T>(&self, factory: &Factory<T>) -> Texture { -// let surface = factory.font.render(self).shaded(WHITE, factory.bg).unwrap(); -// factory.creator.create_texture_from_surface(&surface).unwrap() -// } -// } + fn to_rect(&self, x: i32, y: i32) -> Rect { + Rect::new(x, y, self.width, self.height) + } +} + +trait Framable { + fn to_frame<'a>(&'a self) -> Frame<'a>; + + fn put_canvas<T: sdl2::render::RenderTarget>(&self, canvas: &mut Canvas<T>, x: i32, y: i32) { + let frame = self.to_frame(); + let rect = frame.to_rect(x, y); + canvas.copy(&frame.texture, None, rect); + } +} |
