twisted 0.1.3

A speedcubing and smart-cube library.
Documentation
// SPDX-FileCopyrightText: 2022 Declan Rixon <twisted.cubing@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-only

use crate::{
    cube::Move,
    method::{Breakdown, BreakdownError, Method, Substep, SubstepSolution},
};

/// Allows building custom methods from a list of substeps.
#[macro_export]
macro_rules! method {
    ( $( $x:ty ), * $(,)? ) => {
        {
            use crate::method::builder::Custom;
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push(Custom::step::<$x>());
            )*
            Custom::new(temp_vec)
        }
    };
}

pub trait CustomSubstep {
    fn after_moves(&self, moves: &[Move]) -> Box<dyn CustomSubstep>;

    fn apply_moves(&mut self, moves: &[Move]);

    fn solved(&self) -> bool;

    fn comment(&self) -> &'static str;
}

struct SubstepWrapper<S>(S)
where
    S: Substep;

impl<S> SubstepWrapper<S>
where
    S: Substep,
{
    fn new() -> Self {
        Self(S::new())
    }
}

impl<S: 'static> CustomSubstep for SubstepWrapper<S>
where
    S: Substep,
{
    fn after_moves(&self, moves: &[Move]) -> Box<dyn CustomSubstep> {
        Box::new(Self(self.0.after_moves(moves)))
    }

    fn apply_moves(&mut self, moves: &[Move]) {
        self.0 = self.0.after_moves(moves);
    }

    fn solved(&self) -> bool {
        self.0.solved()
    }

    fn comment(&self) -> &'static str {
        S::comment()
    }
}

pub struct Custom {
    steps: Vec<Box<dyn CustomSubstep>>,
}

impl Custom {
    pub fn step<S: 'static>() -> Box<dyn CustomSubstep>
    where
        S: Substep,
    {
        Box::new(SubstepWrapper::<S>::new())
    }

    pub fn new(steps: Vec<Box<dyn CustomSubstep>>) -> Custom {
        Custom { steps }
    }
}

impl Method for Custom {
    fn breakdown(&self, scramble: &[Move], solution: &[Move]) -> Result<Breakdown, BreakdownError> {
        struct SubstepState {
            model: Box<dyn CustomSubstep>,
            solved: bool,
        }

        impl SubstepState {
            fn process_move(
                &mut self,
                breakdown: &mut Vec<SubstepSolution>,
                move_buff: &mut Vec<Move>,
                mv: Move,
            ) {
                if !self.solved {
                    self.model = self.model.after_moves(&[mv]);
                    if self.model.solved() {
                        breakdown.push(SubstepSolution {
                            comment: self.model.comment(),
                            solution: move_buff.to_vec(),
                        });
                        *move_buff = vec![];
                        self.solved = true;
                    }
                }
            }
        }

        let mut breakdown = vec![];
        let mut move_buff = vec![];

        let mut substep_states = vec![];

        for model in &self.steps {
            let scrambled = model.after_moves(&scramble);
            substep_states.push(SubstepState {
                solved: scrambled.solved(),
                model: scrambled,
            });
        }

        for mv in solution {
            move_buff.push(*mv);
            for state in &mut substep_states {
                state.process_move(&mut breakdown, &mut move_buff, *mv);
            }
        }

        if move_buff.len() != 0 {
            breakdown.push(SubstepSolution {
                comment: "DNF",
                solution: move_buff,
            });
        }

        Ok(breakdown)
    }
}