Created
January 15, 2023 09:51
-
-
Save RyosukeCla/38b9339798a829a1a10cffaaec004241 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{self}; | |
use rand::Rng; | |
struct ReadOnly<T>(T); | |
impl<T> std::ops::Deref for ReadOnly<T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
&self.0 | |
} | |
} | |
#[derive(Debug, Copy, Clone, PartialEq)] | |
enum Interval { | |
PerfectUnison = 0, | |
MinorSecond, | |
MajorSecond, | |
MinorThird, | |
MajorThird, | |
PerfectFourth, | |
Tritone, | |
PerfectFifth, | |
MinorSixth, | |
MajorSixth, | |
MinorSeventh, | |
MajorSeventh, | |
PerfectOctave = 12, | |
Unknown = 999, | |
} | |
impl Interval { | |
fn from_str(s: &str) -> Interval { | |
match s { | |
"Perfect Unison" => Interval::PerfectUnison, | |
"PU" => Interval::PerfectUnison, | |
"minor 2nd" => Interval::MinorSecond, | |
"m2" => Interval::MinorSecond, | |
"Major 2nd" => Interval::MajorSecond, | |
"M2" => Interval::MajorSecond, | |
"minor 3rd" => Interval::MinorThird, | |
"m3" => Interval::MinorThird, | |
"Major 3rd" => Interval::MajorThird, | |
"M3" => Interval::MajorThird, | |
"Perfect 4th" => Interval::PerfectFourth, | |
"P4" => Interval::PerfectFourth, | |
"Tritone" => Interval::Tritone, | |
"T" => Interval::Tritone, | |
"Perfect 5th" => Interval::PerfectFifth, | |
"P5" => Interval::PerfectFifth, | |
"minor 6th" => Interval::MinorSixth, | |
"m6" => Interval::MinorSixth, | |
"Major 6th" => Interval::MajorSixth, | |
"M6" => Interval::MajorSixth, | |
"minor 7th" => Interval::MinorSeventh, | |
"m7" => Interval::MinorSeventh, | |
"Major 7th" => Interval::MajorSeventh, | |
"M7" => Interval::MajorSeventh, | |
"Perfect Octave" => Interval::PerfectOctave, | |
"PO" => Interval::PerfectOctave, | |
_ => Interval::Unknown, | |
} | |
} | |
fn between(from: Tone, to: Tone) -> Interval { | |
let mut diff = to as i32 - from as i32; | |
if diff < 0 { | |
diff += 12; | |
} | |
match diff { | |
0 => Interval::PerfectUnison, | |
1 => Interval::MinorSecond, | |
2 => Interval::MajorSecond, | |
3 => Interval::MinorThird, | |
4 => Interval::MajorThird, | |
5 => Interval::PerfectFourth, | |
6 => Interval::Tritone, | |
7 => Interval::PerfectFifth, | |
8 => Interval::MinorSixth, | |
9 => Interval::MajorSixth, | |
10 => Interval::MinorSeventh, | |
11 => Interval::MajorSeventh, | |
12 => Interval::PerfectOctave, | |
_ => Interval::Unknown, | |
} | |
} | |
} | |
impl std::fmt::Display for Interval { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
match self { | |
Interval::PerfectUnison => write!(f, "PU (Perfect Union)"), | |
Interval::MinorSecond => write!(f, "m2 (minor 2nd)"), | |
Interval::MajorSecond => write!(f, "M2 (Major 2nd)"), | |
Interval::MinorThird => write!(f, "m3 (minor 3rd)"), | |
Interval::MajorThird => write!(f, "M3 (Major 3rd)"), | |
Interval::PerfectFourth => write!(f, "P4 (Perfect 4th)"), | |
Interval::Tritone => write!(f, "T (Tritone)"), | |
Interval::PerfectFifth => write!(f, "P5 (Perfect 5th)"), | |
Interval::MinorSixth => write!(f, "m6 (minor 6th)"), | |
Interval::MajorSixth => write!(f, "M6 (Major 6th)"), | |
Interval::MinorSeventh => write!(f, "m7 (minor 7th)"), | |
Interval::MajorSeventh => write!(f, "M7 (Major 7th)"), | |
Interval::PerfectOctave => write!(f, "PO (Perfect Octave)"), | |
Interval::Unknown => write!(f, "Unknown"), | |
} | |
} | |
} | |
#[derive(Debug, Copy, Clone, PartialEq)] | |
enum Tone { | |
C = 0, | |
CSharp, | |
D, | |
DSharp, | |
E, | |
F, | |
FSharp, | |
G, | |
GSharp, | |
A, | |
ASharp, | |
B = 11, | |
Unknown = 999 | |
} | |
impl Tone { | |
pub fn from(i: i32) -> Tone { | |
match i % 12 { | |
0 => Tone::C, | |
1 => Tone::CSharp, | |
2 => Tone::D, | |
3 => Tone::DSharp, | |
4 => Tone::E, | |
5 => Tone::F, | |
6 => Tone::FSharp, | |
7 => Tone::G, | |
8 => Tone::GSharp, | |
9 => Tone::A, | |
10 => Tone::ASharp, | |
11 => Tone::B, | |
_ => Tone::Unknown, | |
} | |
} | |
pub fn gen() -> Tone { | |
let mut rng = rand::thread_rng(); | |
let i = rng.gen_range(0..12); | |
Tone::from(i) | |
} | |
} | |
impl std::fmt::Display for Tone { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
match self { | |
Tone::C => write!(f, "C"), | |
Tone::CSharp => write!(f, "C#"), | |
Tone::D => write!(f, "D"), | |
Tone::DSharp => write!(f, "D#"), | |
Tone::E => write!(f, "E"), | |
Tone::F => write!(f, "F"), | |
Tone::FSharp => write!(f, "F#"), | |
Tone::G => write!(f, "G"), | |
Tone::GSharp => write!(f, "G#"), | |
Tone::A => write!(f, "A"), | |
Tone::ASharp => write!(f, "A#"), | |
Tone::B => write!(f, "B"), | |
Tone::Unknown => write!(f, "Unknown"), | |
} | |
} | |
} | |
fn main() { | |
let mut count: i64 = 1; | |
let mut success_count: i64 = 0; | |
loop { | |
let from = Tone::gen(); | |
let to = Tone::gen(); | |
println!("Q{}: What is the interval from {} to {} ? (PU, m2, M2, m3, M3, P4, T, P5, m6, M6, m7, M7, Quit)", count, from, to); | |
let mut input = String::new(); | |
io::stdin().read_line(&mut input).expect("failed to readline"); | |
if input.trim() == "Quit" { | |
count -= 1; | |
println!("Success: {} / {} = {}%", success_count, count, success_count * 100 / count); | |
break; | |
} | |
let interval = Interval::from_str(str::trim(&input)); | |
let expected = Interval::between(from, to); | |
if interval == expected { | |
println!("Correct! The answer is {}", expected); | |
success_count += 1; | |
} else { | |
println!("Incorrect! The correct answer is {}", expected); | |
} | |
count += 1; | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use crate::Interval; | |
use crate::Tone; | |
#[test] | |
fn inteval_from_str() { | |
assert_eq!(Interval::from_str("PU"), Interval::PerfectUnison); | |
assert_eq!(Interval::from_str("m2"), Interval::MinorSecond); | |
assert_eq!(Interval::from_str("M2"), Interval::MajorSecond); | |
} | |
#[test] | |
fn interval_between() { | |
assert_eq!(Interval::between(Tone::C, Tone::C), Interval::PerfectUnison); | |
assert_eq!(Interval::between(Tone::C, Tone::CSharp), Interval::MinorSecond); | |
assert_eq!(Interval::between(Tone::C, Tone::D), Interval::MajorSecond); | |
assert_eq!(Interval::between(Tone::C, Tone::DSharp), Interval::MinorThird); | |
assert_eq!(Interval::between(Tone::C, Tone::E), Interval::MajorThird); | |
assert_eq!(Interval::between(Tone::C, Tone::F), Interval::PerfectFourth); | |
assert_eq!(Interval::between(Tone::C, Tone::FSharp), Interval::Tritone); | |
assert_eq!(Interval::between(Tone::C, Tone::G), Interval::PerfectFifth); | |
assert_eq!(Interval::between(Tone::C, Tone::GSharp), Interval::MinorSixth); | |
assert_eq!(Interval::between(Tone::C, Tone::A), Interval::MajorSixth); | |
assert_eq!(Interval::between(Tone::C, Tone::ASharp), Interval::MinorSeventh); | |
assert_eq!(Interval::between(Tone::C, Tone::B), Interval::MajorSeventh); | |
assert_eq!(Interval::between(Tone::B, Tone::A), Interval::MinorSeventh); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment