pub const GENOME_BITS: u32 = 40;
struct BitReader(u64);
impl BitReader {
fn take(&mut self, n: u32) -> u64 {
let v = self.0 & ((1 << n) - 1);
self.0 >>= n;
v
}
}
struct BitWriter {
value: u64,
pos: u32,
}
impl BitWriter {
fn put(&mut self, v: u64, n: u32) {
self.value |= (v & ((1 << n) - 1)) << self.pos;
self.pos += n;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Family {
Prism,
Antiprism,
Bipyramid,
Frustum,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Scheme {
Analogous,
Triadic,
SplitComplementary,
Square,
}
impl Scheme {
#[must_use]
pub fn offsets(self) -> (f64, f64) {
match self {
Scheme::Analogous => (30.0, -30.0),
Scheme::Triadic => (120.0, 240.0),
Scheme::SplitComplementary => (150.0, 210.0),
Scheme::Square => (90.0, 180.0),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Background {
Dark,
Light,
DarkTint,
LightTint,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum EdgeStyle {
None,
ThinDark,
ThinLight,
ThickDark,
}
pub const RING_SEGMENTS: u32 = 20;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct VisualGenes {
pub(crate) family: Family,
pub(crate) sides: u8,
pub(crate) yaw: u8,
pub(crate) pitch: u8,
pub(crate) stretch: u8,
pub(crate) hue: u8,
pub(crate) scheme: Scheme,
pub(crate) background: Background,
pub(crate) edge: EdgeStyle,
pub(crate) ring_bits: u16,
}
impl VisualGenes {
#[must_use]
pub fn decode(genome: u64) -> Self {
let mut r = BitReader(genome);
let family = match r.take(2) {
0 => Family::Prism,
1 => Family::Antiprism,
2 => Family::Bipyramid,
_ => Family::Frustum,
};
let sides = 3 + r.take(3) as u8;
let yaw = r.take(4) as u8;
let pitch = r.take(3) as u8;
let stretch = r.take(2) as u8;
let hue = r.take(5) as u8;
let scheme = match r.take(2) {
0 => Scheme::Analogous,
1 => Scheme::Triadic,
2 => Scheme::SplitComplementary,
_ => Scheme::Square,
};
let background = match r.take(2) {
0 => Background::Dark,
1 => Background::Light,
2 => Background::DarkTint,
_ => Background::LightTint,
};
let edge = match r.take(2) {
0 => EdgeStyle::None,
1 => EdgeStyle::ThinDark,
2 => EdgeStyle::ThinLight,
_ => EdgeStyle::ThickDark,
};
let ring_bits = r.take(15) as u16;
VisualGenes {
family,
sides,
yaw,
pitch,
stretch,
hue,
scheme,
background,
edge,
ring_bits,
}
}
#[must_use]
pub fn encode(&self) -> u64 {
let mut w = BitWriter { value: 0, pos: 0 };
w.put(self.family as u64, 2);
w.put(u64::from(self.sides) - 3, 3);
w.put(u64::from(self.yaw), 4);
w.put(u64::from(self.pitch), 3);
w.put(u64::from(self.stretch), 2);
w.put(u64::from(self.hue), 5);
w.put(self.scheme as u64, 2);
w.put(self.background as u64, 2);
w.put(self.edge as u64, 2);
w.put(u64::from(self.ring_bits), 15);
debug_assert_eq!(w.pos, GENOME_BITS);
w.value
}
#[must_use]
pub fn family(&self) -> Family {
self.family
}
#[must_use]
pub fn sides(&self) -> u8 {
self.sides
}
#[must_use]
pub fn yaw(&self) -> u8 {
self.yaw
}
#[must_use]
pub fn pitch(&self) -> u8 {
self.pitch
}
#[must_use]
pub fn stretch(&self) -> u8 {
self.stretch
}
#[must_use]
pub fn hue(&self) -> u8 {
self.hue
}
#[must_use]
pub fn scheme(&self) -> Scheme {
self.scheme
}
#[must_use]
pub fn background(&self) -> Background {
self.background
}
#[must_use]
pub fn edge(&self) -> EdgeStyle {
self.edge
}
#[must_use]
pub fn ring_bits(&self) -> u16 {
self.ring_bits
}
#[must_use]
pub fn ring_pattern(&self) -> u32 {
u32::from(self.ring_bits)
| u32::from(self.pitch) << 15
| u32::from(self.stretch) << 18
}
}
pub(crate) const RHYTHMS: [[u8; 8]; 8] = [
[2, 2, 2, 2, 2, 2, 2, 2],
[3, 1, 3, 1, 3, 1, 2, 2],
[1, 1, 2, 1, 1, 2, 4, 4],
[4, 2, 2, 4, 1, 1, 1, 1],
[2, 1, 1, 2, 2, 1, 1, 6],
[1, 3, 1, 3, 1, 3, 2, 2],
[3, 3, 2, 2, 2, 1, 1, 2],
[1, 1, 1, 1, 4, 4, 2, 2],
];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SoundGenes {
pub(crate) root: u8,
pub(crate) tempo: u8,
pub(crate) timbre: u8,
pub(crate) rhythm: u8,
pub(crate) notes: [u8; 7],
}
impl SoundGenes {
#[must_use]
pub fn decode(genome: u64) -> Self {
let mut r = BitReader(genome);
let root = r.take(4) as u8;
let tempo = r.take(2) as u8;
let timbre = r.take(3) as u8;
let rhythm = r.take(3) as u8;
let mut notes = [0u8; 7];
for n in &mut notes {
*n = r.take(4) as u8;
}
SoundGenes { root, tempo, timbre, rhythm, notes }
}
#[must_use]
pub fn encode(&self) -> u64 {
let mut w = BitWriter { value: 0, pos: 0 };
w.put(u64::from(self.root), 4);
w.put(u64::from(self.tempo), 2);
w.put(u64::from(self.timbre), 3);
w.put(u64::from(self.rhythm), 3);
for n in self.notes {
w.put(u64::from(n), 4);
}
debug_assert_eq!(w.pos, GENOME_BITS);
w.value
}
#[must_use]
pub fn root(&self) -> u8 {
self.root
}
#[must_use]
pub fn tempo(&self) -> u8 {
self.tempo
}
#[must_use]
pub fn timbre(&self) -> u8 {
self.timbre
}
#[must_use]
pub fn rhythm(&self) -> u8 {
self.rhythm
}
#[must_use]
pub fn notes(&self) -> [u8; 7] {
self.notes
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agent_id::splitmix64;
#[test]
fn decoding_is_lossless() {
let mut s = 7u64;
for _ in 0..50_000 {
s = splitmix64(s);
let g = s & ((1 << GENOME_BITS) - 1);
assert_eq!(VisualGenes::decode(g).encode(), g);
assert_eq!(SoundGenes::decode(g).encode(), g);
}
}
#[test]
fn rhythms_are_distinct_and_balanced() {
for (i, a) in RHYTHMS.iter().enumerate() {
assert_eq!(a.iter().map(|&x| u32::from(x)).sum::<u32>(), 16);
for b in &RHYTHMS[i + 1..] {
assert_ne!(a, b);
}
}
}
}