Shadowrun: Awakened 29 September 2011 - Build 871
sudoku.cpp
Go to the documentation of this file.
00001 /*
00002     Copyright 2005-2010 Intel Corporation.  All Rights Reserved.
00003 
00004     This file is part of Threading Building Blocks.
00005 
00006     Threading Building Blocks is free software; you can redistribute it
00007     and/or modify it under the terms of the GNU General Public License
00008     version 2 as published by the Free Software Foundation.
00009 
00010     Threading Building Blocks is distributed in the hope that it will be
00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with Threading Building Blocks; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018 
00019     As a special exception, you may use this file as part of a free software
00020     library without restriction.  Specifically, if other files instantiate
00021     templates or use macros or inline functions from this file, or you compile
00022     this file and link it with other files to produce an executable, this
00023     file does not by itself cause the resulting executable to be covered by
00024     the GNU General Public License.  This exception does not however
00025     invalidate any other reasons why the executable file might be covered by
00026     the GNU General Public License.
00027 */
00028 
00029 #include <cstdio>
00030 #include <cstdlib>
00031 #include "tbb/atomic.h"
00032 #include "tbb/tick_count.h"
00033 #include "tbb/task_scheduler_init.h"
00034 #include "tbb/task_group.h"
00035 
00036 #pragma warning(disable: 4996)
00037 
00038 #define __TBB_LAMBDAS_PRESENT  ( _MSC_VER >= 1600 && !__INTEL_COMPILER || __INTEL_COMPILER > 1100 && _TBB_CPP0X )
00039 
00040 const unsigned BOARD_SIZE=81;
00041 const unsigned BOARD_DIM=9;
00042 
00043 using namespace tbb;
00044 using namespace std;
00045 
00046 atomic<unsigned> nSols;
00047 unsigned NThreads, NSolutions;
00048 bool Verbose=false;
00049 unsigned short init_values[BOARD_SIZE];
00050 task_group *g;
00051 
00052 typedef struct {
00053     unsigned short solved_element;
00054     unsigned potential_set;
00055 } board_element;
00056 
00057 void read_board(char *filename) {
00058     FILE *fp;
00059     int input;
00060     fp = fopen(filename, "r");
00061     for (unsigned i=0; i<BOARD_SIZE; ++i) {
00062         if (fscanf(fp, "%d", &input))
00063             init_values[i] = input;
00064         else {
00065             fprintf(stderr, "sudoku: Error in input file at entry %d, assuming 0.\n", i);
00066             init_values[i] = 0;
00067         }
00068     }
00069     fclose(fp);
00070 }
00071 
00072 void print_board(board_element *b) {
00073     for (unsigned row=0; row<BOARD_DIM; ++row) {
00074         for (unsigned col=0; col<BOARD_DIM; ++col) {
00075             printf(" %d", b[row*BOARD_DIM+col].solved_element);
00076             if (col==2 || col==5) printf(" |");
00077         }
00078         printf("\n");
00079         if (row==2 || row==5) printf(" ---------------------\n");
00080     }
00081 }
00082 
00083 void print_potential_board(board_element *b) {
00084     for (unsigned row=0; row<BOARD_DIM; ++row) {
00085         for (unsigned col=0; col<BOARD_DIM; ++col) {
00086             if (b[row*BOARD_DIM+col].solved_element) 
00087                 printf("  %4d ", b[row*BOARD_DIM+col].solved_element);
00088             else
00089                 printf(" [%4d]", b[row*BOARD_DIM+col].potential_set);
00090             if (col==2 || col==5) printf(" |");
00091         }
00092         printf("\n");
00093         if (row==2 || row==5)
00094             printf(" ------------------------------------------------------------------\n");
00095     }
00096 }
00097 
00098 void init_board(board_element *b) {
00099     for (unsigned i=0; i<BOARD_SIZE; ++i)
00100         b[i].solved_element = b[i].potential_set = 0;
00101 }
00102 
00103 void init_board(board_element *b, unsigned short arr[81]) {
00104     for (unsigned i=0; i<BOARD_SIZE; ++i) {
00105         b[i].solved_element = arr[i]; 
00106         b[i].potential_set = 0;
00107     }
00108 }
00109 
00110 void init_potentials(board_element *b) {
00111     for (unsigned i=0; i<BOARD_SIZE; ++i)
00112         b[i].potential_set = 0;
00113 }
00114 
00115 void copy_board(board_element *src, board_element *dst) {
00116     for (unsigned i=0; i<BOARD_SIZE; ++i)
00117         dst[i].solved_element = src[i].solved_element;
00118 }
00119 
00120 bool fixed_board(board_element *b) {
00121     for (int i=BOARD_SIZE-1; i>=0; --i)
00122         if (b[i].solved_element==0) return false;
00123     return true;
00124 }
00125 
00126 bool in_row(board_element *b, unsigned row, unsigned col, unsigned short p) {
00127     for (unsigned c=0; c<BOARD_DIM; ++c)
00128         if (c!=col && b[row*BOARD_DIM+c].solved_element==p)  return true;
00129     return false;
00130 }
00131 
00132 bool in_col(board_element *b, unsigned row, unsigned col, unsigned short p) {
00133     for (unsigned r=0; r<BOARD_DIM; ++r)
00134         if (r!=row && b[r*BOARD_DIM+col].solved_element==p)  return true;
00135     return false;
00136 }
00137 
00138 bool in_block(board_element *b, unsigned row, unsigned col, unsigned short p) {
00139     unsigned b_row = row/3 * 3, b_col = col/3 * 3;
00140     for (unsigned i=b_row; i<b_row+3; ++i)
00141         for (unsigned j=b_col; j<b_col+3; ++j)
00142             if (!(i==row && j==col) && b[i*BOARD_DIM+j].solved_element==p) return true;
00143     return false;
00144 }
00145 
00146 void calculate_potentials(board_element *b) {
00147     for (unsigned i=0; i<BOARD_SIZE; ++i) {
00148         b[i].potential_set = 0;
00149         if (!b[i].solved_element) { // element is not yet fixed
00150             unsigned row = i/BOARD_DIM, col = i%BOARD_DIM;
00151             for (unsigned potential=1; potential<=BOARD_DIM; ++potential) {
00152                 if (!in_row(b, row, col, potential) && !in_col(b, row, col, potential)
00153                     && !in_block(b, row, col, potential))
00154                     b[i].potential_set |= 1<<(potential-1);
00155             }
00156         }
00157     }
00158 }
00159 
00160 bool valid_board(board_element *b) {
00161     bool success=true;
00162     for (unsigned i=0; i<BOARD_SIZE; ++i) {
00163         if (success && b[i].solved_element) { // element is fixed
00164             unsigned row = i/BOARD_DIM, col = i%BOARD_DIM;
00165             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))
00166                 success = false;
00167         }
00168     }
00169     return success;
00170 }
00171 
00172 bool examine_potentials(board_element *b, bool *progress) {
00173     bool singletons = false;
00174     for (unsigned i=0; i<BOARD_SIZE; ++i) {
00175         if (b[i].solved_element==0 && b[i].potential_set==0) // empty set
00176             return false;
00177         switch (b[i].potential_set) {
00178         case 1:   { b[i].solved_element = 1; singletons=true; break; }
00179         case 2:   { b[i].solved_element = 2; singletons=true; break; }
00180         case 4:   { b[i].solved_element = 3; singletons=true; break; }
00181         case 8:   { b[i].solved_element = 4; singletons=true; break; }
00182         case 16:  { b[i].solved_element = 5; singletons=true; break; }
00183         case 32:  { b[i].solved_element = 6; singletons=true; break; }
00184         case 64:  { b[i].solved_element = 7; singletons=true; break; }
00185         case 128: { b[i].solved_element = 8; singletons=true; break; }
00186         case 256: { b[i].solved_element = 9; singletons=true; break; }
00187         }
00188     }
00189     *progress = singletons;
00190     return valid_board(b);
00191 }
00192 
00193 #if !__TBB_LAMBDAS_PRESENT
00194 void partial_solve(board_element *b, unsigned first_potential_set);
00195 
00196 class PartialSolveBoard {
00197     board_element *b;
00198     unsigned first_potential_set;
00199 public:
00200     PartialSolveBoard(board_element *_b, unsigned fps) :
00201         b(_b), first_potential_set(fps) {}
00202     void operator() () const {
00203         partial_solve(b, first_potential_set);
00204     }
00205 };
00206 #endif
00207 
00208 void partial_solve(board_element *b, unsigned first_potential_set) {
00209     if (fixed_board(b)) {
00210         if (NSolutions == 1)
00211             g->cancel();
00212         if (++nSols==1 && Verbose) {
00213             print_board(b);
00214         }
00215         free(b);
00216         return;
00217     }
00218     calculate_potentials(b);
00219     bool progress=true;
00220     bool success = examine_potentials(b, &progress);
00221     if (success && progress) {
00222         partial_solve(b, first_potential_set);
00223     } else if (success && !progress) {
00224         board_element *new_board;
00225         while (b[first_potential_set].solved_element!=0) ++first_potential_set;
00226         for (unsigned short potential=1; potential<=BOARD_DIM; ++potential) {
00227             if (1<<(potential-1) & b[first_potential_set].potential_set) {
00228                 new_board = (board_element *)malloc(BOARD_SIZE*sizeof(board_element));
00229                 copy_board(b, new_board);
00230                 new_board[first_potential_set].solved_element = potential;
00231 #if __TBB_LAMBDAS_PRESENT
00232                 g->run( [=]{ partial_solve(new_board, first_potential_set); } );
00233 #else
00234                 g->run(PartialSolveBoard(new_board, first_potential_set));
00235 #endif
00236             }
00237         }
00238         free(b);
00239     }
00240     else {
00241         free(b);
00242     }
00243 }
00244 
00245 void ParseCommandLine(int argc, char *argv[]) {
00246     if (argc < 4) {
00247         fprintf(stderr, 
00248                 "Usage: sudoku <inputfilename> <nthreads> <nSolutions> [-p]\n"
00249                 "  nSolutions=1 stops after finding first solution\n"
00250                 "    and any other value finds all solutions; \n"
00251                 "  -p prints the first solution.\n");
00252         exit(1);
00253     }
00254     else {
00255         sscanf(argv[2], "%d", &NThreads);
00256         sscanf(argv[3], "%d", &NSolutions);
00257     }
00258     if (argc==5) Verbose = true;
00259 }
00260 
00261 int main(int argc, char *argv[]) {
00262     board_element *start_board;
00263     start_board = (board_element *)malloc(BOARD_SIZE*sizeof(board_element));
00264     NThreads = 1;
00265     nSols = 0;
00266     ParseCommandLine(argc, argv);
00267     read_board(argv[1]);
00268     init_board(start_board, init_values);
00269     task_scheduler_init init(NThreads);
00270     g = new task_group;
00271     tick_count t0 = tick_count::now();
00272     partial_solve(start_board, 0);
00273     g->wait();
00274     tick_count t1 = tick_count::now();
00275     delete g;
00276 
00277     if (NSolutions == 1) {
00278         printf("Sudoku: Time to find first solution on %d threads: %6.6f seconds.\n", NThreads, (t1 - t0).seconds());
00279     }
00280     else {
00281         printf("Sudoku: Time to find all %d solutions on %d threads: %6.6f seconds.\n", (int)nSols, NThreads, (t1 - t0).seconds());
00282   }
00283 };
00284 

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net