| 1 | /* |
| 2 | Copyright (c) 2005-2019 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "../../common/utility/utility.h" |
| 18 | |
| 19 | #if __TBB_MIC_OFFLOAD |
| 20 | #pragma offload_attribute (push,target(mic)) |
| 21 | #endif // __TBB_MIC_OFFLOAD |
| 22 | |
| 23 | #include <cstdio> |
| 24 | #include <cstdlib> |
| 25 | #include <string> |
| 26 | |
| 27 | #include "tbb/atomic.h" |
| 28 | #include "tbb/tick_count.h" |
| 29 | #include "tbb/task_scheduler_init.h" |
| 30 | #include "tbb/task_group.h" |
| 31 | |
| 32 | #pragma warning(disable: 4996) |
| 33 | |
| 34 | const unsigned BOARD_SIZE=81; |
| 35 | const unsigned BOARD_DIM=9; |
| 36 | |
| 37 | using namespace tbb; |
| 38 | using namespace std; |
| 39 | |
| 40 | tbb::atomic<unsigned> nSols; |
| 41 | bool find_one = false; |
| 42 | bool verbose = false; |
| 43 | unsigned short init_values[BOARD_SIZE] = {1,0,0,9,0,0,0,8,0,0,8,0,2,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,5,2,1,0,0,4,0,0,0,0,0,0,0,5,0,0,7,4,0,0,7,0,0,0,3,0,0,3,0,0,0,2,0,0,5,0,0,0,0,0,0,1,0,0,5,0,0,0,1,0,0,0,0}; |
| 44 | task_group *g; |
| 45 | double solve_time; |
| 46 | |
| 47 | typedef struct { |
| 48 | unsigned short solved_element; |
| 49 | unsigned potential_set; |
| 50 | } board_element; |
| 51 | |
| 52 | void read_board(const char *filename) { |
| 53 | FILE *fp; |
| 54 | int input; |
| 55 | fp = fopen(filename, "r" ); |
| 56 | if (!fp) { |
| 57 | fprintf(stderr, "sudoku: Could not open input file '%s'.\n" , filename); |
| 58 | exit(1); |
| 59 | } |
| 60 | for (unsigned i=0; i<BOARD_SIZE; ++i) { |
| 61 | if (fscanf(fp, "%d" , &input)) |
| 62 | init_values[i] = input; |
| 63 | else { |
| 64 | fprintf(stderr, "sudoku: Error in input file at entry %d, assuming 0.\n" , i); |
| 65 | init_values[i] = 0; |
| 66 | } |
| 67 | } |
| 68 | fclose(fp); |
| 69 | } |
| 70 | |
| 71 | void print_board(board_element *b) { |
| 72 | for (unsigned row=0; row<BOARD_DIM; ++row) { |
| 73 | for (unsigned col=0; col<BOARD_DIM; ++col) { |
| 74 | printf(" %d" , b[row*BOARD_DIM+col].solved_element); |
| 75 | if (col==2 || col==5) printf(" |" ); |
| 76 | } |
| 77 | printf("\n" ); |
| 78 | if (row==2 || row==5) printf(" ---------------------\n" ); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | void print_potential_board(board_element *b) { |
| 83 | for (unsigned row=0; row<BOARD_DIM; ++row) { |
| 84 | for (unsigned col=0; col<BOARD_DIM; ++col) { |
| 85 | if (b[row*BOARD_DIM+col].solved_element) |
| 86 | printf(" %4d " , b[row*BOARD_DIM+col].solved_element); |
| 87 | else |
| 88 | printf(" [%4d]" , b[row*BOARD_DIM+col].potential_set); |
| 89 | if (col==2 || col==5) printf(" |" ); |
| 90 | } |
| 91 | printf("\n" ); |
| 92 | if (row==2 || row==5) |
| 93 | printf(" ------------------------------------------------------------------\n" ); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | void init_board(board_element *b) { |
| 98 | for (unsigned i=0; i<BOARD_SIZE; ++i) |
| 99 | b[i].solved_element = b[i].potential_set = 0; |
| 100 | } |
| 101 | |
| 102 | void init_board(board_element *b, unsigned short arr[81]) { |
| 103 | for (unsigned i=0; i<BOARD_SIZE; ++i) { |
| 104 | b[i].solved_element = arr[i]; |
| 105 | b[i].potential_set = 0; |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | void init_potentials(board_element *b) { |
| 110 | for (unsigned i=0; i<BOARD_SIZE; ++i) |
| 111 | b[i].potential_set = 0; |
| 112 | } |
| 113 | |
| 114 | void copy_board(board_element *src, board_element *dst) { |
| 115 | for (unsigned i=0; i<BOARD_SIZE; ++i) |
| 116 | dst[i].solved_element = src[i].solved_element; |
| 117 | } |
| 118 | |
| 119 | bool fixed_board(board_element *b) { |
| 120 | for (int i=BOARD_SIZE-1; i>=0; --i) |
| 121 | if (b[i].solved_element==0) return false; |
| 122 | return true; |
| 123 | } |
| 124 | |
| 125 | bool in_row(board_element *b, unsigned row, unsigned col, unsigned short p) { |
| 126 | for (unsigned c=0; c<BOARD_DIM; ++c) |
| 127 | if (c!=col && b[row*BOARD_DIM+c].solved_element==p) return true; |
| 128 | return false; |
| 129 | } |
| 130 | |
| 131 | bool in_col(board_element *b, unsigned row, unsigned col, unsigned short p) { |
| 132 | for (unsigned r=0; r<BOARD_DIM; ++r) |
| 133 | if (r!=row && b[r*BOARD_DIM+col].solved_element==p) return true; |
| 134 | return false; |
| 135 | } |
| 136 | |
| 137 | bool in_block(board_element *b, unsigned row, unsigned col, unsigned short p) { |
| 138 | unsigned b_row = row/3 * 3, b_col = col/3 * 3; |
| 139 | for (unsigned i=b_row; i<b_row+3; ++i) |
| 140 | for (unsigned j=b_col; j<b_col+3; ++j) |
| 141 | if (!(i==row && j==col) && b[i*BOARD_DIM+j].solved_element==p) return true; |
| 142 | return false; |
| 143 | } |
| 144 | |
| 145 | void calculate_potentials(board_element *b) { |
| 146 | for (unsigned i=0; i<BOARD_SIZE; ++i) { |
| 147 | b[i].potential_set = 0; |
| 148 | if (!b[i].solved_element) { // element is not yet fixed |
| 149 | unsigned row = i/BOARD_DIM, col = i%BOARD_DIM; |
| 150 | for (unsigned potential=1; potential<=BOARD_DIM; ++potential) { |
| 151 | if (!in_row(b, row, col, potential) && !in_col(b, row, col, potential) |
| 152 | && !in_block(b, row, col, potential)) |
| 153 | b[i].potential_set |= 1<<(potential-1); |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | bool valid_board(board_element *b) { |
| 160 | bool success=true; |
| 161 | for (unsigned i=0; i<BOARD_SIZE; ++i) { |
| 162 | if (success && b[i].solved_element) { // element is fixed |
| 163 | unsigned row = i/BOARD_DIM, col = i%BOARD_DIM; |
| 164 | if (in_row(b, row, col, b[i].solved_element) || in_col(b, row, col, b[i].solved_element) || in_block(b, row, col, b[i].solved_element)) |
| 165 | success = false; |
| 166 | } |
| 167 | } |
| 168 | return success; |
| 169 | } |
| 170 | |
| 171 | bool examine_potentials(board_element *b, bool *progress) { |
| 172 | bool singletons = false; |
| 173 | for (unsigned i=0; i<BOARD_SIZE; ++i) { |
| 174 | if (b[i].solved_element==0 && b[i].potential_set==0) // empty set |
| 175 | return false; |
| 176 | switch (b[i].potential_set) { |
| 177 | case 1: { b[i].solved_element = 1; singletons=true; break; } |
| 178 | case 2: { b[i].solved_element = 2; singletons=true; break; } |
| 179 | case 4: { b[i].solved_element = 3; singletons=true; break; } |
| 180 | case 8: { b[i].solved_element = 4; singletons=true; break; } |
| 181 | case 16: { b[i].solved_element = 5; singletons=true; break; } |
| 182 | case 32: { b[i].solved_element = 6; singletons=true; break; } |
| 183 | case 64: { b[i].solved_element = 7; singletons=true; break; } |
| 184 | case 128: { b[i].solved_element = 8; singletons=true; break; } |
| 185 | case 256: { b[i].solved_element = 9; singletons=true; break; } |
| 186 | } |
| 187 | } |
| 188 | *progress = singletons; |
| 189 | return valid_board(b); |
| 190 | } |
| 191 | |
| 192 | #if !__TBB_CPP11_LAMBDAS_PRESENT |
| 193 | void partial_solve(board_element *b, unsigned first_potential_set); |
| 194 | |
| 195 | class PartialSolveBoard { |
| 196 | board_element *b; |
| 197 | unsigned first_potential_set; |
| 198 | public: |
| 199 | PartialSolveBoard(board_element *_b, unsigned fps) : |
| 200 | b(_b), first_potential_set(fps) {} |
| 201 | void operator() () const { |
| 202 | partial_solve(b, first_potential_set); |
| 203 | } |
| 204 | }; |
| 205 | #endif |
| 206 | |
| 207 | void partial_solve(board_element *b, unsigned first_potential_set) { |
| 208 | if (fixed_board(b)) { |
| 209 | if ( find_one ) |
| 210 | g->cancel(); |
| 211 | if (++nSols==1 && verbose) { |
| 212 | print_board(b); |
| 213 | } |
| 214 | free(b); |
| 215 | return; |
| 216 | } |
| 217 | calculate_potentials(b); |
| 218 | bool progress=true; |
| 219 | bool success = examine_potentials(b, &progress); |
| 220 | if (success && progress) { |
| 221 | partial_solve(b, first_potential_set); |
| 222 | } else if (success && !progress) { |
| 223 | board_element *new_board; |
| 224 | while (b[first_potential_set].solved_element!=0) ++first_potential_set; |
| 225 | for (unsigned short potential=1; potential<=BOARD_DIM; ++potential) { |
| 226 | if (1<<(potential-1) & b[first_potential_set].potential_set) { |
| 227 | new_board = (board_element *)malloc(BOARD_SIZE*sizeof(board_element)); |
| 228 | copy_board(b, new_board); |
| 229 | new_board[first_potential_set].solved_element = potential; |
| 230 | #if __TBB_CPP11_LAMBDAS_PRESENT |
| 231 | g->run( [=]{ partial_solve(new_board, first_potential_set); } ); |
| 232 | #else |
| 233 | g->run(PartialSolveBoard(new_board, first_potential_set)); |
| 234 | #endif |
| 235 | } |
| 236 | } |
| 237 | free(b); |
| 238 | } |
| 239 | else { |
| 240 | free(b); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | unsigned solve(int p) { |
| 245 | task_scheduler_init init(p); |
| 246 | nSols = 0; |
| 247 | board_element *start_board = (board_element *)malloc(BOARD_SIZE*sizeof(board_element)); |
| 248 | init_board(start_board, init_values); |
| 249 | g = new task_group; |
| 250 | tick_count t0 = tick_count::now(); |
| 251 | partial_solve(start_board, 0); |
| 252 | g->wait(); |
| 253 | solve_time = (tick_count::now() - t0).seconds(); |
| 254 | delete g; |
| 255 | return nSols; |
| 256 | } |
| 257 | |
| 258 | #if __TBB_MIC_OFFLOAD |
| 259 | #pragma offload_attribute (pop) |
| 260 | #endif // __TBB_MIC_OFFLOAD |
| 261 | |
| 262 | int do_get_default_num_threads() { |
| 263 | int threads; |
| 264 | #if __TBB_MIC_OFFLOAD |
| 265 | #pragma offload target(mic) out(threads) |
| 266 | #endif // __TBB_MIC_OFFLOAD |
| 267 | threads = tbb::task_scheduler_init::default_num_threads(); |
| 268 | return threads; |
| 269 | } |
| 270 | |
| 271 | int get_default_num_threads() { |
| 272 | static int threads = do_get_default_num_threads(); |
| 273 | return threads; |
| 274 | } |
| 275 | |
| 276 | int main(int argc, char *argv[]) { |
| 277 | try { |
| 278 | tbb::tick_count mainStartTime = tbb::tick_count::now(); |
| 279 | |
| 280 | utility::thread_number_range threads(get_default_num_threads); |
| 281 | string filename = "" ; |
| 282 | bool silent = false; |
| 283 | |
| 284 | utility::parse_cli_arguments(argc,argv, |
| 285 | utility::cli_argument_pack() |
| 286 | //"-h" option for displaying help is present implicitly |
| 287 | .positional_arg(threads,"n-of-threads" ,utility::thread_number_range_desc) |
| 288 | .positional_arg(filename,"filename" ,"input filename" ) |
| 289 | |
| 290 | .arg(verbose,"verbose" ,"prints the first solution" ) |
| 291 | .arg(silent,"silent" ,"no output except elapsed time" ) |
| 292 | .arg(find_one,"find-one" ,"stops after finding first solution\n" ) |
| 293 | ); |
| 294 | |
| 295 | if ( silent ) verbose = false; |
| 296 | |
| 297 | if ( !filename.empty() ) |
| 298 | read_board( filename.c_str() ); |
| 299 | // otherwise (if file name not specified), the default statically initialized board will be used. |
| 300 | for(int p = threads.first; p <= threads.last; p = threads.step(p) ) { |
| 301 | unsigned number; |
| 302 | #if __TBB_MIC_OFFLOAD |
| 303 | #pragma offload target(mic) in(init_values, p, verbose, find_one) out(number, solve_time) |
| 304 | { |
| 305 | #endif // __TBB_MIC_OFFLOAD |
| 306 | number = solve(p); |
| 307 | #if __TBB_MIC_OFFLOAD |
| 308 | } |
| 309 | #endif // __TBB_MIC_OFFLOAD |
| 310 | |
| 311 | if ( !silent ) { |
| 312 | if ( find_one ) { |
| 313 | printf("Sudoku: Time to find first solution on %d threads: %6.6f seconds.\n" , p, solve_time); |
| 314 | } |
| 315 | else { |
| 316 | printf("Sudoku: Time to find all %u solutions on %d threads: %6.6f seconds.\n" , number, p, solve_time); |
| 317 | } |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | utility::report_elapsed_time((tbb::tick_count::now() - mainStartTime).seconds()); |
| 322 | |
| 323 | return 0; |
| 324 | } catch(std::exception& e) { |
| 325 | std::cerr<<"error occurred. error text is :\"" <<e.what()<<"\"\n" ; |
| 326 | return 1; |
| 327 | } |
| 328 | }; |
| 329 | |
| 330 | |