arday11ChessLibrary/
lib.rs

1use std::cmp::PartialEq;
2use std::collections::HashMap;
3use serde::Serialize;
4use crate::Color::{BLACK, WHITE};
5use crate::Move::{CAPTURE, REGULAR};
6use crate::Status::{BLACK_TO_MOVE, WHITE_TO_MOVE};
7use std::io::*;
8
9const STARTING_FEN: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
10
11#[derive(PartialEq, Debug)]
12pub enum Status {
13    WHITE_TO_MOVE,
14    BLACK_TO_MOVE,
15    DRAW,
16    WHITE_HAS_CHECKMATE,
17    BLACK_HAS_CHECKMATE,
18}
19
20pub type ChessBoard = Vec<Vec<char>>;
21
22#[derive(Debug)]
23pub struct Board {
24    pub board: ChessBoard,
25}
26
27impl Board {
28    pub fn create() -> Board {
29        Board {
30            board: convert_fen_to_vector(STARTING_FEN),
31        }
32    }
33
34    pub fn create_from_fen(FEN: &str) -> Board {
35        Board {
36            board: convert_fen_to_vector(FEN),
37        }
38    }
39
40    pub fn get(&self, rank: usize, file: usize) -> char {
41        self.board[rank][file]
42    }
43
44    pub fn pushRow(&mut self, row: Vec<char>) {
45        self.board.push(row);
46    }
47
48    pub fn clone(&self) -> Self {
49        // Create a new Board instance
50        Board {
51            board: self.board.clone(), // Clone the 2D vector
52        }
53    }
54
55    pub fn make_move(board: &Board, start: Position, end: &Position, piece: char) -> Board {
56        let mut new_board = board.clone();
57        new_board.board[start.rank][start.file] = '-';
58        new_board.board[end.rank][end.file] = piece;
59        new_board
60    }
61    /*
62    pub fn generate_legal_moves(&self, currentTurn: &Color) -> Vec<Board> {
63        let mut boards: Vec<Board> = Vec::new();
64
65        for (rowIndex, row) in self.board.iter().enumerate() {
66            for (fileIndex, piece) in row.iter().enumerate() {
67                if *currentTurn == WHITE {
68                    match piece {
69                        &'P' => {
70                            let legal_moves = get_pawn_moves(self, &Position::create(rowIndex, fileIndex));
71
72                            for legal_move in &legal_moves {
73                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), legal_move, 'P');
74                                boards.push(new_board);
75                            }
76                        }
77                        &'R' => {
78                            let legal_moves = get_rook_moves(self, &Position::create(rowIndex, fileIndex));
79                            for legal_move in legal_moves {
80                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'R');
81                                boards.push(new_board);
82                            }
83                        }
84                        &'N' => {
85                            let legal_moves = get_knight_moves(self, &Position::create(rowIndex, fileIndex));
86                            for legal_move in legal_moves {
87                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'N');
88                                boards.push(new_board);
89                            }
90                        }
91                        &'B' => {
92                            let legal_moves = get_bishop_moves(self, &Position::create(rowIndex, fileIndex));
93                            for legal_move in legal_moves {
94                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'B');
95                                boards.push(new_board);
96                            }
97                        }
98                        &'Q' => {
99                            let legal_moves = get_queen_moves(self, &Position::create(rowIndex, fileIndex));
100                            for legal_move in legal_moves {
101                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'Q');
102                                boards.push(new_board);
103                            }
104                        }
105                        &'K' => {
106                            let legal_moves = get_king_moves(self, &Position::create(rowIndex, fileIndex), WHITE, Game::new());
107                            for legal_move in legal_moves {
108                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'K');
109                                boards.push(new_board);
110                            }
111                        }
112                        _ => {}
113                    }
114                }
115
116                else {
117                    match piece {
118                        &'p' => {
119                            let legal_moves = get_pawn_moves(self, &Position::create(rowIndex, fileIndex));
120
121                            for legal_move in &legal_moves {
122                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), legal_move, 'p');
123                                boards.push(new_board);
124                            }
125                        }
126                        &'r' => {
127                            let legal_moves = get_rook_moves(self, &Position::create(rowIndex, fileIndex));
128                            for legal_move in legal_moves {
129                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'r');
130                                boards.push(new_board);
131                            }
132                        }
133                        &'n' => {
134                            let legal_moves = get_knight_moves(self, &Position::create(rowIndex, fileIndex));
135                            for legal_move in legal_moves {
136                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'n');
137                                boards.push(new_board);
138                            }
139                        }
140                        &'b' => {
141                            let legal_moves = get_bishop_moves(self, &Position::create(rowIndex, fileIndex));
142                            for legal_move in legal_moves {
143                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'b');
144                                boards.push(new_board);
145                            }
146                        }
147                        &'q' => {
148                            let legal_moves = get_queen_moves(self, &Position::create(rowIndex, fileIndex));
149                            for legal_move in legal_moves {
150                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'q');
151                                boards.push(new_board);
152                            }
153                        }
154                        &'k' => {
155                            let legal_moves = get_king_moves(self, &Position::create(rowIndex, fileIndex));
156                            for legal_move in legal_moves {
157                                let new_board = Self::make_move(self, Position::create(rowIndex, fileIndex), &legal_move, 'k');
158                                boards.push(new_board);
159                            }
160                        }
161                        _ => {}
162                    }
163                }
164            }
165        }
166
167        boards
168    }
169     */
170
171    pub fn print(&self) {
172        for row in &self.board {
173            println!("{:?}", row);
174        }
175    }
176
177    /*
178    pub fn perft(&self, depth: usize, currentColor: Color) -> u64 {
179        if depth == 0 {
180            return 1;
181        }
182
183        let mut nodes = 0;
184
185        let moves = self.generate_legal_moves(&currentColor);
186
187        // Recursively count nodes at the next depth for each legal move
188        for new_board in moves {
189            let newColor = if currentColor == WHITE {
190                BLACK
191            }
192            else {
193                WHITE
194            };
195
196            nodes += new_board.perft(depth - 1, newColor);
197
198            /*
199            new_board.print();
200            println!("");
201            println!("");
202             */
203        }
204
205        nodes
206    }
207     */
208}
209
210#[derive(Serialize)]
211#[derive(Debug)]
212#[derive(Eq, Hash, PartialEq)]
213pub struct Position {
214    rank: usize,
215    file: usize
216}
217
218impl Position {
219    pub fn create(rank: usize, file: usize) -> Position {
220        Position {
221            rank,
222            file
223        }
224    }
225}
226
227#[derive(PartialEq, Debug)]
228pub enum Color {
229    WHITE,
230    BLACK
231}
232
233pub enum Move {
234    REGULAR,
235    CAPTURE,
236    CASTLE
237}
238pub struct Game {
239    board: Board,
240    status: Status,
241    current_move: Move,
242    white_castle_short: bool,
243    white_castle_long: bool,
244    black_castle_short: bool,
245    black_castle_long: bool,
246    en_passant_possible: bool,
247}
248
249impl Game {
250    pub fn new() -> Game {
251        Game {
252            status: WHITE_TO_MOVE,
253            current_move: REGULAR,
254            white_castle_short: true,
255            white_castle_long: true,
256            black_castle_short: true,
257            black_castle_long: true,
258            en_passant_possible: false,
259            board: Board::create(),
260        }
261    }
262}
263
264pub fn convert_fen_to_vector(fen: &str) -> ChessBoard {
265    let mut board: ChessBoard = Vec::new();
266    let position = fen.split(" ").collect::<Vec<&str>>()[0];
267
268    for rank in position.split("/").collect::<Vec<&str>>() {
269        let mut row_vector = Vec::new();
270
271        for square in rank.chars() {
272            match square.to_digit(10) {
273                Some(num) => {
274                    let num = num as i32;
275
276                    for _i in 0..num {
277                        row_vector.push('-');
278                    }
279                }
280
281                None => row_vector.push(square)
282            }
283        }
284
285        board.push(row_vector);
286    }
287
288    board
289}
290
291fn get_piece_from_position(board: &Board, piece_pos: &Position) -> char {
292    board.get(piece_pos.rank, piece_pos.file)
293}
294
295fn get_castling_position(game: &Game) -> Vec<Position> {
296    let mut positons = Vec::new();
297
298    if game.status == WHITE_TO_MOVE {
299        if game.white_castle_short {
300            if game.board.get(7, 5) == '-' && game.board.get(7, 6) == '-' {
301                positons.push(Position {
302                    rank: 7,
303                    file: 6
304                })
305            }
306        }
307
308        if game.white_castle_short {
309            if game.board.get(7, 1) == '-' && game.board.get(7, 2) == '-' && game.board.get(7, 3) == '-' {
310                positons.push(Position {
311                    rank: 7,
312                    file: 2
313                })
314            }
315        }
316    }
317
318    else {
319        if game.black_castle_short {
320            if game.board.get(0, 5) == '-' && game.board.get(0, 6) == '-' {
321                positons.push(Position {
322                    rank: 0,
323                    file: 6
324                })
325            }
326        }
327
328        if game.black_castle_long {
329            if game.board.get(0, 1) == '-' && game.board.get(0, 2) == '-' && game.board.get(0, 3) == '-' {
330                positons.push(Position {
331                    rank: 0,
332                    file: 2
333                })
334            }
335        }
336    }
337
338    positons
339}
340
341fn has_enemy_piece(board: &Board, pos: &Position, current_piece: char) -> bool {
342    let piece = get_piece_from_position(&board, &pos);
343
344    if current_piece.is_uppercase() && piece.is_lowercase() {
345        return true;
346    }
347
348    if current_piece.is_lowercase() && piece.is_uppercase() {
349        return true;
350    }
351
352    false
353}
354
355fn get_pawn_capture_pos(board: &Board, pawn_pos: &Position, pawn: char) -> Vec<Position> {
356    let mut target: Vec<Position> = Vec::new();
357    let mut target_rank = 0;
358
359    if pawn.is_lowercase() {
360        target_rank = pawn_pos.rank + 1;
361    }
362
363    else {
364        target_rank = pawn_pos.rank - 1;
365    }
366
367    if (pawn_pos.file > 0) {
368        let square2 = pawn_pos.file - 1;
369        let target2 = Position::create(target_rank, square2);
370        let has_enemy2 = has_enemy_piece(&board, &target2, pawn);
371
372        if pawn_pos.file == 7 && has_enemy2 {
373            target.push(Position::create(target_rank, square2));
374        }
375
376        else if has_enemy2 {
377            target.push(Position::create(target_rank, square2))
378        }
379    }
380
381    if (pawn_pos.file < 7) {
382        let square1 = pawn_pos.file + 1;
383        let target1 = Position::create(target_rank, square1);
384        let has_enemy1 = has_enemy_piece(&board, &target1, pawn);
385
386        if pawn_pos.file == 0 && has_enemy1 {
387            target.push(Position::create(target_rank, square1));
388        }
389
390        else if has_enemy1 {
391            target.push(Position::create(target_rank, square1));
392        }
393    }
394
395    target
396}
397
398fn get_horizontal_moves(board: &Board, piece_pos: &Position) -> Vec<Position> {
399    let mut positions: Vec<Position> = Vec::new();
400    let rank = piece_pos.rank;
401    let mut file = piece_pos.file;
402
403    loop {
404        if file == 7 {
405            if rank != piece_pos.rank || file != piece_pos.file {
406                positions.push(Position::create(rank, 7));
407            }
408
409            file = piece_pos.file;
410
411            break;
412        }
413
414        if rank == piece_pos.rank && file == piece_pos.file {
415            file += 1;
416            continue;
417        }
418
419        if board.get(rank, file) != '-'  {
420            if is_enemy(board, piece_pos, &Position::create(rank, file)) {
421                positions.push(Position::create(rank, file));
422            }
423
424            file = piece_pos.file;
425
426            break;
427        }
428
429        positions.push(Position::create(rank, file));
430
431        file += 1;
432    }
433
434    loop {
435        if file == 0 {
436            if rank != piece_pos.rank || file != piece_pos.file {
437                positions.push(Position::create(rank, 0));
438            }
439
440            file = piece_pos.file;
441
442            break;
443        }
444
445        if rank == piece_pos.rank && file == piece_pos.file {
446            file -= 1;
447            continue;
448        }
449
450        if board.get(rank, file) != '-' {
451            if is_enemy(board, piece_pos, &Position::create(rank, file)) {
452                positions.push(Position::create(rank, file));
453            }
454
455            file = piece_pos.file;
456
457            break;
458        }
459
460        positions.push(Position::create(rank, file));
461
462        file -= 1;
463    }
464
465    positions
466}
467
468fn is_enemy(board: &Board, current_pos: &Position, target_pos: &Position) -> bool {
469    let current_piece = board.get(current_pos.rank, current_pos.file);
470
471    if current_piece.is_lowercase() {
472        if board.get(target_pos.rank, target_pos.file).is_uppercase() {
473            return true;
474        }
475    }
476
477    else {
478        if board.get(target_pos.rank, target_pos.file).is_lowercase() {
479            return true;
480        }
481    }
482
483    false
484}
485
486fn get_vertical_moves(board: &Board, piece_pos: &Position) -> Vec<Position> {
487    let mut positions: Vec<Position> = Vec::new();
488    let mut rank = piece_pos.rank;
489    let file = piece_pos.file;
490
491    loop {
492        if rank == 7 {
493            if file != piece_pos.file || rank != piece_pos.rank {
494                positions.push(Position::create(7, file));
495            }
496
497            rank = piece_pos.rank;
498
499            break;
500        }
501
502        if rank == piece_pos.rank && file == piece_pos.file {
503            rank += 1;
504            continue;
505        }
506
507        if board.get(rank, file) != '-' {
508            if is_enemy(board, piece_pos, &Position::create(rank, file)) {
509                positions.push(Position::create(rank, file));
510            }
511
512            rank = piece_pos.rank;
513
514            break;
515        }
516
517        positions.push(Position::create(rank, file));
518
519        rank += 1;
520    }
521
522    loop {
523        if rank == 0 {
524            if file != piece_pos.file || rank != piece_pos.rank {
525                positions.push(Position::create(rank, file));
526            }
527
528            rank = piece_pos.rank;
529
530            break;
531        }
532
533        if rank == piece_pos.rank && file == piece_pos.file {
534            rank -= 1;
535            continue;
536        }
537
538        if board.get(rank, file) != '-' {
539            if is_enemy(board, piece_pos, &Position::create(rank, file)) {
540                positions.push(Position::create(rank, file));
541            }
542
543            rank = piece_pos.rank;
544
545            break;
546        }
547
548        positions.push(Position::create(rank, file));
549
550        rank -= 1;
551    }
552
553    positions
554}
555
556fn get_diagonal_moves(board: &Board, piece_pos: &Position) -> Vec<Position> {
557    let mut positions: Vec<Position> = Vec::new();
558    let mut rank = piece_pos.rank;
559    let mut file = piece_pos.file;
560
561    if rank > 0 && file > 0 {
562        loop {
563            if board.get(rank - 1, file - 1) != '-' {
564                if is_enemy(board, piece_pos, &Position::create(rank - 1, file - 1)) {
565                    positions.push(Position::create(rank - 1, file - 1));
566                }
567
568                rank = piece_pos.rank;
569                file = piece_pos.file;
570                break;
571            }
572
573            positions.push(Position::create(rank - 1, file - 1));
574
575            rank = rank - 1;
576            file = file - 1;
577
578            if rank == 0 || file == 0 {
579                rank = piece_pos.rank;
580                file = piece_pos.file;
581                break;
582            }
583        }
584    }
585
586    if rank > 0 && file < 7 {
587        loop {
588            if board.get(rank - 1, file + 1) != '-' {
589                if is_enemy(board, piece_pos, &Position::create(rank - 1, file + 1)) {
590                    positions.push(Position::create(rank - 1, file + 1));
591                }
592
593                rank = piece_pos.rank;
594                file = piece_pos.file;
595                break;
596            }
597
598            positions.push(Position::create(rank - 1, file + 1));
599
600            rank = rank - 1;
601            file = file + 1;
602
603            if rank == 0 || file == 7 {
604                rank = piece_pos.rank;
605                file = piece_pos.file;
606                break;
607            }
608        }
609    }
610
611    if rank < 7 && file > 0 {
612        loop {
613            if board.get(rank + 1, file - 1) != '-' {
614                if is_enemy(board, piece_pos, &Position::create(rank + 1, file - 1)) {
615                    positions.push(Position::create(rank + 1, file - 1));
616                }
617
618                rank = piece_pos.rank;
619                file = piece_pos.file;
620                break;
621            }
622
623            positions.push(Position::create(rank + 1, file - 1));
624
625            rank = rank + 1;
626            file = file - 1;
627
628            if rank == 7 || file == 0 {
629                rank = piece_pos.rank;
630                file = piece_pos.file;
631                break;
632            }
633        }
634    }
635
636    if rank < 7 && file < 7 {
637        loop {
638            if board.get(rank + 1, file + 1) != '-' {
639                if is_enemy(board, piece_pos, &Position::create(rank + 1, file + 1)) {
640                    positions.push(Position::create(rank + 1, file + 1));
641                }
642
643                rank = piece_pos.rank;
644                file = piece_pos.file;
645                break;
646            }
647
648            positions.push(Position::create(rank + 1, file + 1));
649
650            rank = rank + 1;
651            file = file + 1;
652
653            if rank == 7 || file == 7 {
654                rank = piece_pos.rank;
655                file = piece_pos.file;
656                break;
657            }
658        }
659    }
660
661    positions
662}
663
664// Exported Functions
665
666pub fn get_pawn_moves(game: &Game, pawn_pos: &Position) -> Vec<Position> {
667    let piece = get_piece_from_position(&game.board, &pawn_pos);
668
669    let mut positions: Vec<Position> = Vec::new();
670    println!("{:?}", pawn_pos);
671    if piece == 'p' {
672        if (game.board.get(pawn_pos.rank + 1,pawn_pos.file) == '-') {
673            if pawn_pos.rank == 1 && game.board.get(3,pawn_pos.file) == '-' {
674                positions.push(Position::create(3, pawn_pos.file)); // 2 step pawn move
675            }
676
677            positions.push(Position::create(pawn_pos.rank + 1, pawn_pos.file)); // 1 step pawn move
678
679            let capture_positions = get_pawn_capture_pos(&game.board, pawn_pos, 'p');
680
681            for capture in capture_positions {
682                positions.push(capture);
683            }
684
685            return positions;
686        }
687    } else if piece == 'P' {
688        if game.board.get(pawn_pos.rank - 1, pawn_pos.file) == '-' {
689            if pawn_pos.rank == 6 && game.board.get(4, pawn_pos.rank) == '-' {
690                positions.push(Position::create(4, pawn_pos.file)); // 2 step pawn move
691            }
692
693            positions.push(Position::create(pawn_pos.rank - 1, pawn_pos.file)); // 1 step pawn move
694
695            let capture_positions = get_pawn_capture_pos(&game.board, pawn_pos, 'P');
696
697            for capture in capture_positions {
698                positions.push(capture);
699            }
700
701            return positions;
702        }
703    }
704
705    positions
706}
707pub fn get_knight_moves(board: &Board, knight_pos: &Position) -> Vec<Position> {
708    let piece = get_piece_from_position(&board, &knight_pos);
709    let mut positions: Vec<Position> = Vec::new();
710    let rank = knight_pos.rank;
711    let file = knight_pos.file;
712
713    if piece == 'n' || piece == 'N' {
714        if rank > 1 {
715            if file > 0 {
716                positions.push(Position::create(rank - 2, file - 1));
717            }
718
719            if file < 7 {
720                positions.push(Position::create(rank - 2, file + 1));
721            }
722        }
723
724        if rank < 6 {
725            if file > 0 {
726                positions.push(Position::create(rank + 2, file - 1));
727            }
728
729            if file < 7 {
730                positions.push(Position::create(rank + 2, file + 1));
731            }
732        }
733
734        if file > 1 {
735            if rank > 0 {
736                positions.push(Position::create(rank - 1, file - 2));
737            }
738
739            if rank < 7 {
740                positions.push(Position::create(rank + 1, file - 2));
741            }
742        }
743
744        if file < 6 {
745            if rank > 0 {
746                positions.push(Position::create(rank - 1, file + 2));
747            }
748
749            if rank < 7 {
750                positions.push(Position::create(rank + 1, file + 2));
751            }
752        }
753
754        let mut valid_moves = Vec::new();
755
756        for position in positions {
757            let newRank = position.rank;
758            let newFile = position.file;
759
760            if (board.get(position.rank, position.file) == '-') {
761                valid_moves.push(position);
762            }
763
764            else if board.get(position.rank, position.file).is_lowercase() && board.get(knight_pos.rank, knight_pos.file).is_uppercase() {
765                valid_moves.push(position);
766            }
767
768            else if board.get(position.rank, position.file).is_uppercase() && board.get(knight_pos.rank, knight_pos.file).is_lowercase() {
769                valid_moves.push(position);
770            }
771        }
772
773        return valid_moves;
774    }
775
776    positions
777}
778
779pub fn get_bishop_moves(board: &Board, bishop_pos: &Position) -> Vec<Position> {
780    let piece = get_piece_from_position(&board, &bishop_pos);
781
782    if piece == 'b' || piece == 'B' {
783        return get_diagonal_moves(&board, &bishop_pos);
784    }
785
786    Vec::new()
787}
788
789pub fn get_queen_moves(board: &Board, queen_pos: &Position) -> Vec<Position> {
790    let piece = get_piece_from_position(&board, &queen_pos);
791    let mut positions = get_diagonal_moves(&board, &queen_pos);
792
793    if piece == 'q' || piece == 'Q' {
794        let horizontal_moves = get_horizontal_moves(&board, &queen_pos);
795        let vertical_moves = get_vertical_moves(&board, &queen_pos);
796
797        for h_move in horizontal_moves {
798            positions.push(h_move);
799        }
800
801        for v_move in vertical_moves {
802            positions.push(v_move);
803        }
804
805        return positions;
806    }
807
808    positions
809}
810
811pub fn get_king_moves(game: &Game, king_pos: &Position) -> Vec<Position> {
812    let piece = get_piece_from_position(&game.board, &king_pos);
813
814    if piece == 'k' || piece == 'K' {
815        let king_rank = king_pos.rank;
816        let king_file = king_pos.file;
817        let mut positions:Vec<Position> = Vec::new();
818
819        if king_rank > 0 {
820            if king_file > 0 {
821                positions.push(Position::create(king_rank - 1, king_file - 1));
822            }
823
824            if king_file < 7 {
825                positions.push(Position::create(king_rank - 1, king_file + 1));
826            }
827
828            positions.push(Position::create(king_rank - 1, king_file ));
829        }
830
831        if king_rank < 7 {
832            if king_file > 0 {
833                positions.push(Position::create(king_rank + 1, king_file - 1));
834            }
835
836            if king_file < 7 {
837                positions.push(Position::create(king_rank + 1, king_file + 1));
838            }
839
840            positions.push(Position::create(king_rank + 1, king_file ));
841        }
842
843        if king_file > 0 {
844            positions.push(Position::create(king_rank, king_file - 1));
845        }
846
847        if king_file < 7 {
848            positions.push(Position::create(king_rank, king_file + 1));
849        }
850
851        let mut valid_positions: Vec<Position>= Vec::new();
852        let castle_positions = get_castling_position(game);
853
854        for position in positions {
855            if game.board.get(position.rank, position.file) == '-' {
856                valid_positions.push(position)
857            }
858        }
859
860        for castle_position in castle_positions {
861            valid_positions.push(castle_position);
862        }
863
864        return valid_positions;
865    }
866
867    Vec::new()
868}
869
870pub fn get_rook_moves(board: &Board, rook_pos: &Position) -> Vec<Position> {
871    let piece = get_piece_from_position(&board, &rook_pos);
872    let mut positions = Vec::new();
873
874    if piece == 'r' || piece == 'R' {
875        let horizontal_moves = get_horizontal_moves(&board, &rook_pos);
876        let vertical_moves = get_vertical_moves(&board, &rook_pos);
877
878        for h_move in horizontal_moves {
879            positions.push(h_move);
880        }
881
882        for v_move in vertical_moves {
883            positions.push(v_move);
884        }
885
886        return positions;
887    }
888
889    positions
890}
891
892pub fn get_moves(game: &Game, position: &Position) -> Option<Vec<Position>> {
893    let square: char = game.board.get(position.rank, position.file);
894
895    if (square != '-') {
896        let moves = match square.to_ascii_lowercase() {
897            'p' => get_pawn_moves(&game, &position),
898            'r' => get_rook_moves(&game.board, &position),
899            'n' => get_knight_moves(&game.board, &position),
900            'b' => get_bishop_moves(&game.board, &position),
901            'q' => get_queen_moves(&game.board, &position),
902            'k' => get_king_moves(&game, &position),
903            _ => Vec::new(),
904        };
905
906        return Some(moves);
907    }
908
909    None
910}
911
912pub fn get_all_moves(game: Game) -> HashMap<Position, Vec<Position>> {
913    let mut legal_moves: HashMap<Position, Vec<Position>> = HashMap::new();
914
915    let mut row_index = 0;
916
917    for row in &game.board.board {
918        for (col, square) in row.iter().enumerate() {
919            if *square != '-' {
920                let piece = *square;
921                let position = Position::create(row_index, col);
922
923                let is_white = game.status == WHITE_TO_MOVE && piece.is_ascii_lowercase();
924                let is_black = game.status == BLACK_TO_MOVE && piece.is_ascii_uppercase();
925
926                if is_white || is_black {
927                    let moves = get_moves(&game, &position).unwrap();
928
929                    legal_moves.insert(Position::create(row_index, col), moves);
930                }
931            }
932        }
933
934        row_index += 1;
935    }
936
937    legal_moves
938}
939
940fn validate_moves(current_move: &Position, legal_moves: Vec<Position>) -> bool {
941    for legal_move in legal_moves {
942        if current_move.file == legal_move.file && current_move.rank == legal_move.rank {
943            return true;
944        }
945    }
946
947    false
948}
949
950pub fn make_move(game: &mut Game, start: &Position, end: &Position) -> Result<bool> {
951    let piece: char = game.board.get(start.rank, start.file);
952
953    if game.status == WHITE_TO_MOVE && piece.is_lowercase() {
954        return Err(Error::new(ErrorKind::Other, "Error: Cannot move black piece on white turn"))
955    }
956
957    if game.status == BLACK_TO_MOVE && piece.is_uppercase() {
958        return Err(Error::new(ErrorKind::Other, "Error: Cannot move white piece on black turn"))
959    }
960
961    let positions = get_moves(game, start).unwrap();
962
963    if validate_moves(end, positions) {
964        let isTargetEnemy = is_enemy(&game.board, start, end);
965
966        game.board.board[start.rank][start.file] = '-';
967        game.board.board[end.rank][end.file] = piece;
968
969        if isTargetEnemy {
970            game.current_move = CAPTURE;
971        }
972
973        else {
974            game.current_move = REGULAR;
975        }
976
977        if game.status == WHITE_TO_MOVE {
978            game.status = BLACK_TO_MOVE;
979        }
980
981        else if game.status == BLACK_TO_MOVE {
982            game.status = WHITE_TO_MOVE;
983        }
984
985        return Ok(true);
986    }
987
988    Err(Error::new(ErrorKind::Other, "Error: Move is invalid"))
989}
990
991pub fn run() {
992    let mut game = Game::new();
993
994    loop {
995        game.board.print();
996        println!("");
997
998        let mut input = String::new();
999
1000        println!("Enter position");
1001        stdin().read_line(&mut input).unwrap();
1002
1003        let nums: Vec<usize> = input.split_whitespace().map(|w| w.parse().unwrap()).collect();
1004        let startRow = nums[0];
1005        let startFile = nums[1];
1006
1007        let endRow = nums[2];
1008        let endFile = nums[3];
1009
1010        match make_move(&mut game, &Position::create(startRow, startFile), &Position::create(endRow, endFile)) {
1011            Ok(v) => println!("Success"),
1012            Err(e) => println!("{}", e)
1013        }
1014    }
1015}
1016
1017#[cfg(test)]
1018mod tests {
1019    use super::*;
1020
1021    #[test]
1022    fn test_perft_depth_1() {
1023        let board = Board::create();
1024        let nodes = board.perft(1, WHITE);
1025        assert_eq!(nodes, 20, "Perft Depth 1 failed: Expected 20 nodes, got {}", nodes);
1026    }
1027
1028    #[test]
1029    fn test_perft_depth_2() {
1030        let board = Board::create();
1031        let nodes = board.perft(2, WHITE);
1032        assert_eq!(nodes, 400, "Perft Depth 2 failed: Expected 400 nodes, got {}", nodes);
1033    }
1034
1035    #[test]
1036    fn test_perft_depth_3() {
1037        let board = Board::create();
1038        let nodes = board.perft(3, WHITE);
1039        assert_eq!(nodes, 8902, "Perft Depth 3 failed: Expected 8902 nodes, got {}", nodes);
1040    }
1041}