1mod types;
42mod pieces;
43mod board;
44mod movegen;
45mod eval;
46mod search;
47
48#[cfg(feature = "python")]
49mod python;
50
51pub const BOARD_SIZE: usize = types::BOARD_SIZE;
57
58pub const NUM_SQUARES: usize = types::NUM_SQUARES;
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63pub enum Color {
64 Black = 0,
65 White = 1,
66}
67
68impl Color {
69 pub fn opponent(self) -> Self {
71 match self {
72 Color::Black => Color::White,
73 Color::White => Color::Black,
74 }
75 }
76
77 pub(crate) fn raw(self) -> u8 {
78 self as u8
79 }
80
81 pub(crate) fn from_raw(v: u8) -> Self {
82 if v == 0 { Color::Black } else { Color::White }
83 }
84}
85
86impl std::fmt::Display for Color {
87 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
88 match self {
89 Color::Black => write!(f, "Black"),
90 Color::White => write!(f, "White"),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum GameResult {
98 BlackWins,
99 WhiteWins,
100 Draw,
101}
102
103impl std::fmt::Display for GameResult {
104 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
105 match self {
106 GameResult::BlackWins => write!(f, "Black wins"),
107 GameResult::WhiteWins => write!(f, "White wins"),
108 GameResult::Draw => write!(f, "Draw"),
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
119pub struct Square {
120 pub row: usize,
121 pub col: usize,
122}
123
124impl Square {
125 pub fn new(row: usize, col: usize) -> Self {
126 Square { row, col }
127 }
128
129 pub(crate) fn from_index(idx: usize) -> Self {
130 Square { row: idx / BOARD_SIZE, col: idx % BOARD_SIZE }
131 }
132
133 pub fn index(&self) -> usize {
135 self.row * BOARD_SIZE + self.col
136 }
137}
138
139impl std::fmt::Display for Square {
140 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
141 write!(f, "({},{})", self.row, self.col)
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub struct Piece {
148 pub(crate) type_id: u16,
150 pub color: Color,
152}
153
154impl Piece {
155 pub fn abbrev(&self) -> &'static str {
157 pieces::abbrev(self.type_id)
158 }
159
160 pub fn name(&self) -> &'static str {
162 pieces::name(self.type_id)
163 }
164
165 pub fn value(&self) -> i32 {
167 pieces::value(self.type_id)
168 }
169
170 pub fn is_royal(&self) -> bool {
172 pieces::is_royal(self.type_id)
173 }
174
175 pub fn promotes_to(&self) -> Option<&'static str> {
177 pieces::promotes_to(self.type_id).map(|pt| pieces::abbrev(pt))
178 }
179}
180
181impl std::fmt::Display for Piece {
182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
183 let prefix = if self.color == Color::White { 'v' } else { '^' };
184 write!(f, "{}{}", prefix, self.abbrev())
185 }
186}
187
188#[derive(Debug, Clone)]
190pub struct Move {
191 inner: types::Move,
192}
193
194impl Move {
195 pub fn from(&self) -> Square {
197 Square::from_index(self.inner.from_sq as usize)
198 }
199
200 pub fn to(&self) -> Square {
202 Square::from_index(self.inner.to_sq as usize)
203 }
204
205 pub fn is_promotion(&self) -> bool {
207 self.inner.promotion
208 }
209
210 pub fn is_igui(&self) -> bool {
212 self.inner.is_igui
213 }
214
215 pub fn captured(&self) -> Option<&'static str> {
217 if self.inner.captured_piece != 0 {
218 Some(pieces::abbrev(self.inner.captured_piece))
219 } else {
220 None
221 }
222 }
223
224 pub fn raw(&self) -> &types::Move {
226 &self.inner
227 }
228}
229
230impl std::fmt::Display for Move {
231 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
232 write!(f, "{}->{}", self.from(), self.to())?;
233 if self.is_promotion() { write!(f, "+")?; }
234 Ok(())
235 }
236}
237
238pub struct SearchResult {
240 pub best_move: Option<Move>,
242 pub score: i32,
244 pub nodes: u64,
246 pub time_ms: u64,
248}
249
250pub struct PieceInfo {
252 pub abbrev: &'static str,
253 pub name: &'static str,
254 pub value: i32,
255 pub promotes_to: Option<&'static str>,
256 pub slide_directions: usize,
257 pub jump_destinations: usize,
258 pub has_hook: bool,
259 pub area_steps: u8,
260 pub has_range_capture: bool,
261 pub has_igui: bool,
262}
263
264pub fn piece_info(abbrev: &str) -> Option<PieceInfo> {
272 let pt = pieces::find_by_abbrev(abbrev)?;
273 let mv = pieces::movement(pt);
274 let promo = pieces::promotes_to(pt).map(|p| pieces::abbrev(p));
275 Some(PieceInfo {
276 abbrev: pieces::abbrev(pt),
277 name: pieces::name(pt),
278 value: pieces::value(pt),
279 promotes_to: promo,
280 slide_directions: mv.slides.len(),
281 jump_destinations: mv.jumps.len(),
282 has_hook: mv.hook.is_some(),
283 area_steps: mv.area,
284 has_range_capture: !mv.range_capture.is_empty(),
285 has_igui: mv.igui,
286 })
287}
288
289pub fn num_piece_types() -> usize {
291 pieces::num_piece_types()
292}
293
294pub struct Board {
304 inner: board::Board,
305}
306
307impl Board {
308 pub fn empty() -> Self {
310 Board { inner: board::Board::new() }
311 }
312
313 pub fn initial() -> Self {
321 let mut b = board::Board::new();
322 b.setup_initial();
323 Board { inner: b }
324 }
325
326 pub fn side_to_move(&self) -> Color {
328 Color::from_raw(self.inner.side_to_move)
329 }
330
331 pub fn move_number(&self) -> u32 {
333 self.inner.move_number
334 }
335
336 pub fn get(&self, row: usize, col: usize) -> Option<Piece> {
338 let sq = types::sq_index(row, col);
339 let cell = self.inner.cells[sq];
340 if cell == types::EMPTY_CELL {
341 None
342 } else {
343 Some(Piece {
344 type_id: types::cell_piece(cell),
345 color: Color::from_raw(types::cell_color(cell)),
346 })
347 }
348 }
349
350 pub fn piece_count(&self, color: Color) -> usize {
352 self.inner.piece_count[color.raw() as usize]
353 }
354
355 pub fn material_score(&self) -> i32 {
358 eval::material_score(&self.inner)
359 }
360
361 pub fn evaluate(&self) -> i32 {
363 eval::evaluate(&self.inner)
364 }
365
366 pub fn game_result(&self) -> Option<GameResult> {
368 self.inner.game_result().map(|r| match r {
369 types::GameResult::BlackWins => GameResult::BlackWins,
370 types::GameResult::WhiteWins => GameResult::WhiteWins,
371 types::GameResult::Draw => GameResult::Draw,
372 })
373 }
374
375 pub fn legal_moves(&self) -> Vec<Move> {
377 movegen::generate_legal_moves(&self.inner)
378 .into_iter()
379 .map(|m| Move { inner: m })
380 .collect()
381 }
382
383 pub fn apply(&mut self, mv: &Move) {
385 self.inner.apply_move(&mv.inner);
386 }
387
388 pub fn apply_by_coord(&mut self, from_row: usize, from_col: usize,
396 to_row: usize, to_col: usize, promotion: bool) -> bool {
397 let from_sq = types::sq_index(from_row, from_col) as u16;
398 let to_sq = types::sq_index(to_row, to_col) as u16;
399 let moves = movegen::generate_legal_moves(&self.inner);
400 for m in &moves {
401 if m.from_sq == from_sq && m.to_sq == to_sq && m.promotion == promotion {
402 self.inner.apply_move(m);
403 return true;
404 }
405 }
406 false
407 }
408
409 pub fn undo(&mut self) -> bool {
411 self.inner.undo_move()
412 }
413
414 pub fn random_move(&self) -> Option<Move> {
416 let moves = movegen::generate_legal_moves(&self.inner);
417 if moves.is_empty() { return None; }
418 use rand::Rng;
419 let idx = rand::thread_rng().gen_range(0..moves.len());
420 Some(Move { inner: moves.into_iter().nth(idx).unwrap() })
421 }
422
423 pub fn search(&mut self, depth: u32, time_limit_ms: u64) -> SearchResult {
428 let r = search::search(&mut self.inner, depth, time_limit_ms);
429 SearchResult {
430 best_move: r.best_move.map(|m| Move { inner: m }),
431 score: r.score,
432 nodes: r.nodes,
433 time_ms: r.time_ms,
434 }
435 }
436
437 pub fn display(&self) -> String {
439 self.inner.display()
440 }
441
442 pub fn pieces(&self, color: Color) -> Vec<(Square, Piece)> {
444 let c = color.raw() as usize;
445 let mut result = Vec::new();
446 for i in 0..self.inner.piece_list_len[c] {
447 let sq = self.inner.piece_list[c][i];
448 if sq == types::INVALID_SQ { continue; }
449 let cell = self.inner.cells[sq as usize];
450 if cell == types::EMPTY_CELL { continue; }
451 result.push((
452 Square::from_index(sq as usize),
453 Piece {
454 type_id: types::cell_piece(cell),
455 color,
456 },
457 ));
458 }
459 result
460 }
461}
462
463impl Clone for Board {
464 fn clone(&self) -> Self {
465 Board { inner: self.inner.clone() }
466 }
467}
468
469impl std::fmt::Display for Board {
470 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
471 write!(f, "{}", self.display())
472 }
473}
474
475impl std::fmt::Debug for Board {
476 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
477 f.debug_struct("Board")
478 .field("side_to_move", &self.side_to_move())
479 .field("move_number", &self.move_number())
480 .field("black_pieces", &self.piece_count(Color::Black))
481 .field("white_pieces", &self.piece_count(Color::White))
482 .finish()
483 }
484}
485
486#[cfg(feature = "python")]
491use pyo3::prelude::*;
492
493#[cfg(feature = "python")]
494#[pymodule]
495fn taikyokushogi(m: &Bound<'_, PyModule>) -> PyResult<()> {
496 python::register(m)
497}