(**************************************************************************) (* *) (* Combine - an OCaml library for combinatorics *) (* *) (* Copyright (C) 2012 *) (* Remy El Sibaie *) (* Jean-Christophe Filliatre *) (* *) (* This software is free software; you can redistribute it and/or *) (* modify it under the terms of the GNU Library General Public *) (* License version 2.1, with the special exception on linking *) (* described in file LICENSE. *) (* *) (* This software is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *) (* *) (**************************************************************************) (* This module encodes Sudoku as EMC and solves it. This program reads a Sudoku problem from a file, in the following format: 000206003 060080000 071003000 006000910 007809600 024000800 000100540 000030080 200608000 Here is the correspondance between the Sudoku and its encoding as EMC: - 9 rows x 9 values = 81 columns - 9 columns x 9 values = 81 columns - 9 subgrids x 9 values = 81 columns - 81 cells = 81 columns One row in the EMC matrix encodes the placement of one value in one cell. For instance, setting the value of the cell (0, 0) to 4 corresponds to - 1 in the 4th column - 1 in the 85th column - 1 in the 166th column - 1 in the 243th column - 0 in all other columns *) open Format open Combine let size = 9 let emc_size = size * size * 4 let zdd = ref false let dlx = ref false let display_sudoku_line line = Array.iter (fun e -> printf "%d" e) line; printf "@." let display_sudoku sudoku_array = Array.iter (fun e -> display_sudoku_line e) sudoku_array; printf "@." let read_sudoku_line s = Array.init size (fun i -> (int_of_char s.[i]) - (int_of_char '0')) let read s = let lines = Str.split (Str.regexp "\n") s in let sudoku_array = Array.make_matrix size size 0 in let rec read lines i = match lines with [] -> () h::t -> if h <> "" then begin sudoku_array.(i) <- read_sudoku_line h; read t (i + 1) end else read t i in read lines 0; sudoku_array let file = ref None let out = ref "" let msg = "usage: project [options] file" let spec = ["-o", Arg.Set_string out, " solution output in SVG format"; ] let set_file f = match !file with Some _ -> Arg.usage [] msg; exit 1 None when Sys.file_exists f -> file := Some f None -> eprintf "%s: no such file@." f; exit 1 let () = Arg.parse spec set_file msg let error_pieces_board () = eprintf "problem must have board and piece(s) @."; exit 1 let sudoku = let c = match !file with Some f -> open_in f None -> stdin in let p = try let s = String.make (in_channel_length c) ' ' in really_input c s 0 (in_channel_length c - 1); read s with Invalid_argument msg -> eprintf "invalid input file: %s@." msg; exit 1 in begin match !file with Some _ -> close_in c None -> () end; p let ok_in_cell v celli cellj sudoku = for iteri = celli to celli + 2 do for iterj = cellj to cellj + 2 do if sudoku.(iteri).(iterj) = v then raise Exit done done let ok v i j sudoku = try if sudoku.(i).(j) <> 0 then raise Exit; let celli, cellj = (i / 3) * 3, (j / 3) * 3 in ok_in_cell v celli cellj sudoku; let rec iteri_out_cell first last = if first > last then () else if sudoku.(first).(j) = v then raise Exit else iteri_out_cell (first + 1) last in let rec iterj_out_cell first last = if first > last then () else if sudoku.(i).(first) = v then raise Exit else iterj_out_cell (first + 1) last in iteri_out_cell 0 (celli - 1); iteri_out_cell (celli + 3) (size - 1); iterj_out_cell 0 (cellj - 1); iterj_out_cell (cellj + 3) (size - 1); true with Exit -> false let set_val v i j line = (* Column *) line.(9 * j + v - 1) <- true; (* Line *) line.(9 * (9 + i) + v - 1) <- true; (* Group *) line.(9 * (9 * 2 + (j / 3 + (i / 3) * 3)) + v - 1) <- true; (* Cell *) line.(9 * (9 * 3 + i) + j) <- true let get_line v i j = let line = Array.make emc_size false in set_val v i j line; line let const_line sudoku = let line = Array.make emc_size false in for i = 0 to size - 1 do for j = 0 to size - 1 do let v = sudoku.(i).(j) in if v <> 0 then begin set_val v i j line end done done; line let emc sudoku = let lr = ref [const_line sudoku] in for i = 0 to size - 1 do for j = 0 to size - 1 do for v = 1 to size do if ok v i j sudoku then lr := get_line v i j :: !lr done done done; Array.of_list !lr let print_emc_sudoku () = for i = 1 to 9 do printf "%d " i done; for i = 1 to 9 do printf "%d " i done; for i = 1 to 9 do printf "%d " i done; printf "@."; printf "colonne "; for i = 1 to 8 do printf " " done; printf "ligne "; for i = 1 to 8 do printf " " done; printf "cellule "; for i = 1 to 8 do printf " " done; printf "@." let decode sudoku emc_array i = if i < Array.length emc_array - 1 then begin let c = ref 0 in let l = ref 0 in let v = ref 0 in for j = 0 to 161 do if emc_array.(i).(j) then begin if j < 81 then begin c := j / 9; v := j mod 9 + 1 end else l := (j - 81) / 9; end done; assert (!v <> 0); sudoku.(!l).(!c) <- !v end let print_board_svg u fmt = for i = 0 to 9 do fprintf fmt "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" \ style=\"stroke:black;stroke-width:1;\" />@\n" 0 (i * u) (u * 9) (i * u); fprintf fmt "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" \ style=\"stroke:black;stroke-width:1;\" />@\n" (i * u) 0 (i * u) (u * 9); fprintf fmt "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" \ style=\"stroke:black;fill-opacity:0;stroke-width:3;\"/>@\n" ((i mod 3) * u * 3) ((i / 3) * u * 3) (u * 3) (u * 3) done let print_solution_to_svg fmt ~width ~height m = let u = 100 in fprintf fmt "<?xml version=\"1.0\" standalone=\"no\"?> @\n\ @[<hov 2><svg xmlns=\"http://www.w3.org/2000/svg\" \ width=\"%d\" height=\"%d\">@\n" width height; for i = 0 to 9 - 1 do for j = 0 to 9 -1 do fprintf fmt "<text x=\"%d\" y=\"%d\" style= \"font-size:50px;\ text-anchor:middle;\" >\ %d</text>" (i * u + 50) (j * u + 65) m.(j).(i) done done; print_board_svg u fmt; fprintf fmt "@]@\n</svg>" let print_solution_to_svg_file f ~width ~height m = let c = open_out f in let fmt = formatter_of_out_channel c in print_solution_to_svg fmt ~width ~height m; fprintf fmt "@."; close_out c let () = let emc_array = emc sudoku in display_sudoku sudoku; printf "DLX : emc_size : %dx%d @." (Array.length emc_array) (Array.length emc_array.(0)); try let p = Emc.D.create emc_array in let s = Emc.D.find_solution p in let n = List.length s in printf "solution size : %d@." n; List.iter (decode sudoku emc_array) s; display_sudoku sudoku; if not (!out = "") then begin print_solution_to_svg_file (!out) ~width:900 ~height:900 sudoku; printf "out : %s@\n" !out end; printf "%d solutions@." (Emc.D.count_solutions p) with Not_found -> printf "No solution@."
This document was generated using caml2html