rust - Two ways to count moves of a chess horse at any point on the chessboard

Briefly

There are two more methods of path calculation, one is implemented by iteration, the other is implemented by queue.

Iterative scheme

The traversal starts from the starting point, and there are eight next_points around a point. After removing the points beyond the chessboard, the selected traversed point is satisfied, the point is not traversed or the number of steps required to traverse the path before this point is greater than the number of steps for the current path. .

Queue scheme

Push the starting point to the queue. When the queue is not empty, pop a point from the queue each time through the loop. If those eight next_points satisfy the conditions: 1. The point does not exceed the board; 2. The point is not traversed or the number of steps required to traverse the path before this point is greater than the number of steps for the current path.

accomplish

lib.rs

pub mod checkerboard;
pub mod checkerboardq;

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn for_horse() {
        let mut board = super::checkerboard::CheckerBoard::new();

        board.calculate((2,3));
        println!("{}",board.get_count());
        assert_eq!(board.get(&(2,3)),0);
    }

    #[test]
    fn for_horse_query() {
        let mut board = super::checkerboardq::CheckerBoardQ::new();
        board.calculate((2,3));
        board.print_board();
        println!("{}",board.get_count());
        assert_eq!(board.get(&(2,3)),0);
    }
}

checkerboard.rs

//迭代方案
#[derive(Debug)]
pub struct CheckerBoard {
    board: Vec<Vec<isize>>,//存放路径对应步数
    count:usize,//记录遍历次数
}

impl CheckerBoard {
    // add code here
    pub fn new() -> CheckerBoard {
        let mut board:Vec<Vec<isize>> = Vec::with_capacity(10);
        let count:usize=0;
        for _ in 0..10 {
            board.push(vec![-1;9]);
        }
        CheckerBoard{
            board,
            count,
        }
    }

    pub fn get(&self,point:&(usize,usize)) -> isize{
        self.board[point.0][point.1]
    }

    pub fn set(&mut self,point:&(usize,usize),length:isize){
        self.board[point.0][point.1] = length;
    }

    pub fn print_board(&self){
        for i in 0..10{
            println!("{:?}",self.board[i]);
        }
    }
    pub fn calculate(&mut self,point:(usize,usize)){
        self.travel(point,-1);
    }
    fn travel(&mut self,point:(usize,usize),length:isize){
        if point.0>9 || point.1>8{
            return;
        }
        self.count = self.count + 1;
        let _length = self.get(&point);
        if _length>=0 && _length<=length+1{
            return;
        }
        self.count = self.count+1;
        let _length = length+1;
        self.set(&point,length+1);
        //以下代码这么丑是为了减少bool计算次数
        if point.0>1{
            if point.1>1{
                self.travel((point.0-1,point.1-2),_length);
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0-2,point.1-1),_length);
                self.travel((point.0-2,point.1+1),_length);
                self.travel((point.0+1,point.1-2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }else if point.1>0 {                
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0-2,point.1-1),_length);
                self.travel((point.0-2,point.1+1),_length);
                self.travel((point.0+2,point.1-1),_length);
            }
        }else if point.0>0{
            if point.1>1{
                self.travel((point.0-1,point.1-2),_length);
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0+1,point.1-2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }else if point.1>0{
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }
        }
        self.travel((point.0+1,point.1+2),_length);
        self.travel((point.0+2,point.1+1),_length);
        
    }

    pub fn get_count(&self) -> usize{
        self.count
    }
    
}

checkerboardq.rs


#[derive(Debug)]
struct Queue<T> {
    qdata: Vec<T>,
}

impl <T> Queue<T> {
    pub fn new() -> Self {
        Queue{qdata: Vec::new()}
    }

    pub fn len(&self)->usize{
        self.qdata.len()
    }

    pub fn push(&mut self, item:T) {
        self.qdata.push(item);
    }

    pub fn pop(&mut self) -> T{
        self.qdata.remove(0)
    }
}

#[derive(Debug)]
pub struct CheckerBoardQ {
    board: Vec<Vec<isize>>,
    count:usize,
    point_query:Queue<(isize,isize)>,
}
impl CheckerBoardQ {
    // add code here
    pub fn new() -> CheckerBoardQ {
        let mut board:Vec<Vec<isize>> = Vec::with_capacity(10);
        let count:usize=0;
        let point_query:Queue<(isize,isize)> = Queue::new();
        for _ in 0..10 {
            board.push(vec![-1;9]);
        }
        CheckerBoardQ{
            board,
            count,
            point_query,
        }
    }
    fn set(&mut self,point:&(isize,isize),length:isize){
        self.board[point.0 as usize ][point.1 as usize] = length;
    }
    pub fn get(&self,point:&(usize,usize)) -> isize{
        self.board[point.0][point.1]
    }
    fn _get(&self,point:&(isize,isize)) -> isize{
        self.board[point.0 as usize][point.1 as usize]
    }
    pub fn print_board(&self){
        for i in 0..10{
            println!("{:?}",self.board[i]);
        }
    }
    pub fn calculate(&mut self,point:(usize,usize)){
        assert!(point.0<10 && point.1<9);
        let i_point = (point.0 as isize,point.1 as isize);
        self.set(&i_point,0);
        self.point_query.push(i_point);
        while self.point_query.len()>0 {
            self.count = self.count + 1;
            let _point = self.point_query.pop();
            self.travel(_point);
        }

        
    }
    fn update_query(&mut self,length:isize,next_point:(isize,isize)){
        let length = length+1;
        let _length = self._get(&next_point);
        if _length<0 || _length>length {
            self.set(&next_point,length);
            self.point_query.push(next_point);
        }
    }
    fn travel(&mut self,point:(isize,isize))  {
        //以下代码看上去简洁许多,但实际bool计算次数较上面方案多
        let mut _next_point_query = vec![
            (point.0-1,point.1-2),(point.0-1,point.1+2),
            (point.0+1,point.1-2),(point.0+1,point.1+2),
            (point.0-2,point.1-1),(point.0-2,point.1+1),
            (point.0+2,point.1-1),(point.0+2,point.1+1)].into_iter();
        let length = self._get(&point);
        while let Some(_point) = _next_point_query.next() {
            if _point.0<0||_point.0>9||_point.1<0||_point.1>8{
                continue;
            }
            self.update_query(length,_point);
        }
    }
    pub fn get_count(&self) -> usize {
        self.count
    }
    
}

Test Results

cargo test -- --nocapture --test-threads=1

It is obvious from the test results that the performance of the iterative scheme is far lower than that of the queue scheme. This is actually very easy to understand. Although the core judgment conditions of the two schemes are the same, the iterative scheme starts from one point and traverses all the paths that satisfy the core judgment conditions. The queue scheme only advances one step at a time, and can end a large part of the traversal path that will definitely be eliminated in advance.

summary

There is not much rust syntax designed in the above code. What is more interesting is the algorithm for judging whether the eight next_points are on the chessboard, which should be both elegant and efficient. The two algorithms I use are basically the extremes of this situation. The first code is a lot, but the average number of bool calculations for traversing a point is 3.7. The second is obviously that each next_point point is judged once, which is 8.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326928484&siteId=291194637