use core::fmt;
use crate::feen::Feen;
impl fmt::Display for Feen<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let placement = core::str::from_utf8(self.placement_field()).map_err(|_| fmt::Error)?;
let hands = core::str::from_utf8(self.hands_field()).map_err(|_| fmt::Error)?;
write!(
f,
"{placement} {hands} {}/{}",
self.active_style().to_char(),
self.inactive_style().to_char(),
)
}
}
#[cfg(feature = "alloc")]
mod owned {
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp::Reverse;
use core::fmt::{self, Write};
use sashite_epin::Identifier as Piece;
use sashite_qi::{Player, Qi};
use sashite_sin::Identifier as Style;
use crate::hands::token_key;
#[must_use]
pub fn encode(position: &Qi<Piece, Style>) -> String {
let mut out = String::new();
let _ = write_feen(&mut out, position);
out
}
pub fn write_feen<W: Write>(w: &mut W, position: &Qi<Piece, Style>) -> fmt::Result {
write_placement(w, position)?;
w.write_str(" ")?;
write_hand(w, position.first_hand())?;
w.write_str("/")?;
write_hand(w, position.second_hand())?;
let (active, inactive) = match position.turn() {
Player::First => (position.first_style(), position.second_style()),
Player::Second => (position.second_style(), position.first_style()),
};
write!(w, " {}/{}", active.to_char(), inactive.to_char())
}
fn write_placement<W: Write>(w: &mut W, position: &Qi<Piece, Style>) -> fmt::Result {
let dims = position.shape();
let ndim = dims.len();
let files = dims[ndim - 1]; let total = position.square_count();
let layer_stride = if ndim >= 3 { files * dims[ndim - 2] } else { 0 };
let mut empty_run: usize = 0;
let mut emitted: usize = 0;
for cell in position.board() {
match cell {
Some(piece) => {
flush_empties(w, &mut empty_run)?;
w.write_str(piece.encode().as_str())?;
}
None => empty_run += 1,
}
emitted += 1;
if emitted % files == 0 {
flush_empties(w, &mut empty_run)?;
if emitted < total {
if ndim >= 3 && emitted % layer_stride == 0 {
w.write_str("//")?;
} else {
w.write_str("/")?;
}
}
}
}
Ok(())
}
fn flush_empties<W: Write>(w: &mut W, empty_run: &mut usize) -> fmt::Result {
if *empty_run > 0 {
write!(w, "{empty_run}")?;
*empty_run = 0;
}
Ok(())
}
fn write_hand<'a, W: Write>(
w: &mut W,
hand: impl Iterator<Item = (&'a Piece, usize)>,
) -> fmt::Result {
let mut items: Vec<(Piece, usize)> = hand.map(|(piece, count)| (*piece, count)).collect();
items.sort_unstable_by_key(|&(piece, count)| {
let (letter, case, state, terminal, derived) = token_key(piece);
(Reverse(count), letter, case, state, terminal, derived)
});
for (piece, count) in items {
if count > 1 {
write!(w, "{count}")?;
}
w.write_str(piece.encode().as_str())?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
pub use owned::{encode, write_feen};