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