I have been quite interested recently with the new languages Go and rust. Go and rust have some very nice new features and their design and tooling reflects the standards of this days. So I have been hacking with go and rust.

I share this piece of rust code that solves sudoku, in the hope it will be useful to someone. It is not very much optimized as the algorithm is naive, but the purpose of this code was to get more comfortable with rust.


fn print_grid(g: [Option<i32>; 81]) {

    let mut cnt = 0;
    let mut line = 0;

    for &x in g.iter() {

        cnt = cnt + 1;

        match x {
            Some(i) => print!("{}", i),
            None => print!("_"),
        }

        if cnt == 9 {
            line = line + 1;
            println!("");
            cnt = 0;
            if line == 3 {
                line = 0;
                println!("");
            }
        } else if cnt % 3 == 0 {
            print!("   ");

        } else {
            print!(" ");
        }
    }
}

fn check_grid(g: [Option<i32>; 81]) -> bool {

    // check lines
    for x in 0..9 {
        let i = 9 * x;
        let val = [i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8];

        for v in 0..(val.len() - 1) {
            let valv = g[val[v]];
            if valv != None {
                for c in 1..val.len() {
                    let valc = g[val[c]];
                    if valc != None && val[v] != val[c] && valv == valc {
                        // println!("Block false at {}", x);
                        return false;
                    }
                }
            }
        }
    }

    // check columns
    for x in 0..9 {
        let val = [x, x + 9, x + 18, x + 27, x + 36, x + 45, x + 54, x + 63, x + 72];

        for v in 0..(val.len() - 1) {
            let valv = g[val[v]];
            if valv != None {
                for c in 1..val.len() {
                    let valc = g[val[c]];
                    if valc != None && val[v] != val[c] && valv == valc {
                        // println!("Block false at {}", x);
                        return false;
                    }
                }
            }
        }
    }

    // check blocks
    for x in 0..9 {
        let mut i = 3 * (x % 3);
        if x > 2 {
            i = i + 27;
        } else if x > 5 {
            i = i + 54;
        }

        let val = [i, i + 1, i + 2, i + 9, i + 10, i + 11, i + 18, i + 19, i + 20];

        for v in 0..(val.len() - 1) {
            let valv = g[val[v]];
            if valv != None {
                for c in 1..val.len() {
                    let valc = g[val[c]];
                    if valc != None && val[v] != val[c] && valv == valc {
                        // println!("Block false at {}", x);
                        return false;
                    }
                }
            }
        }
    }

    return true;
}

fn is_grid_complete(g: [Option<i32>; 81]) -> bool {
    let mut ret = true;
    for &x in g.iter() {
        match x {
            Some(_) => {}
            None => {
                ret = false;
                break;
            }
        }
    }
    return ret;
}

fn clone_grid(g: [Option<i32>; 81]) -> [Option<i32>; 81] {
    let mut new_g: [Option<i32>; 81] = [None; 81];
    for x in 0..g.len() {
        new_g[x] = g[x];
    }
    return new_g;
}


fn solve_grid(g: [Option<i32>; 81]) -> Option<[Option<i32>; 81]> {

    if is_grid_complete(g) {
        return Some(g);
    }

    for x in 0..g.len() {
        match g[x] {
            Some(_) => {}
            None => {

                let mut checked: [bool; 9] = [false; 9];

                for v in 1..10 {

                    let mut new_g = clone_grid(g);
                    new_g[x] = Some(v);

                    checked[(v - 1) as usize] = true;

                    if check_grid(new_g) {
                        match solve_grid(new_g) {
                            None => {
                                if checked == [true; 9] {
                                    // the path is a dead end
                                    return None;
                                } else {
                                    continue;
                                }
                            }

                            Some(gx) => return Some(gx),
                        }
                    }
                }

                if checked == [true; 9] {
                    // Detected a dead end
                    return None;
                }
            }
        }
    }

    return None;
}

fn parse_grid(grid_string: &str) -> [Option<i32>; 81] {
    let mut grid = [None; 81];

    let mut i = 0;
    for s in grid_string.split_whitespace() {
        match s {
            "_" => {}
            val => {
                grid[i] = Some(val.parse::<i32>().unwrap());
            }
        }
        i = i + 1;
    }

    return grid;
}

fn main() {
    let grid_string = r#"
            1 _ _   _ _ _   _ _ 3
            _ 4 _   _ _ 9   2 6 _
            _ _ _   7 _ _   _ 5 4

            _ _ _   1 7 _   9 _ _
            _ _ 2   _ _ _   6 _ _
            _ _ 3   _ 9 5   _ _ _

            2 7 _   _ _ 1   _ _ _
            _ 8 9   3 _ _   _ 7 _
            6 _ _   _ _ _   _ _ 2"#;

    let grid: [Option<i32>; 81] = parse_grid(grid_string);

    print_grid(grid);

    match solve_grid(grid) {
        Some(g) => {
            println!("Grid complete !");
            print_grid(g)
        }
        None => println!("Couldn't solve the sudoku"),
    }

}

You can just, given you have installed rust.

cargo build