aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/history.rs77
-rw-r--r--src/main.rs32
-rw-r--r--src/scramble.rs133
-rw-r--r--src/shuffle.rs102
-rw-r--r--src/text.rs51
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);
+ }
+}