Advertisement
loloof64

Rust panicking on mutable borrow of RefCell

Dec 1st, 2018
380
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 14.96 KB | None | 0 0
  1. use std::collections::HashMap;
  2. use std::cell::RefCell;
  3. use std::rc::Rc;
  4. use gtk::prelude::*;
  5. use gdk::prelude::*;
  6. use gdk::{EventMask, EventType};
  7. use gtk::{DrawingArea, Dialog, Orientation, Box as GBox, Button, Image};
  8. use gdk_pixbuf::Pixbuf;
  9. use cairo::Context;
  10. use cairo::enums::{FontSlant, FontWeight};
  11. use shakmaty::{Piece, Role, Chess};
  12. use chess_position_trainer::graphic::load_image;
  13. use chess_position_trainer::logic::chessgame::ChessGame;
  14.  
  15. #[derive(Clone)]
  16. pub struct ChessBoard
  17. {
  18.     drawing_area: DrawingArea,
  19.     reversed: bool,
  20.     logic: ChessGame,
  21.     cells_size: u32,
  22.     moved_piece: Option<MovedPiece>,
  23.     pieces_images: HashMap<char, Pixbuf>,
  24. }
  25.  
  26. #[derive(Clone, Debug)]
  27. struct MovedPiece
  28. {
  29.     piece_type: Piece,
  30.     coords_x: f64,
  31.     coords_y: f64,
  32.     start_file: u8,
  33.     start_rank: u8
  34. }
  35.  
  36. enum PromotionType
  37. {
  38.     QUEEN = 1,
  39.     ROOK = 2,
  40.     BISHOP = 4,
  41.     KNIGHT = 8
  42. }
  43.  
  44. impl MovedPiece {
  45.     fn translate_to(&mut self, x: f64, y: f64)
  46.     {
  47.         self.coords_x = x;
  48.         self.coords_y = y;
  49.     }
  50. }
  51.  
  52. impl ChessBoard
  53. {
  54.     pub fn new_from_default() -> Result<Rc<RefCell<ChessBoard>>, String>
  55.     {
  56.         ChessBoard::new(
  57.             "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
  58.         )
  59.     }
  60.  
  61.     pub fn new(initial_position: &str) -> Result<Rc<RefCell<ChessBoard>>, String>
  62.     {
  63.         ChessBoard::get_chessboard(
  64.             initial_position,
  65.         )
  66.     }
  67.  
  68.     pub fn reverse(&mut self)
  69.     {
  70.         self.reversed = ! self.reversed;
  71.         self.drawing_area.queue_draw();
  72.     }
  73.  
  74.     pub fn get_drawing_area(&self) -> &DrawingArea
  75.     {
  76.         &self.drawing_area
  77.     }
  78.    
  79.     fn load_pieces_images(size: u32) -> HashMap<char, Pixbuf> {
  80.         let mut images = HashMap::new();
  81.  
  82.         let params: Vec<(char, &'static [u8], &str)> = vec![
  83.            ('P', include_bytes!("../../resources/Chess_pl.png"), "Failed to get white pawn image !"),
  84.            ('N', include_bytes!("../../resources/Chess_nl.png"), "Failed to get white knight image !"),
  85.            ('B', include_bytes!("../../resources/Chess_bl.png"), "Failed to get white bishop image !"),
  86.            ('R', include_bytes!("../../resources/Chess_rl.png"), "Failed to get white rook image !"),
  87.            ('Q', include_bytes!("../../resources/Chess_ql.png"), "Failed to get white queen image !"),
  88.            ('K', include_bytes!("../../resources/Chess_kl.png"), "Failed to get white king image !"),
  89.  
  90.            ('p', include_bytes!("../../resources/Chess_pd.png"), "Failed to get black pawn image !"),
  91.            ('n', include_bytes!("../../resources/Chess_nd.png"), "Failed to get black knight image !"),
  92.            ('b', include_bytes!("../../resources/Chess_bd.png"), "Failed to get black bishop image !"),
  93.            ('r', include_bytes!("../../resources/Chess_rd.png"), "Failed to get black rook image !"),
  94.            ('q', include_bytes!("../../resources/Chess_qd.png"), "Failed to get black queen image !"),
  95.            ('k', include_bytes!("../../resources/Chess_kd.png"), "Failed to get black king image !"),
  96.        ];
  97.  
  98.        params.iter().for_each(|(piece_code, image_binary, error_string)| {
  99.            images.insert(
  100.                *piece_code,
  101.                load_image(
  102.                    image_binary,
  103.                    size as i32
  104.                ).expect(error_string)
  105.            );
  106.  
  107.        });
  108.  
  109.        images
  110.    }
  111.  
  112.    fn get_chessboard(initial_position: &str) -> Result<Rc<RefCell<ChessBoard>>, String>
  113.    {
  114.        let drawing_area = DrawingArea::new();
  115.        drawing_area.add_events((
  116.            EventMask::BUTTON1_MOTION_MASK.bits() |
  117.            EventMask::BUTTON_PRESS_MASK.bits() |
  118.            EventMask::BUTTON_RELEASE_MASK.bits()
  119.        ) as i32);
  120.  
  121.        let logic = ChessGame::new_from_fen(initial_position);
  122.  
  123.        match logic {
  124.            Some(game_logic) => {
  125.                let pieces_images = ChessBoard::load_pieces_images((50f64 * 0.8) as u32);
  126.  
  127.                let chess_board = ChessBoard {
  128.                    drawing_area,
  129.                    reversed: false,
  130.                    logic: game_logic,
  131.                    cells_size: 50u32,
  132.                    moved_piece: None,
  133.                    pieces_images,
  134.                };
  135.  
  136.                let chess_board_ref = Rc::new(RefCell::new(chess_board));
  137.  
  138.                let chess_board_ref_2 = chess_board_ref.clone();
  139.                chess_board_ref.borrow().drawing_area.connect_draw(move |_drawing_area, cr|{
  140.                    chess_board_ref_2.borrow().paint(cr);
  141.                    Inhibit(false)
  142.                });
  143.  
  144.                let chess_board_ref_3 = chess_board_ref.clone();
  145.                chess_board_ref.borrow().drawing_area.connect_event(move |_self, event| {
  146.                    let coords = event.get_coords().expect("Failed to get mouse coordinates !");
  147.                    
  148.                    match event.get_event_type() {
  149.                        EventType::ButtonPress => chess_board_ref_3.borrow_mut().handle_mouse_pressed(coords),
  150.                        EventType::ButtonRelease => chess_board_ref_3.borrow_mut().handle_mouse_released(coords),
  151.                        EventType::MotionNotify => chess_board_ref_3.borrow_mut().handle_mouse_moved(coords),
  152.                    _ => {}
  153.                    }
  154.                    Inhibit(false)
  155.                });
  156.  
  157.                Ok(chess_board_ref)
  158.            },
  159.            None => Err(format!("Bad FEN {} !", initial_position))
  160.        }
  161.    }
  162.  
  163.    fn handle_mouse_pressed(&mut self, coords: (f64, f64)){
  164.        let cells_size = self.cells_size as f64;
  165.        let mut cell_coords = (
  166.            ((coords.0 - (cells_size * 0.5)) / cells_size) as i32,
  167.            7 - (((coords.1 - (cells_size * 0.5)) / cells_size) as i32),
  168.        );
  169.        if self.reversed {
  170.            cell_coords = (7-cell_coords.0, 7-cell_coords.1);
  171.        }
  172.  
  173.        let (coords_x, coords_y) = coords;
  174.        let moved_piece = self.logic.piece_at_cell(cell_coords.0 as i8, cell_coords.1 as i8);
  175.        if let Some(piece_type) = moved_piece {
  176.            self.moved_piece = Some(MovedPiece{
  177.                coords_x,
  178.                coords_y,
  179.                piece_type,
  180.                start_file: cell_coords.0 as u8,
  181.                start_rank: cell_coords.1 as u8,
  182.            });
  183.  
  184.            self.drawing_area.queue_draw();
  185.        }
  186.    }
  187.  
  188.    fn handle_mouse_released(&mut self, coords: (f64, f64)){
  189.        let cells_size = self.cells_size as f64;
  190.        let mut cell_coords = (
  191.            ((coords.0 - (cells_size * 0.5)) / cells_size) as u8,
  192.            7 - (((coords.1 - (cells_size * 0.5)) / cells_size) as u8),
  193.        );
  194.        if self.reversed {
  195.            cell_coords = ((7-cell_coords.0) as u8, (7-cell_coords.1) as u8);
  196.        }
  197.  
  198.        if let Some(ref moved_piece) = self.moved_piece {
  199.            let start_cell = (moved_piece.start_file, moved_piece.start_rank);
  200.            let end_cell = cell_coords;
  201.  
  202.            if self.logic.is_legal_move::<Chess>(start_cell, end_cell) {
  203.                if self.logic.is_promotion_move(start_cell, end_cell) {
  204.                    let selected_role = self.open_promotion_selector();
  205.                    self.logic.do_move::<Chess>(start_cell, end_cell, Some(selected_role));
  206.                    self.drawing_area.queue_draw();
  207.                }
  208.                else {
  209.                    self.logic.do_move::<Chess>(start_cell, end_cell, None);
  210.                    self.drawing_area.queue_draw();
  211.                }
  212.            }
  213.        }
  214.  
  215.        self.moved_piece = None;
  216.        self.drawing_area.queue_draw();
  217.    }
  218.  
  219.    fn handle_mouse_moved(&mut self, coords: (f64, f64)) {
  220.        if let Some(ref mut move_spec) = self.moved_piece {
  221.            move_spec.translate_to(coords.0, coords.1);
  222.            self.drawing_area.queue_draw();
  223.  
  224.        }
  225.    }
  226.  
  227.    fn open_promotion_selector(&self) -> Role {
  228.        let dialog = Dialog::new();
  229.        dialog.set_title("Select your promotion piece");
  230.        dialog.set_modal(true);
  231.  
  232.        let dialog_ref = Rc::new(dialog);
  233.  
  234.        let is_white_turn = self.logic.is_white_turn();
  235.        let queen_image = if is_white_turn {
  236.            self.pieces_images.get(&'Q').expect("Failed to get white queen image")
  237.        } else {
  238.            self.pieces_images.get(&'q').expect("Failed to get white queen image")
  239.        };
  240.        let queen_image = Image::new_from_pixbuf(queen_image);
  241.  
  242.        let queen_button = Button::new();
  243.        queen_button.set_image(&queen_image);
  244.        queen_button.connect_clicked({
  245.            let dialog_ref = dialog_ref.clone();
  246.            move |_button| {
  247.                dialog_ref.response(PromotionType::QUEEN as i32);
  248.            }
  249.        });
  250.  
  251.        let buttons_box = GBox::new(Orientation::Horizontal, 10);
  252.        buttons_box.pack_start(
  253.            &queen_button,
  254.            true,
  255.            true,
  256.            0
  257.        );
  258.  
  259.        dialog_ref.get_content_area().pack_start(
  260.            &buttons_box,
  261.            true,
  262.            true,
  263.            10
  264.        );
  265.  
  266.        let response = dialog_ref.run();
  267.        if response == PromotionType::QUEEN as i32 { Role::Queen }
  268.                        else if response == PromotionType::ROOK as i32 { Role::Rook }
  269.                        else if response == PromotionType::BISHOP as i32 { Role::Bishop }
  270.                        else if response == PromotionType::KNIGHT as i32 { Role::Knight }
  271.                        else { Role::Queen }
  272.  
  273.  
  274.    }
  275.  
  276.    fn paint(&self, cr: &Context){
  277.        self.draw_background(cr);
  278.        self.draw_cells(cr);
  279.        self.draw_pieces(cr);
  280.        self.draw_moved_piece(cr);
  281.        self.draw_coordinates(cr);
  282.        self.draw_player_turn(cr);
  283.    }
  284.  
  285.    fn draw_background(&self, cr: &Context)
  286.    {
  287.        let green_color = [60.0/255.0, 204.0/255.0, 100.0/255.0];
  288.        cr.set_source_rgb(
  289.            green_color[0],
  290.            green_color[1],
  291.            green_color[2],
  292.        );
  293.        cr.paint();
  294.    }
  295.  
  296.    fn draw_cells(&self, cr: &Context)
  297.    {
  298.        (0..8).for_each(|rank| {
  299.            (0..8).for_each(|file| {
  300.                let white_cell_color = [255.0/255.0, 255.0/255.0, 179.0/255.0];
  301.                let black_cell_color = [153.0/255.0, 102.0/255.0, 51.0/255.0];
  302.  
  303.                let is_white_cell = (file + rank) % 2 == 0;
  304.                let cell_color = if is_white_cell {white_cell_color} else {black_cell_color};
  305.  
  306.                let rect_x = (self.cells_size as f64) * (0.5 + (file as f64));
  307.                let rect_y = (self.cells_size as f64) * (0.5 + (rank as f64));
  308.                let rect_size = self.cells_size as f64;
  309.  
  310.                cr.rectangle(
  311.                    rect_x,
  312.                    rect_y,
  313.                    rect_size,
  314.                    rect_size,
  315.                );
  316.                cr.set_source_rgb(
  317.                    cell_color[0],
  318.                    cell_color[1],
  319.                    cell_color[2],
  320.                );
  321.                cr.fill();
  322.            });
  323.        });
  324.    }
  325.  
  326.    fn draw_pieces(&self, cr: &Context)
  327.    {
  328.        (0..64).for_each(|index| {
  329.  
  330.            let file = index % 8;
  331.            let rank = index / 8;
  332.                
  333.            let real_file = (if self.reversed { 7-file } else { file }) as u8;
  334.            let real_rank = (if self.reversed { 7-rank } else { rank }) as u8;
  335.  
  336.            if let Some(piece) = self.logic.piece_at_cell(real_file as i8, real_rank as i8) {
  337.                let not_moved_piece = match self.moved_piece {
  338.                    None => true,
  339.                    Some(ref moved_piece) => moved_piece.start_file != real_file || moved_piece.start_rank != real_rank
  340.                };
  341.  
  342.                if not_moved_piece {
  343.                        let image = self.pieces_images.get(&piece.char()).expect("Failed to get piece image !");
  344.                        let location_x = (self.cells_size as f64) * (file as f64 + 0.5 + 0.1);
  345.                        let location_y = (self.cells_size as f64) * ((7.0-rank as f64) + 0.5 + 0.1);
  346.                        cr.set_source_pixbuf(
  347.                            &image,
  348.                            location_x,
  349.                            location_y
  350.                        );
  351.                        cr.paint();  
  352.                }
  353.            }
  354.  
  355.        });
  356.    }
  357.  
  358.    fn draw_moved_piece(&self, cr: &Context)
  359.    {
  360.        if let Some(ref moved_piece) = self.moved_piece {
  361.            let piece_pointer_x = moved_piece.coords_x - (self.cells_size as f64) * 0.4;
  362.            let piece_pointer_y = moved_piece.coords_y - (self.cells_size as f64) * 0.4;
  363.            let image = self.pieces_images.get(&moved_piece.piece_type.char()).expect("Failed to get moved piece image !");
  364.  
  365.            cr.set_source_pixbuf(
  366.                &image,
  367.                piece_pointer_x,
  368.                piece_pointer_y
  369.            );
  370.            cr.paint();
  371.        }
  372.    }
  373.  
  374.    fn draw_coordinates(&self, cr: &Context)
  375.    {
  376.        let files = ["A", "B", "C", "D", "E", "F", "G", "H"];
  377.        let ranks = ["8", "7", "6", "5", "4", "3", "2", "1"];
  378.  
  379.        cr.set_source_rgb(0.2, 0.4, 1.0);
  380.        cr.select_font_face(
  381.            "Sans Serif",
  382.            FontSlant::Normal,
  383.            FontWeight::Bold
  384.        );
  385.        cr.set_font_size((self.cells_size as f64) * 0.38);
  386.        
  387.        (0..8).for_each(|file_index| {
  388.            let real_file_index = if self.reversed { 7 - file_index } else { file_index };
  389.  
  390.            let letter = files[file_index];
  391.            let letter_x = (self.cells_size as f64) * (0.9 + (real_file_index as f64));
  392.            let letter_y_top = (self.cells_size as f64) * 0.4;
  393.            let letter_y_bottom = (self.cells_size as f64) * 8.9;
  394.  
  395.            cr.move_to(letter_x, letter_y_top);
  396.            cr.show_text(letter);
  397.  
  398.            cr.move_to(letter_x, letter_y_bottom);
  399.            cr.show_text(letter);
  400.        });
  401.  
  402.        (0..8).for_each(|rank_index| {
  403.            let real_rank_index = if self.reversed { 7 - rank_index } else { rank_index };
  404.  
  405.            let letter = ranks[rank_index];
  406.            let letter_y = (self.cells_size as f64) * (1.2 + (real_rank_index as f64));
  407.            let letter_x_left = (self.cells_size as f64) * 0.1;
  408.            let letter_x_right = (self.cells_size as f64) * 8.6;
  409.  
  410.            cr.move_to(letter_x_left, letter_y);
  411.            cr.show_text(letter);
  412.  
  413.            cr.move_to(letter_x_right, letter_y);
  414.            cr.show_text(letter);
  415.        });
  416.    }
  417.  
  418.    fn draw_player_turn(&self, cr: &Context)
  419.    {
  420.        let color = if self.logic.is_white_turn() { [1.0, 1.0, 1.0] } else { [0.0, 0.0, 0.0] };
  421.        let center = (self.cells_size as f64) * 8.75;
  422.        let radius = (self.cells_size as f64) * 0.25;
  423.        cr.arc(center, center, radius, 0.0, 2.0 * std::f64::consts::PI);
  424.        cr.set_source_rgb(
  425.            color[0],
  426.            color[1],
  427.            color[2],
  428.        );
  429.        cr.fill();
  430.    }
  431. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement