HomeAboutArticles

Sudoku solver in rust

This is traditional backtracking: Try all possibilities, but give up the moment it is obviously wrong.

The board is read from a file. Further down you will find 2 puzzles you can put in files.

sudoku.rs

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::env;
use std::process;

struct Board {
  b: [[char;9];9],
}

impl Board {
  pub fn new() -> Board {
    Board { b: [['.';9];9] }
  } // end new

  fn okrow(&self, r: usize,val: char) -> bool {
    for i in 0..9 {
      if self.b[r][i]==val {
        return false;
      }
    }
    true
  } // end okrow

  fn okcol(&self, c: usize,val: char) -> bool {
    for i in 0..9 {
      if self.b[i][c]==val {
        return false;
      }
    }
    return true;
  } // end okcol

  fn oksquare(&self, r: usize,c: usize,val: char) -> bool {
    let corner: [usize;9] = [0,0,0,3,3,3,6,6,6];
    let top = corner[r];
    let left = corner[c];
    for i in left..left+3 {
      for j in top..top+3 {
        if self.b[j][i]==val {
          return false;
        }
      }
    }
    return true;
  } // end oksquare

  pub fn print(&self) {
    for x in 0..9 {
      for y in 0..9 {
        print!("{}",self.b[x][y]);
        if (y+1) % 3 == 0 { print!(" "); }
      }
      println!();
      if (x+1) % 3 == 0 { println!(); }
    }
  } // end p

  pub fn readfile(&mut self,filename: String) {
    let f = File::open(filename);
    match f {
      Ok(file) => {
        let reader = BufReader::new(file);

        let mut x = 0;
        for line in reader.lines() {
          let line = line.unwrap(); // Ignore errors.
          let mut y = 0;
          for char in line.chars() {
            if char!=' '  {
              self.b[x][y] = char;
              y += 1;
            }
          }
          if y>0 {
            x += 1;
          }
        }
        },
      Err(_why) => {
        println!("File not found");
        process::exit(1);
      }
    }
  } // end readfile

  pub fn tryit(&mut self,r: usize,c: usize) {
    if r>=9 {
      println!("Solution");
      self.print();
    } else {
      let c1 = if c==9-1 { 0   } else { c+1 };
      let r1 = if c==9-1 { r+1 } else { r }; 
      if self.b[r][c]=='.' {
        for val in ['1','2','3','4','5','6','7','8','9'].iter() {
          if self.okrow(r,*val) && 
             self.okcol(c,*val) && 
             self.oksquare(r,c,*val) {
            self.b[r][c]=*val;
            self.tryit(r1,c1);
            self.b[r][c]='.';
          }
        }
      } else {
        self.tryit(r1,c1);
      }
    }
  } // end tryit


} // end impl BoardStruct


fn main() {
  println!("Hello Sudoku!");

  let filename;

  let args: Vec<_> = env::args().collect();
  if args.len() == 2 {
    filename = args[1].to_string();
  } else if args.len() == 1{
    println!("Missing file argument");
    process::exit(1);
  } else {
    println!("Too many arguments");
    process::exit(1);
  }

  println!("Opening {}",filename);

  let mut board = Board::new();
  board.readfile(filename);
  board.tryit(0,0);
}

Compile and run:

rustc sudoku.rs
./sudoku sudoku1.txt

sudoku1.txt

29..4.8..
....9.2.1
...7...3.
...6..72.
4.1...6.8
.52..4...
.6...7...
7.5.6....
..9.8..76

sudoku2.txt

3.1...8.4
.468.13..
8.74.3..2
61..8...9
4896.2...
73.....86
5.47.8...
.731.5648
1.8...9..

Speed?

It is faster than the lua-version, even when it has to read the file first:

real    0m0.009s
user    0m0.005s
sys     0m0.004s