summary refs log tree commit diff
path: root/src/test/bench/sudoku.rs
blob: 974c813675100905cf853b10537e93c10477d1da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use std;

import std::bitv;
import io::{writer_util, reader_util};

// 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
//

export grid_t, read_grid, solve_grid, write_grid;

// internal type of sudoku grids
type grid = [[mut u8]];

// exported type of sudoku grids
enum grid_t { grid_ctor(grid), }

// read a sudoku problem from file f
fn read_grid(f: io::reader) -> grid_t {
    assert f.read_line() == "9,9"; /* assert first line is exactly "9,9" */

    let g = vec::from_fn(10u, {|_i|
        vec::to_mut(vec::from_elem(10u, 0 as u8))
    });
    while !f.eof() {
        let comps = str::split_char(str::trim(f.read_line()), ',');
        if vec::len(comps) >= 3u {
            let row     = option::get(uint::from_str(comps[0])) as u8;
            let col     = option::get(uint::from_str(comps[1])) as u8;
            g[row][col] = option::get(uint::from_str(comps[2])) as u8;
        }
    }
    ret grid_ctor(g);
}

// solve sudoku grid
fn solve_grid(g: grid_t) {
    fn next_color(g: grid, row: u8, col: u8, start_color: u8) -> bool {
        if start_color < 10u8 {
            // colors not yet used
            let avail = bitv::bitv(10u, false);
            u8::range(start_color, 10u8) { |color|
                bitv::set(avail, color as uint, true);
            }

            // drop colors already in use in neighbourhood
            drop_colors(g, avail, row, col);

            // find first remaining color that is available
            let mut i = 1 as uint;
            while i < (10 as uint) { /* FIXME llvm ctlhd */
                if bitv::get(avail, i) {
                    g[row][col] = i as u8;
                    ret true;
                }
                i += 1 as uint; /* else */
            }
        }
        g[row][col] = 0u8;
        ret false;
    }

    // find colors available in neighbourhood of (row, col)
    fn drop_colors(g: grid, avail: bitv::bitv, row: u8, col: u8) {
        fn drop_color(g: grid, colors: bitv::bitv, row: u8, col: u8) {
            let color = g[row][col];
            if color != 0u8 { bitv::set(colors, color as uint, false); }
        }

        let it = bind drop_color(g, avail, _, _);

        u8::range(0u8, 9u8) { |idx|
            it(idx, col); /* check same column fields */
            it(row, idx); /* check same row fields */
        }

        // check same block fields
        let row0 = (row / 3u8) * 3u8;
        let col0 = (col / 3u8) * 3u8;
        u8::range(row0, row0 + 3u8) { |alt_row|
            u8::range(col0, col0 + 3u8) { |alt_col| it(alt_row, alt_col); }
        }
    }

    let mut work: [(u8, u8)] = []; /* queue of uncolored fields */
    u8::range(0u8, 9u8) { |row|
        u8::range(0u8, 9u8) { |col|
            let color = (*g)[row][col];
            if color == 0u8 { work += [(row, col)]; }
        }
    }

    let mut ptr = 0u;
    let end = vec::len(work);
    while (ptr < end) {
        let (row, col) = work[ptr];
        // is there another color to try?
        if next_color(*g, row, col, (*g)[row][col] + (1 as u8)) {
            //  yes: advance work list
            ptr = ptr + 1u;
        } else {
            // no: redo this field aft recoloring pred; unless there is none
            if ptr == 0u { fail "No solution found for this sudoku"; }
            ptr = ptr - 1u;
        }
    }
}

fn write_grid(f: io::writer, g: grid_t) {
    u8::range(0u8, 9u8) { |row|
        f.write_str(#fmt("%u", (*g)[row][0] as uint));
        u8::range(1u8, 9u8) { |col|
            f.write_str(#fmt(" %u", (*g)[row][col] as uint));
        }
        f.write_char('\n');
     }
}

fn main(args: [str]) {
    let grid = if vec::len(args) == 1u {
        // FIXME create sudoku inline since nested vec consts dont work yet
        let g = vec::from_fn(10u, {|_i|
            vec::to_mut(vec::from_elem(10u, 0 as u8))
        });
        g[0][1] = 4u8;
        g[0][3] = 6u8;
        g[0][7] = 3u8;
        g[0][8] = 2u8;
        g[1][2] = 8u8;
        g[1][4] = 2u8;
        g[2][0] = 7u8;
        g[2][3] = 8u8;
        g[3][3] = 5u8;
        g[4][1] = 5u8;
        g[4][5] = 3u8;
        g[4][6] = 6u8;
        g[5][0] = 6u8;
        g[5][1] = 8u8;
        g[5][7] = 9u8;
        g[6][1] = 9u8;
        g[6][2] = 5u8;
        g[6][5] = 6u8;
        g[6][7] = 7u8;
        g[7][4] = 4u8;
        g[7][7] = 6u8;
        g[8][0] = 4u8;
        g[8][5] = 7u8;
        g[8][6] = 2u8;
        g[8][8] = 3u8;
        grid_ctor(g)
    } else {
        read_grid(io::stdin())
    };
    solve_grid(grid);
    write_grid(io::stdout(), grid);
}