sudoku.rs 8.9 KB
Newer Older
1
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12
// ignore-pretty very bad with line comments

13
#![allow(non_snake_case)]
D
Daniel Micay 已提交
14

A
Alex Crichton 已提交
15 16
use std::io;
use std::io::stdio::StdReader;
A
Alex Crichton 已提交
17
use std::io::BufferedReader;
18
use std::num::Int;
19
use std::os;
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

// Computes a single solution to a given 9x9 sudoku
//
// Call with "-" to read input sudoku from stdin
//
// The expected line-based format is:
//
// 9,9
// <row>,<column>,<color>
// ...
//
// Row and column are 0-based (i.e. <= 8) and color is 1-based (>=1,<=9).
// A color of 0 indicates an empty field.
//
// If called without arguments, sudoku solves a built-in example sudoku
//

// internal type of sudoku grids
38
type grid = Vec<Vec<u8> > ;
39

40 41 42 43
struct Sudoku {
    grid: grid
}

44
impl Sudoku {
45
    pub fn new(g: grid) -> Sudoku {
46 47 48
        return Sudoku { grid: g }
    }

49
    pub fn from_vec(vec: &[[u8, ..9], ..9]) -> Sudoku {
50 51
        let g = Vec::from_fn(9u, |i| {
            Vec::from_fn(9u, |j| { vec[i][j] })
52
        });
53 54 55 56
        return Sudoku::new(g)
    }

    pub fn equal(&self, other: &Sudoku) -> bool {
D
Daniel Micay 已提交
57 58
        for row in range(0u8, 9u8) {
            for col in range(0u8, 9u8) {
59 60
                if self.grid[row as uint][col as uint] !=
                        other.grid[row as uint][col as uint] {
61 62 63 64 65 66 67
                    return false;
                }
            }
        }
        return true;
    }

A
Alex Crichton 已提交
68
    pub fn read(mut reader: BufferedReader<StdReader>) -> Sudoku {
69
        /* assert first line is exactly "9,9" */
R
Richo Healey 已提交
70
        assert!(reader.read_line().unwrap() == "9,9".to_string());
71

72
        let mut g = Vec::from_fn(10u, { |_i| vec!(0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8) });
73
        for line in reader.lines() {
74
            let line = line.unwrap();
75 76 77 78
            let comps: Vec<&str> = line.as_slice()
                                       .trim()
                                       .split(',')
                                       .collect();
79

Y
Youngmin Yoo 已提交
80
            if comps.len() == 3u {
81 82
                let row     = from_str::<uint>(comps[0]).unwrap() as u8;
                let col     = from_str::<uint>(comps[1]).unwrap() as u8;
83
                g[row as uint][col as uint] =
84
                    from_str::<uint>(comps[2]).unwrap() as u8;
85 86
            }
            else {
S
Steve Klabnik 已提交
87
                panic!("Invalid sudoku file");
88 89 90 91 92
            }
        }
        return Sudoku::new(g)
    }

P
Patrick Walton 已提交
93
    pub fn write(&self, writer: &mut io::Writer) {
D
Daniel Micay 已提交
94
        for row in range(0u8, 9u8) {
95
            write!(writer, "{}", self.grid[row as uint][0]);
D
Daniel Micay 已提交
96
            for col in range(1u8, 9u8) {
97
                write!(writer, " {}", self.grid[row as uint][col as uint]);
98
            }
A
Alex Crichton 已提交
99
            write!(writer, "\n");
100 101 102 103 104
         }
    }

    // solve sudoku grid
    pub fn solve(&mut self) {
105
        let mut work: Vec<(u8, u8)> = Vec::new(); /* queue of uncolored fields */
D
Daniel Micay 已提交
106 107
        for row in range(0u8, 9u8) {
            for col in range(0u8, 9u8) {
108
                let color = self.grid[row as uint][col as uint];
109 110 111
                if color == 0u8 {
                    work.push((row, col));
                }
112 113 114 115
            }
        }

        let mut ptr = 0u;
Y
Youngmin Yoo 已提交
116
        let end = work.len();
H
Huon Wilson 已提交
117
        while ptr < end {
118
            let (row, col) = work[ptr];
119
            // is there another color to try?
120
            let the_color = self.grid[row as uint][col as uint] +
121 122
                                (1 as u8);
            if self.next_color(row, col, the_color) {
123 124 125 126
                //  yes: advance work list
                ptr = ptr + 1u;
            } else {
                // no: redo this field aft recoloring pred; unless there is none
S
Steve Klabnik 已提交
127
                if ptr == 0u { panic!("No solution found for this sudoku"); }
128 129
                ptr = ptr - 1u;
            }
130 131 132
        }
    }

133
    fn next_color(&mut self, row: u8, col: u8, start_color: u8) -> bool {
134 135
        if start_color < 10u8 {
            // colors not yet used
136
            let mut avail = box Colors::new(start_color);
137 138

            // drop colors already in use in neighbourhood
139
            self.drop_colors(&mut *avail, row, col);
140 141

            // find first remaining color that is available
142
            let next = avail.next();
143
            self.grid[row as uint][col as uint] = next;
144
            return 0u8 != next;
145
        }
146
        self.grid[row as uint][col as uint] = 0u8;
B
Brian Anderson 已提交
147
        return false;
148 149 150
    }

    // find colors available in neighbourhood of (row, col)
151
    fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) {
D
Daniel Micay 已提交
152
        for idx in range(0u8, 9u8) {
153 154 155 156
            /* check same column fields */
            avail.remove(self.grid[idx as uint][col as uint]);
            /* check same row fields */
            avail.remove(self.grid[row as uint][idx as uint]);
157 158 159 160 161
        }

        // check same block fields
        let row0 = (row / 3u8) * 3u8;
        let col0 = (col / 3u8) * 3u8;
D
Daniel Micay 已提交
162 163
        for alt_row in range(row0, row0 + 3u8) {
            for alt_col in range(col0, col0 + 3u8) {
164
                avail.remove(self.grid[alt_row as uint][alt_col as uint]);
165
            }
166 167
        }
    }
168 169 170 171 172
}

// Stores available colors as simple bitfield, bit 0 is always unset
struct Colors(u16);

173
static HEADS: u16 = (1u16 << 10) - 1; /* bits 9..0 */
174 175

impl Colors {
176
    fn new(start_color: u8) -> Colors {
177
        // Sets bits 9..start_color
178
        let tails = !0u16 << start_color as uint;
179
        return Colors(HEADS & tails);
180
    }
181

182
    fn next(&self) -> u8 {
183 184
        let Colors(c) = *self;
        let val = c & HEADS;
185 186
        if (0u16 == val) {
            return 0u8;
187
        } else {
188
            return val.trailing_zeros() as u8
189 190 191
        }
    }

192 193
    fn remove(&mut self, color: u8) {
        if color != 0u8 {
194
            let Colors(val) = *self;
195
            let mask = !(1u16 << color as uint);
196
            *self    = Colors(val & mask);
197 198 199 200
        }
    }
}

201
static DEFAULT_SUDOKU: [[u8, ..9], ..9] = [
202 203 204 205 206 207 208 209 210 211 212 213 214
         /* 0    1    2    3    4    5    6    7    8    */
  /* 0 */  [0u8, 4u8, 0u8, 6u8, 0u8, 0u8, 0u8, 3u8, 2u8],
  /* 1 */  [0u8, 0u8, 8u8, 0u8, 2u8, 0u8, 0u8, 0u8, 0u8],
  /* 2 */  [7u8, 0u8, 0u8, 8u8, 0u8, 0u8, 0u8, 0u8, 0u8],
  /* 3 */  [0u8, 0u8, 0u8, 5u8, 0u8, 0u8, 0u8, 0u8, 0u8],
  /* 4 */  [0u8, 5u8, 0u8, 0u8, 0u8, 3u8, 6u8, 0u8, 0u8],
  /* 5 */  [6u8, 8u8, 0u8, 0u8, 0u8, 0u8, 0u8, 9u8, 0u8],
  /* 6 */  [0u8, 9u8, 5u8, 0u8, 0u8, 6u8, 0u8, 7u8, 0u8],
  /* 7 */  [0u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 6u8, 0u8],
  /* 8 */  [4u8, 0u8, 0u8, 0u8, 0u8, 7u8, 2u8, 0u8, 3u8]
];

#[cfg(test)]
215
static DEFAULT_SOLUTION: [[u8, ..9], ..9] = [
216 217 218 219 220 221 222 223 224 225 226 227 228 229
         /* 0    1    2    3    4    5    6    7    8    */
  /* 0 */  [1u8, 4u8, 9u8, 6u8, 7u8, 5u8, 8u8, 3u8, 2u8],
  /* 1 */  [5u8, 3u8, 8u8, 1u8, 2u8, 9u8, 7u8, 4u8, 6u8],
  /* 2 */  [7u8, 2u8, 6u8, 8u8, 3u8, 4u8, 1u8, 5u8, 9u8],
  /* 3 */  [9u8, 1u8, 4u8, 5u8, 6u8, 8u8, 3u8, 2u8, 7u8],
  /* 4 */  [2u8, 5u8, 7u8, 4u8, 9u8, 3u8, 6u8, 1u8, 8u8],
  /* 5 */  [6u8, 8u8, 3u8, 7u8, 1u8, 2u8, 5u8, 9u8, 4u8],
  /* 6 */  [3u8, 9u8, 5u8, 2u8, 8u8, 6u8, 4u8, 7u8, 1u8],
  /* 7 */  [8u8, 7u8, 2u8, 3u8, 4u8, 1u8, 9u8, 6u8, 5u8],
  /* 8 */  [4u8, 6u8, 1u8, 9u8, 5u8, 7u8, 2u8, 8u8, 3u8]
];

#[test]
fn colors_new_works() {
230 231 232 233 234 235 236 237 238
    assert_eq!(*Colors::new(1), 1022u16);
    assert_eq!(*Colors::new(2), 1020u16);
    assert_eq!(*Colors::new(3), 1016u16);
    assert_eq!(*Colors::new(4), 1008u16);
    assert_eq!(*Colors::new(5), 992u16);
    assert_eq!(*Colors::new(6), 960u16);
    assert_eq!(*Colors::new(7), 896u16);
    assert_eq!(*Colors::new(8), 768u16);
    assert_eq!(*Colors::new(9), 512u16);
239 240 241 242
}

#[test]
fn colors_next_works() {
243 244 245 246 247 248 249 250 251 252 253
    assert_eq!(Colors(0).next(), 0u8);
    assert_eq!(Colors(2).next(), 1u8);
    assert_eq!(Colors(4).next(), 2u8);
    assert_eq!(Colors(8).next(), 3u8);
    assert_eq!(Colors(16).next(), 4u8);
    assert_eq!(Colors(32).next(), 5u8);
    assert_eq!(Colors(64).next(), 6u8);
    assert_eq!(Colors(128).next(), 7u8);
    assert_eq!(Colors(256).next(), 8u8);
    assert_eq!(Colors(512).next(), 9u8);
    assert_eq!(Colors(1024).next(), 0u8);
254 255 256 257 258 259 260 261 262 263 264
}

#[test]
fn colors_remove_works() {
    // GIVEN
    let mut colors = Colors::new(1);

    // WHEN
    colors.remove(1);

    // THEN
265
    assert_eq!(colors.next(), 2u8);
266 267 268
}

#[test]
269
fn check_DEFAULT_SUDOKU_solution() {
270
    // GIVEN
271 272
    let mut sudoku = Sudoku::from_vec(&DEFAULT_SUDOKU);
    let solution   = Sudoku::from_vec(&DEFAULT_SOLUTION);
273 274 275 276 277

    // WHEN
    sudoku.solve();

    // THEN
P
Patrick Walton 已提交
278
    assert!(sudoku.equal(&solution));
279 280
}

281
fn main() {
282
    let args        = os::args();
Y
Youngmin Yoo 已提交
283
    let use_default = args.len() == 1u;
284
    let mut sudoku = if use_default {
285
        Sudoku::from_vec(&DEFAULT_SUDOKU)
286
    } else {
287
        Sudoku::read(io::stdin())
288
    };
289
    sudoku.solve();
P
Patrick Walton 已提交
290
    sudoku.write(&mut io::stdout());
291
}