Skip to content

Instantly share code, notes, and snippets.

@RyosukeCla
Created January 15, 2023 09:51
Show Gist options
  • Save RyosukeCla/38b9339798a829a1a10cffaaec004241 to your computer and use it in GitHub Desktop.
Save RyosukeCla/38b9339798a829a1a10cffaaec004241 to your computer and use it in GitHub Desktop.
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