use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::PathBuf;
use crate::board;
pub type PieceGrid = [[char; 7]; 3];
const EMPTY_GRID: PieceGrid = [[' '; 7]; 3];
#[derive(Clone)]
pub struct CustomPieces {
pub pieces: HashMap<usize, PieceGrid>,
save_path: PathBuf,
}
pub const SHAPE_PALETTE: &[char] = &[
'█', '▀', '▁', '▂', '▃', '▄', '▅', '▆', '▇',
'▉', '▊', '▋', '▌', '▍', '▎', '▏', '▐',
'░', '▒', '▓', '▔', '▕',
'▖', '▗', '▘', '▙', '▚', '▛', '▜', '▝', '▞', '▟',
'─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼',
'═', '║', '╔', '╗', '╚', '╝', '╠', '╣', '╦', '╩', '╬',
'╭', '╮', '╯', '╰',
'━', '┃', '┏', '┓', '┗', '┛', '┣', '┫', '┳', '┻', '╋',
'┄', '┅', '┆', '┇', '┈', '┉', '┊', '┋', '╌', '╍', '╎', '╏',
'╱', '╲',
'■', '□', '▢', '▣', '▤', '▥', '▦', '▧', '▨', '▩', '▪', '▫', '▬', '▭', '▮', '▯',
'▰', '▱', '▲', '△', '▴', '▵', '▶', '▷', '▸', '▹',
'▼', '▽', '▾', '▿', '◀', '◁', '◂', '◃',
'◆', '◇', '◈', '◉', '◊', '○', '◌', '◍', '◎', '●',
'◐', '◑', '◒', '◓', '◔', '◕', '◖', '◗',
'◘', '◙', '◚', '◛', '◜', '◝', '◞', '◟', '◠', '◡',
'◢', '◣', '◤', '◥',
'◦', '◧', '◨', '◩', '◪', '◫', '◬', '◭', '◮', '◯',
'◰', '◱', '◲', '◳', '◴', '◵', '◶', '◷',
'◸', '◹', '◺', '◻', '◼', '◽', '◾', '◿',
'★', '☆', '✦', '✧', '✚', '✛', '✜', '✝',
'⊕', '⊖', '⊗', '⊘', '⊙', '⊞', '⊟', '⊠', '⊡',
'♔', '♕', '♖', '♗', '♘', '♙', '♚', '♛', '♜', '♝', '♞', '♟',
'+', '*', '^', '~', '·', '•', '∘', '∙',
];
pub const PIECE_TYPES: &[(usize, &str)] = &[
(board::KING, "King"),
(board::QUEEN, "Queen"),
(board::ROOK, "Rook"),
(board::BISHOP, "Bishop"),
(board::KNIGHT, "Knight"),
(board::PAWN, "Pawn"),
];
impl CustomPieces {
pub fn new(save_path: PathBuf) -> Self {
let mut cp = Self {
pieces: HashMap::new(),
save_path,
};
cp.load();
cp
}
pub fn get(&self, piece_type: usize) -> Option<&PieceGrid> {
self.pieces.get(&piece_type)
}
pub fn set(&mut self, piece_type: usize, grid: PieceGrid) {
self.pieces.insert(piece_type, grid);
}
pub fn save(&self) -> io::Result<()> {
if let Some(parent) = self.save_path.parent() {
fs::create_dir_all(parent)?;
}
let mut out = String::new();
for &(pt, name) in PIECE_TYPES {
if let Some(grid) = self.pieces.get(&pt) {
out.push_str(&format!("# {name}\n"));
out.push_str(&format!("{pt}\n"));
for row in grid {
let line: String = row.iter().collect();
out.push_str(&line);
out.push('\n');
}
out.push('\n');
}
}
fs::write(&self.save_path, out)
}
fn load(&mut self) {
let content = match fs::read_to_string(&self.save_path) {
Ok(c) => c,
Err(_) => return,
};
let mut lines = content.lines().peekable();
while let Some(line) = lines.next() {
let line = line.trim();
if line.starts_with('#') || line.is_empty() {
continue;
}
let pt: usize = match line.parse() {
Ok(v) => v,
Err(_) => continue,
};
let mut grid = EMPTY_GRID;
for row in &mut grid {
if let Some(row_line) = lines.next() {
for (col, ch) in row_line.chars().enumerate() {
if col < 7 {
row[col] = ch;
}
}
}
}
self.pieces.insert(pt, grid);
}
}
}
pub struct CanvasState {
pub selected_piece: usize, pub cursor_row: usize, pub cursor_col: usize, pub current_shape: usize, pub grid: PieceGrid, pub mode: CanvasMode,
}
pub enum CanvasMode {
PiecePicker, Drawing, ShapePicker, }
impl CanvasState {
pub fn new() -> Self {
Self {
selected_piece: 0,
cursor_row: 1,
cursor_col: 3,
current_shape: 0,
grid: EMPTY_GRID,
mode: CanvasMode::PiecePicker,
}
}
pub fn piece_type(&self) -> usize {
PIECE_TYPES[self.selected_piece].0
}
pub fn piece_name(&self) -> &str {
PIECE_TYPES[self.selected_piece].1
}
pub fn current_char(&self) -> char {
SHAPE_PALETTE[self.current_shape]
}
pub fn load_grid(&mut self, customs: &CustomPieces) {
self.grid = customs
.get(self.piece_type())
.copied()
.unwrap_or(EMPTY_GRID);
}
pub fn stamp(&mut self) {
self.grid[self.cursor_row][self.cursor_col] = self.current_char();
}
pub fn erase(&mut self) {
self.grid[self.cursor_row][self.cursor_col] = ' ';
}
}