use crate::cube::{
subset::{corner_pair_between_bars, eo_bars_by_axes, face_containing_bar},
Axis, Move,
};
use crate::method::{
builder,
shared::model::{edge::OrientedSliceEdge, orientation::MultiAxisCO},
substep::{DominoReduction, DominoReductionCP, DominoReductionCSep, Solved},
Breakdown, BreakdownError, Method, Substep,
};
#[derive(Copy, Clone)]
pub struct EOBar([OrientedSliceEdge; 12]);
impl EOBar {
fn oriented(&self, axis: Axis) -> bool {
match axis {
Axis::X => self.0.iter().all(|e| e.eo.x),
Axis::Y => self.0.iter().all(|e| e.eo.y),
Axis::Z => self.0.iter().all(|e| e.eo.z),
}
}
fn eobar(&self, eo_axis: Axis) -> Option<&'static [usize; 2]> {
for slice_axis in eo_axis.complements() {
let bars = eo_bars_by_axes(eo_axis, slice_axis).unwrap();
for bar in bars {
if bar.iter().all(|p| self.0[*p].from(slice_axis)) {
return Some(bar);
}
}
}
return None;
}
}
impl Substep for EOBar {
fn new() -> Self {
use Axis::*;
Self([X, Z, X, Z, Y, Y, Y, Y, X, Z, X, Z].map(|a| OrientedSliceEdge::new(a)))
}
fn after_moves(&self, moves: &[Move]) -> Self {
let mut new = self.clone();
for mv in moves {
let cycle = mv.face.edge_cycle();
let edges = cycle.map(|e| new.0[e]);
for from in 0..4 {
let to = (from as i32 + mv.amount).rem_euclid(4) as usize;
new.0[cycle[to]] = if mv.amount % 2 == 0 {
edges[from]
} else {
edges[from].flipped(mv.face.axis())
}
}
}
new
}
fn solved(&self) -> bool {
for axis in [Axis::X, Axis::Y, Axis::Z] {
if self.oriented(axis) && self.eobar(axis).is_some() {
return true;
}
}
false
}
fn comment() -> &'static str {
"EO Bar"
}
}
#[derive(Copy, Clone)]
pub struct EOStairs {
pub co: MultiAxisCO,
pub eobar: EOBar,
}
impl EOStairs {
fn hs_bar(&self, eo_axis: Axis, eo_bar: &'static [usize; 2]) -> Option<&'static [usize; 2]> {
let hs_bar_slice = face_containing_bar(eo_bar)?.axis();
let eo_bar_slice = Axis::complement(hs_bar_slice, eo_axis)?;
let hs_bars = eo_bars_by_axes(eo_axis, hs_bar_slice)?;
let EOBar(edges) = &self.eobar;
'finding_bar: for &hs_bar in hs_bars {
let hs_bar_solved =
edges[hs_bar[0]].from(eo_bar_slice) && edges[hs_bar[1]].from(eo_bar_slice);
if !hs_bar_solved {
continue 'finding_bar;
};
let corners = corner_pair_between_bars(eo_bar, hs_bar)?;
if self.co.subset_solved(eo_bar_slice, corners) {
return Some(hs_bar);
}
}
None
}
}
impl Substep for EOStairs {
fn new() -> Self {
Self {
co: MultiAxisCO::new(),
eobar: EOBar::new(),
}
}
fn after_moves(&self, moves: &[Move]) -> Self {
Self {
co: self.co.after_moves(moves),
eobar: self.eobar.after_moves(moves),
}
}
fn solved(&self) -> bool {
for axis in [Axis::X, Axis::Y, Axis::Z] {
if self.eobar.oriented(axis) {
if let Some(eobar) = self.eobar.eobar(axis) {
if self.hs_bar(axis, eobar).is_some() {
return true;
}
}
}
}
false
}
fn comment() -> &'static str {
"EO + Hollow Stairs"
}
}
pub struct EOStairsCO(EOStairs);
impl Substep for EOStairsCO {
fn new() -> Self {
Self(EOStairs::new())
}
fn after_moves(&self, moves: &[Move]) -> Self {
let EOStairsCO(inner) = &self;
EOStairsCO(inner.after_moves(moves))
}
fn solved(&self) -> bool {
let EOStairsCO(inner) = &self;
for axis in [Axis::X, Axis::Y, Axis::Z] {
if inner.eobar.oriented(axis) {
if let Some(eobar) = inner.eobar.eobar(axis) {
if let Some(hs_bar) = inner.hs_bar(axis, eobar) {
let bottom_face = face_containing_bar(hs_bar).unwrap();
let top_face = bottom_face.opposite();
let co = inner.co.for_axis(top_face.axis());
let top_corners = top_face.corner_cycle().map(|c| co[c]);
let bottom_corners = bottom_face.corner_cycle().map(|c| co[c]);
if (top_corners == [2, 1, 2, 1] || top_corners == [1, 2, 1, 2])
&& bottom_corners == [0, 0, 0, 0]
{
return true;
}
}
}
}
}
false
}
fn comment() -> &'static str {
"EO + Hollow Stairs + CO"
}
}
pub struct EOFirst(builder::Custom);
impl EOFirst {
pub fn new() -> Self {
Self(crate::method![
EOBar,
EOStairs,
EOStairsCO,
DominoReduction,
DominoReductionCSep,
DominoReductionCP,
Solved,
])
}
}
impl Method for EOFirst {
fn breakdown(&self, scramble: &[Move], solution: &[Move]) -> Result<Breakdown, BreakdownError> {
self.0.breakdown(scramble, solution)
}
}