use cetkaik_fundamental::{AbsoluteSide, Color, ColorAndProf, Profession};
use cetkaik_traits::{IsAbsoluteField, IsPieceWithSide};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum Piece {
Tam2,
NonTam2Piece {
color: Color,
prof: Profession,
side: AbsoluteSide,
},
}
#[must_use]
pub fn distance(a: Coord, b: Coord) -> i32 {
use super::{perspective, relative};
relative::distance(
perspective::to_relative_coord(a, perspective::Perspective::IaIsDownAndPointsUpward),
perspective::to_relative_coord(b, perspective::Perspective::IaIsDownAndPointsUpward),
)
}
#[must_use]
#[allow(clippy::cast_possible_wrap)]
pub const fn same_direction(origin: Coord, a: Coord, b: Coord) -> bool {
use super::perspective;
let origin =
perspective::to_relative_coord(origin, perspective::Perspective::IaIsDownAndPointsUpward);
let a = perspective::to_relative_coord(a, perspective::Perspective::IaIsDownAndPointsUpward);
let b = perspective::to_relative_coord(b, perspective::Perspective::IaIsDownAndPointsUpward);
let a_u = (a[0] as isize) - (origin[0] as isize);
let a_v = (a[1] as isize) - (origin[1] as isize);
let b_u = (b[0] as isize) - (origin[0] as isize);
let b_v = (b[1] as isize) - (origin[1] as isize);
(a_u * b_u + a_v * b_v > 0) && (a_u * b_v - a_v * b_u == 0)
}
impl Piece {
#[must_use]
pub const fn is_tam2(self) -> bool {
match self {
Piece::Tam2 => true,
Piece::NonTam2Piece { .. } => false,
}
}
#[must_use]
pub fn has_color(self, clr: Color) -> bool {
match self {
Piece::Tam2 => false,
Piece::NonTam2Piece { color, .. } => color == clr,
}
}
#[must_use]
pub fn has_prof(self, prf: Profession) -> bool {
match self {
Piece::Tam2 => false,
Piece::NonTam2Piece { prof, .. } => prof == prf,
}
}
#[must_use]
pub fn has_side(self, sid: AbsoluteSide) -> bool {
match self {
Piece::Tam2 => false,
Piece::NonTam2Piece { side, .. } => side == sid,
}
}
}
#[must_use]
pub const fn is_water(Coord(row, col): Coord) -> bool {
match row {
Row::O => matches!(
col,
Column::N | Column::T | Column::Z | Column::X | Column::C
),
Row::I | Row::U | Row::Y | Row::AI => matches!(col, Column::Z),
_ => false,
}
}
impl cetkaik_traits::IsAbsoluteBoard for Board {
fn yhuap_initial() -> Self {
yhuap_initial_board()
}
}
impl cetkaik_traits::IsBoard for Board {
type PieceWithSide = Piece;
type Coord = Coord;
fn peek(&self, c: Self::Coord) -> Option<Self::PieceWithSide> {
self.0.get(&c).copied()
}
fn pop(&mut self, c: Self::Coord) -> Option<Self::PieceWithSide> {
self.0.remove(&c)
}
fn put(&mut self, c: Self::Coord, p: Option<Self::PieceWithSide>) {
match p {
None => {
self.0.remove(&c);
}
Some(piece) => {
self.0.insert(c, piece);
}
}
}
fn assert_empty(&self, c: Self::Coord) {
assert!(
!self.0.contains_key(&c),
"Expected the square {:?} to be empty, but it was occupied",
c
);
}
fn assert_occupied(&self, c: Self::Coord) {
assert!(
self.0.contains_key(&c),
"Expected the square {:?} to be occupied, but it was empty",
c
);
}
type EmptySquaresIter = std::vec::IntoIter<Coord>;
fn empty_squares(&self) -> std::vec::IntoIter<Coord> {
use Column::{C, K, L, M, N, P, T, X, Z};
use Row::{A, AI, AU, E, I, IA, O, U, Y};
let mut ans = vec![];
for row in &[A, E, I, U, O, Y, AI, AU, IA] {
for column in &[K, L, N, T, Z, X, C, M, P] {
let coord = Coord(*row, *column);
if self.peek(coord).is_none() {
ans.push(coord);
}
}
}
ans.into_iter()
}
}
impl cetkaik_traits::IsField for Field {
type Board = Board;
type Coord = Coord;
type PieceWithSide = Piece;
type Side = AbsoluteSide;
fn move_nontam_piece_from_src_to_dest_while_taking_opponent_piece_if_needed(
&self,
src: Self::Coord,
dest: Self::Coord,
whose_turn: Self::Side,
) -> Result<Self, &'static str> {
let mut new_self = self.clone();
let src_piece = new_self
.board
.0
.remove(&src)
.ok_or("src does not contain a piece")?;
let Piece::NonTam2Piece { color: _color, prof: _prof, side } = src_piece
else {
return Err("Expected a NonTam2Piece to be present at the src, but found a Tam2")
};
if whose_turn != side {
return Err("Found the opponent piece at the src");
}
let maybe_captured_piece = new_self.board.0.remove(&dest);
new_self.board.0.insert(dest, src_piece);
if let Some(captured_piece) = maybe_captured_piece {
match captured_piece {
Piece::Tam2 => return Err("Tried to capture a Tam2"),
Piece::NonTam2Piece {
color: captured_piece_color,
prof: captured_piece_prof,
side: captured_piece_side,
} => {
if captured_piece_side == whose_turn {
return Err("Tried to capture an ally");
}
match whose_turn {
AbsoluteSide::IASide => {
new_self
.ia_side_hop1zuo1
.push(cetkaik_fundamental::ColorAndProf {
color: captured_piece_color,
prof: captured_piece_prof,
});
}
AbsoluteSide::ASide => {
new_self
.a_side_hop1zuo1
.push(cetkaik_fundamental::ColorAndProf {
color: captured_piece_color,
prof: captured_piece_prof,
});
}
}
}
}
}
Ok(new_self)
}
fn as_board(&self) -> &Self::Board {
&self.board
}
fn as_board_mut(&mut self) -> &mut Self::Board {
&mut self.board
}
#[must_use]
fn search_from_hop1zuo1_and_parachute_at(
&self,
color: Color,
prof: Profession,
side: AbsoluteSide,
to: Self::Coord,
) -> Option<Self> {
match side {
AbsoluteSide::ASide => {
let mut new_self = self.clone();
let index = new_self
.a_side_hop1zuo1
.iter()
.position(|x| *x == ColorAndProf { color, prof })?;
new_self.a_side_hop1zuo1.remove(index);
if self.board.0.contains_key(&to) {
return None;
}
new_self
.board
.0
.insert(to, Piece::NonTam2Piece { color, prof, side });
Some(new_self)
}
AbsoluteSide::IASide => {
let mut new_self = self.clone();
let index = new_self
.ia_side_hop1zuo1
.iter()
.position(|x| *x == ColorAndProf { color, prof })?;
new_self.ia_side_hop1zuo1.remove(index);
if self.board.0.contains_key(&to) {
return None;
}
new_self
.board
.0
.insert(to, Piece::NonTam2Piece { color, prof, side });
Some(new_self)
}
}
}
}
use std::collections::HashMap;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Board(pub HashMap<Coord, Piece>);
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Field {
pub board: Board,
pub a_side_hop1zuo1: Vec<ColorAndProf>,
pub ia_side_hop1zuo1: Vec<ColorAndProf>,
}
impl Field {
pub fn insert_nontam_piece_into_hop1zuo1(
&mut self,
color: Color,
prof: Profession,
side: AbsoluteSide,
) {
match side {
AbsoluteSide::ASide => self.a_side_hop1zuo1.push(ColorAndProf { color, prof }),
AbsoluteSide::IASide => self.ia_side_hop1zuo1.push(ColorAndProf { color, prof }),
}
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Deserialize, Serialize)]
#[allow(missing_docs)]
pub enum Row {
A,
E,
I,
U,
O,
Y,
AI,
AU,
IA,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Deserialize, Serialize)]
#[allow(missing_docs)]
pub enum Column {
K,
L,
N,
T,
Z,
X,
C,
M,
P,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Copy)]
pub struct Coord(pub Row, pub Column);
impl serde::ser::Serialize for Coord {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&serialize_coord(*self))
}
}
struct CoordVisitor;
impl<'de> serde::de::Visitor<'de> for CoordVisitor {
type Value = Coord;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a coordinate")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Coord::from_str(s).map_or_else(
|_| {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(s),
&self,
))
},
|c| Ok(c),
)
}
}
impl<'de> serde::de::Deserialize<'de> for Coord {
fn deserialize<D>(deserializer: D) -> Result<Coord, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserializer.deserialize_str(CoordVisitor)
}
}
impl FromStr for Coord {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_coord(s).ok_or(())
}
}
#[must_use]
pub fn parse_coord(coord: &str) -> Option<Coord> {
if coord.is_empty() || coord.len() > 3 {
return None;
}
let column = match coord.chars().next() {
Some('C') => Some(Column::C),
Some('K') => Some(Column::K),
Some('L') => Some(Column::L),
Some('M') => Some(Column::M),
Some('N') => Some(Column::N),
Some('P') => Some(Column::P),
Some('T') => Some(Column::T),
Some('X') => Some(Column::X),
Some('Z') => Some(Column::Z),
None | Some(_) => None,
}?;
let row = match &coord[1..coord.len()] {
"A" => Some(Row::A),
"AI" => Some(Row::AI),
"AU" => Some(Row::AU),
"E" => Some(Row::E),
"I" => Some(Row::I),
"O" => Some(Row::O),
"U" => Some(Row::U),
"Y" => Some(Row::Y),
"IA" => Some(Row::IA),
_ => None,
}?;
Some(Coord(row, column))
}
#[must_use]
#[allow(clippy::too_many_lines)]
pub fn yhuap_initial_board() -> Board {
Board(
[
(Coord(Row::O, Column::Z), Piece::Tam2),
(
Coord(Row::AI, Column::Z),
Piece::NonTam2Piece {
prof: Profession::Nuak1,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::K),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::N),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::C),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::P),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::L),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::T),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::X),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AI, Column::M),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::L),
Piece::NonTam2Piece {
prof: Profession::Gua2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::M),
Piece::NonTam2Piece {
prof: Profession::Gua2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::C),
Piece::NonTam2Piece {
prof: Profession::Kaun1,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::N),
Piece::NonTam2Piece {
prof: Profession::Kaun1,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::T),
Piece::NonTam2Piece {
prof: Profession::Dau2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::X),
Piece::NonTam2Piece {
prof: Profession::Dau2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::M),
Piece::NonTam2Piece {
prof: Profession::Maun1,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::L),
Piece::NonTam2Piece {
prof: Profession::Maun1,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::P),
Piece::NonTam2Piece {
prof: Profession::Kua2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::P),
Piece::NonTam2Piece {
prof: Profession::Tuk2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::X),
Piece::NonTam2Piece {
prof: Profession::Uai1,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::T),
Piece::NonTam2Piece {
prof: Profession::Uai1,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::Z),
Piece::NonTam2Piece {
prof: Profession::Io,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::IA, Column::K),
Piece::NonTam2Piece {
prof: Profession::Kua2,
color: Color::Kok1,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::AU, Column::K),
Piece::NonTam2Piece {
prof: Profession::Tuk2,
color: Color::Huok2,
side: AbsoluteSide::IASide,
},
),
(
Coord(Row::I, Column::Z),
Piece::NonTam2Piece {
prof: Profession::Nuak1,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::K),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::N),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::C),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::P),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::L),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::T),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::X),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::I, Column::M),
Piece::NonTam2Piece {
prof: Profession::Kauk2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::M),
Piece::NonTam2Piece {
prof: Profession::Gua2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::L),
Piece::NonTam2Piece {
prof: Profession::Gua2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::N),
Piece::NonTam2Piece {
prof: Profession::Kaun1,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::C),
Piece::NonTam2Piece {
prof: Profession::Kaun1,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::X),
Piece::NonTam2Piece {
prof: Profession::Dau2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::T),
Piece::NonTam2Piece {
prof: Profession::Dau2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::L),
Piece::NonTam2Piece {
prof: Profession::Maun1,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::M),
Piece::NonTam2Piece {
prof: Profession::Maun1,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::K),
Piece::NonTam2Piece {
prof: Profession::Kua2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::P),
Piece::NonTam2Piece {
prof: Profession::Tuk2,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::P),
Piece::NonTam2Piece {
prof: Profession::Kua2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::E, Column::K),
Piece::NonTam2Piece {
prof: Profession::Tuk2,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::T),
Piece::NonTam2Piece {
prof: Profession::Uai1,
color: Color::Huok2,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::X),
Piece::NonTam2Piece {
prof: Profession::Uai1,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
(
Coord(Row::A, Column::Z),
Piece::NonTam2Piece {
prof: Profession::Io,
color: Color::Kok1,
side: AbsoluteSide::ASide,
},
),
]
.into_iter()
.collect(),
)
}
#[must_use]
pub fn serialize_coord(coord: Coord) -> String {
let Coord(row, column) = coord;
format!(
"{}{}",
match column {
Column::K => "K",
Column::L => "L",
Column::M => "M",
Column::N => "N",
Column::P => "P",
Column::Z => "Z",
Column::X => "X",
Column::C => "C",
Column::T => "T",
},
match row {
Row::A => "A",
Row::E => "E",
Row::I => "I",
Row::O => "O",
Row::U => "U",
Row::Y => "Y",
Row::IA => "IA",
Row::AI => "AI",
Row::AU => "AU",
}
)
}
impl std::fmt::Display for Coord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", serialize_coord(*self))
}
}
pub type PureMove = cetkaik_fundamental::PureMove_<Coord>;
impl IsAbsoluteField for Field {
fn yhuap_initial() -> Self {
Field {
board: yhuap_initial_board(),
a_side_hop1zuo1: vec![],
ia_side_hop1zuo1: vec![],
}
}
type Hop1Zuo1Iter = std::vec::IntoIter<cetkaik_fundamental::ColorAndProf>;
fn hop1zuo1_of(&self, side: cetkaik_fundamental::AbsoluteSide) -> Self::Hop1Zuo1Iter {
match side {
AbsoluteSide::IASide => self.ia_side_hop1zuo1.clone().into_iter(),
AbsoluteSide::ASide => self.a_side_hop1zuo1.clone().into_iter(),
}
}
}
impl IsPieceWithSide for Piece {
type Side = AbsoluteSide;
fn match_on_piece_and_apply<U>(
self,
f_tam: &dyn Fn() -> U,
f_piece: &dyn Fn(Color, Profession, Self::Side) -> U,
) -> U {
match self {
Self::Tam2 => f_tam(),
Self::NonTam2Piece { color, prof, side } => f_piece(color, prof, side),
}
}
}