1/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2/* 3 * Copyright © 2014 Parin Porecha 4 * Copyright © 2014 Michael Catanzaro 5 * 6 * This file is part of GNOME Sudoku. 7 * 8 * GNOME Sudoku is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * GNOME Sudoku is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with GNOME Sudoku. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22using Gee; 23 24namespace SudokuGenerator 25{ 26 private class Worker : Object 27 { 28 private int nsudokus; 29 private DifficultyCategory level; 30 // FIXME Require Gee.ConcurrentList and remove the mutex 31 // https://bugzilla.gnome.org/show_bug.cgi?id=737507 32 private Gee.List<SudokuBoard> boards_list; 33 private static Mutex mutex; 34 private unowned SourceFunc callback; 35 36 public Worker (int nsudokus, DifficultyCategory level, Gee.List<SudokuBoard> boards_list, SourceFunc callback) 37 { 38 this.nsudokus = nsudokus; 39 this.level = level; 40 this.boards_list = boards_list; 41 this.callback = callback; 42 } 43 44 public void run () 45 { 46 // Generating a board takes a relatively long time. 47 var board = SudokuGenerator.generate_board (level); 48 mutex.lock (); 49 boards_list.add (board); 50 if (boards_list.size == nsudokus) 51 { 52 // We've added the final board to the list. 53 // Finish the call to generate_boards_async. 54 Idle.add (() => { 55 callback (); 56 return Source.REMOVE; 57 }); 58 } 59 mutex.unlock (); 60 } 61 } 62 63 private SudokuBoard generate_board (DifficultyCategory category) 64 { 65 var board = new SudokuBoard (); 66 int[] puzzle = new int[board.rows * board.cols]; 67 if (category != DifficultyCategory.CUSTOM) 68 puzzle = QQwing.generate_puzzle ((int) category); 69 70 for (var row = 0; row < board.rows; row++) 71 for (var col = 0; col < board.cols; col++) 72 { 73 var val = puzzle[(row * board.cols) + col]; 74 if (val != 0) 75 board.insert (row, col, val, true); 76 } 77 board.difficulty_category = category; 78 79 return board; 80 } 81 82 public async Gee.List<SudokuBoard> generate_boards_async ( 83 int nboards, DifficultyCategory category, Cancellable? cancellable) throws ThreadError, IOError 84 { 85 var boards = new ArrayList<SudokuBoard> (); 86 var pool = new ThreadPool<Worker>.with_owned_data ((worker) => { 87 worker.run (); 88 }, (int) get_num_processors (), false); 89 90 if (cancellable != null) 91 { 92 cancellable.connect(() => { 93 ThreadPool.free((owned) pool, true, false); 94 generate_boards_async.callback(); 95 }); 96 } 97 98 for (var i = 0; i < nboards; i++) 99 pool.add (new Worker (nboards, category, boards, generate_boards_async.callback)); 100 101 yield; 102 103 cancellable.set_error_if_cancelled (); 104 return boards; 105 } 106 107 public void print_stats (SudokuBoard board) 108 { 109 var cells = board.get_cells (); 110 var puzzle = new int[board.rows * board.cols]; 111 112 for (var row = 0; row < board.rows; row++) 113 for (var col = 0; col < board.cols; col++) 114 puzzle[(row * board.cols) + col] = cells[row, col]; 115 116 QQwing.print_stats (puzzle); 117 } 118 119 public string qqwing_version () 120 { 121 return QQwing.get_version (); 122 } 123} 124