1use crate::game_logic::coord::Coord;
2use ratatui::style::Color;
3use shakmaty::Square;
4
5#[must_use]
6pub fn color_to_ratatui_enum(piece_color: Option<shakmaty::Color>) -> Color {
7 match piece_color {
8 Some(shakmaty::Color::Black) => Color::Black,
9 Some(shakmaty::Color::White) => Color::White,
10 None => Color::Red,
11 }
12}
13
14#[must_use]
15pub fn flip_square_if_needed(square: Square, is_flipped: bool) -> Square {
16 if is_flipped {
17 Coord::from_square(square)
18 .reverse()
19 .to_square()
20 .unwrap_or(square)
21 } else {
22 square
23 }
24}
25
26#[must_use]
27pub fn get_square_from_coord(coord: Coord, is_flipped: bool) -> Option<Square> {
28 if is_flipped {
29 coord.reverse().to_square()
30 } else {
31 coord.to_square()
32 }
33}
34
35#[must_use]
36pub fn get_coord_from_square(square: Option<Square>, is_flipped: bool) -> Coord {
37 if let Some(s) = square {
38 if is_flipped {
39 Coord::from_square(s).reverse()
40 } else {
41 Coord::from_square(s)
42 }
43 } else {
44 Coord::undefined()
45 }
46}
47
48#[must_use]
50pub fn get_int_from_char(c: Option<char>) -> u8 {
51 match c {
52 Some('b' | '1') => 1,
53 Some('c' | '2') => 2,
54 Some('d' | '3') => 3,
55 Some('e' | '4') => 4,
56 Some('f' | '5') => 5,
57 Some('g' | '6') => 6,
58 Some('h' | '7') => 7,
59 _ => 0,
60 }
61}
62
63#[must_use]
64pub fn get_opposite_square(square: Option<Square>) -> Option<Square> {
65 square.and_then(|s| Coord::from_square(s).reverse().to_square())
66}
67
68#[must_use]
70pub fn convert_position_into_notation(position: &str) -> String {
71 let chars: Vec<char> = position.chars().collect();
72 if chars.len() < 4 {
73 return String::new();
74 }
75
76 let from_row = chars[0].to_digit(10).unwrap_or(0).min(255) as u8;
78 let from_col = chars[1].to_digit(10).unwrap_or(0).min(255) as u8;
79 let to_row = chars[2].to_digit(10).unwrap_or(0).min(255) as u8;
80 let to_col = chars[3].to_digit(10).unwrap_or(0).min(255) as u8;
81
82 let from_rank = 7 - from_row;
85 let to_rank = 7 - to_row;
86
87 let files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
88 let ranks = ['1', '2', '3', '4', '5', '6', '7', '8'];
89
90 format!(
91 "{}{}{}{}",
92 files[from_col as usize],
93 ranks[from_rank as usize],
94 files[to_col as usize],
95 ranks[to_rank as usize]
96 )
97}
98
99#[must_use]
101pub fn normalize_lowercase_to_san(input: &str) -> String {
102 let s0 = input.trim();
103
104 if s0.is_empty() {
105 return String::new();
106 }
107
108 let lower = s0.to_ascii_lowercase();
110 match lower.as_str() {
111 "o-o" | "0-0" => return "O-O".to_string(),
112 "o-o-o" | "0-0-0" => return "O-O-O".to_string(),
113 _ => {}
114 }
115
116 let mut s = s0.to_string();
117
118 if let Some(first) = s.chars().next() {
120 let up = match first {
121 'n' => Some('N'),
122 'b' => Some('B'),
123 'r' => Some('R'),
124 'q' => Some('Q'),
125 'k' => Some('K'),
126 _ => None, };
128
129 if let Some(up) = up {
130 s.replace_range(0..first.len_utf8(), &up.to_string());
131 }
132 }
133
134 if let Some(eq) = s.find('=') {
136 let bytes = s.as_bytes();
138 if eq + 1 < bytes.len() {
139 let promo = bytes[eq + 1] as char;
140 let up = match promo {
141 'q' => Some('Q'),
142 'r' => Some('R'),
143 'b' => Some('B'),
144 'n' => Some('N'),
145 _ => None,
146 };
147 if let Some(up) = up {
148 s.replace_range(eq + 1..eq + 2, &up.to_string());
149 }
150 }
151 }
152
153 s
154}