#![warn(clippy::all, clippy::nursery, clippy::pedantic, clippy::cargo)]
use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Choice {
Left,
Right,
}
impl Choice {
const fn to_bit(self) -> u32 {
match self {
Self::Left => 0,
Self::Right => 1,
}
}
pub fn display<T>(self, choices: [T; 2]) -> T {
let [left, right] = choices;
match self {
Self::Left => left,
Self::Right => right,
}
}
}
impl From<bool> for Choice {
fn from(b: bool) -> Self {
if b { Self::Right } else { Self::Left }
}
}
pub struct Predictor {
n: usize,
state: u32,
count: usize,
grams: Vec<Prediction>,
pub total_predictions: usize,
pub correct_predictions: usize,
}
impl Predictor {
#[must_use]
pub fn new(n: usize) -> Self {
Self {
n,
state: 0,
count: 0,
total_predictions: 0,
correct_predictions: 0,
grams: vec![Prediction::default(); 1 << n],
}
}
pub fn predict(&mut self, choice: Choice) -> Option<Choice> {
if self.count < self.n {
self.state = (self.state << 1) | choice.to_bit();
self.count += 1;
return None;
}
let prediction = self.grams[self.state as usize].predict();
self.grams[self.state as usize].register(choice);
self.state = ((self.state << 1) | choice.to_bit()) & ((1 << self.n) - 1);
self.total_predictions += 1;
if prediction == choice {
self.correct_predictions += 1;
}
Some(prediction)
}
}
#[derive(Debug, Clone, Copy, Default)]
struct Prediction {
left: usize,
right: usize,
}
impl Prediction {
fn predict(&self) -> Choice {
match self.left.cmp(&self.right) {
Ordering::Less => Choice::Right,
Ordering::Greater => Choice::Left,
Ordering::Equal => Choice::from(rand::random::<bool>()),
}
}
const fn register(&mut self, choice: Choice) {
match choice {
Choice::Left => self.left += 1,
Choice::Right => self.right += 1,
}
}
}