sudoku-solver
A side project written in C to solve Sudoku puzzles.
sudoku.c File Reference

Holds all the logic for the sudoku solver. More...

Functions

int process_arguments (int argc, char *argv[], int *verbosity, int *bulkFlag, int *timeFlag, int *forceFlag, int *debugFlag, char **inCsv, char **outCsv, char **missing)
 Processes the command line arguments provided. More...
 
int initialize_globals (char buffer[ROW_BUF_SIZE], int isBulk)
 Allocates and initializes the global variables. More...
 
void free_globals ()
 Frees the dynamically allocated global variables.
 
int addToValid (int *tokenCount, char *token)
 Add the token to the valid array if not full. More...
 
int process_token (int *tokenCount, char *token, int row, int col)
 Sanitize, and set the grid to reference a valid token. More...
 
int read_csv (const char *fileName, char *missing)
 Reads from a csv file and loads into the grid. More...
 
void get_columns_bitmap (int array[VALUES][VALUES])
 Stores all columns as a bitmap of numbers. More...
 
void get_boxes_bitmap (int array[SUB_SIZE][SUB_SIZE][VALUES])
 Stores all columns as a bitmap of numbers. More...
 
void get_row_bitmap (int row, int array[VALUES])
 Stores the current row as a bitmap of numbers. More...
 
void pre_process (int *cellsLeft)
 Initializes a bitmap to hold possible values. More...
 
void print_array (int *array, int len, int isPadded)
 Prints a grid cell's possible contents. More...
 
void print_possible ()
 Prints all possible values for each cell in the grid (FOR TESTING).
 
void print_line (char type, int length)
 Prints a line of given length with a given character. More...
 
void print_grid ()
 Prints the current state of the grid in a formatted output.
 
int * make_pair (int row, int col)
 Dynamically allocated and initializes a pair of values. More...
 
int * find_single ()
 Gets the next pair of row and column where there is only one possibility. More...
 
int get_possible_value (int *pair)
 Assumes there is only one value in the bitmap and retrieves it. More...
 
int * unique_in_row (int *value)
 Gets position which is only place for a possible value in a row. More...
 
int * unique_in_col (int *value)
 Gets position which is only place for a possible value in a col. More...
 
int * unique_in_box (int *value)
 Gets position which is the only place for a possible value in a box. More...
 
int * unique_in_range (int mode, int *value, int verbosity)
 Gets position which is only place for a possible value in a house. More...
 
int remove_value_from_row (int row, int value, int mask[VALUES], int verbosity)
 Removes a value from the possibilities of all others in the same row. More...
 
int remove_value_from_column (int col, int value, int mask[VALUES], int verbosity)
 Removes a value from the possibilities of all others in the same column. More...
 
int remove_value_from_box (int row, int col, int value, int mask[VALUES], int verbosity)
 Removes a value from the possibilities of all others in the same box. More...
 
int valid_in_row (int value, int row)
 Checks if the value is valid (no duplicates) in the given row. More...
 
int valid_in_col (int value, int col)
 Checks if the value is valid (no duplicates) in the given column. More...
 
int valid_in_box (int value, int row, int col)
 Checks if the value is valid (no duplicates) in the given box. More...
 
int brute_force_rec (int curRow, int curCol, int verbosity)
 Attempts to solve using a brute force mechanism. More...
 
void brute_force (int verbosity)
 Wrapper function to handle result of recursive brute force appropriately. More...
 
int max (int a, int b)
 Returns the maximum of two integers. More...
 
int focus_house (int houseBM[SUB_SIZE])
 Returns overlapping house where possible locations of a value lie in. More...
 
int intersection_removal (int verbosity)
 Find value in only overlapping house and remove from rest of other houses. More...
 
int remove_naked_group (int *rows, int *cols, int *shared, int size, int mode, int verbosity)
 Removes values according to the contents of a known naked group. More...
 
int remove_hidden_group (int *rows, int *cols, int *shared, int size, int mode, int verbosity)
 Removes values according to the contents of a known hidden group. More...
 
int eval_naked_group (int *candidates, int size, int pos, int mode, int verbosity)
 Evaluates a naked group by checking validity and removing redundancy. More...
 
int eval_hidden_group (int *candidates, int size, int pos, int mode, int verbosity)
 Evaluates a hidden group by checking validity and removing redundancy. More...
 
int candid_group (int considered[VALUES], int curConsidered, int candidCount, int *candidates, int curCandid, int targetSize, int curSize, int pos, int mode, int verbosity, int isHidden)
 Recursive function to create groups of candidate numbers of the given size. More...
 
int find_group (int considered[VALUES], int candidCount, int size, int pos, int mode, int verbosity, int isHidden)
 Wrapper function for forming naked groups and evaluating them. More...
 
int naked_groups (int size, int verbosity)
 Top-level function to find and remove naked groups. More...
 
int hidden_groups (int size, int verbosity)
 Top-level function to find and remove hidden groups. More...
 
int eliminate_redundant (int verbosity)
 Elminates redundant possibilities using the techniques the solver knows. More...
 
int process_next (int *cellsLeft, int verbosity, int forceFlag)
 Process the next iteration of the loop. More...
 
void write_to_file (FILE *output)
 Writes the contents of the grid to a file in csv format. More...
 
int export_grid (char *outCsv)
 Exports the solved grid as a csv file. More...
 
int read_bulk (const char *fileName, int forceFlag)
 Reads, solves and checks all sudokus in a bulk sudoku file. More...
 
int read_file (char *inCsv, char *missing, int isBulk, int forceFlag)
 Adapter function to handle the reading of a file depending on its structure. More...
 
int main (int argc, char *argv[])
 Called initially and encompasses the program's functionality. More...
 

Variables

char *** grid
 A string 2-D array storing known or determined values.
 
int *** possible
 Stores all possible values for each cell in the grid.
 
int ** possibleCount
 Stores the number of possibilities for each cell.
 
char ** valid
 Stores the valid tokens that can be used in the sudoku grid.
 
int VALUES
 The number of values that the sudoku can hold.
 
int SUB_SIZE
 The root of the values; i.e. the length of the boxes.
 
char * MODE_LABELS [] = {"rows", "cols", "boxes"}
 

Detailed Description

Holds all the logic for the sudoku solver.

Function Documentation

◆ addToValid()

int addToValid ( int *  tokenCount,
char *  token 
)

Add the token to the valid array if not full.

Parameters
[in,out]tokenCountThe number of tokens currently parsed
[in]tokenA candidate token for the array of valid tokens
[out]status0: Success, 1: Invalid sudoku
227  {
228  int new = 1;
229  for (int i = 0; i < *tokenCount; i++) {
230  if (strcmp(valid[i], token) == 0) {
231  new = 0;
232  }
233  }
234  if (new == 1) {
235  if (*tokenCount >= VALUES + 1) {
236  fprintf(stderr, "Too many different valid tokens in grid\n");
237  return 1;
238  }
239  valid[*tokenCount] = strdup(token);
240  (*tokenCount)++;
241  }
242  return 0;
243 }

◆ brute_force()

void brute_force ( int  verbosity)

Wrapper function to handle result of recursive brute force appropriately.

Parameters
[in]verbosityThe verbosity of the output (print or hide)
895  {
896  if (verbosity > 0) {
897  printf(PROCESS "Attempting brute force...\n");
898  }
899  if (brute_force_rec(0, 0, verbosity) == 1) {
900  printf("Brute Force failed.\n");
901  }
902 }

◆ brute_force_rec()

int brute_force_rec ( int  curRow,
int  curCol,
int  verbosity 
)

Attempts to solve using a brute force mechanism.

Parameters
[in]curRow,curColThe current position to be forced in
[in]verbosityThe verbosity of the output (print or hide)
[out]status0: Success, 1: Invalid state
846  {
847  if (verbosity == 2) {
848  printf(FOUND "Brute Force(%i, %i)\n", curRow, curCol);
849  }
850  int colSet = 0;
851  for (int row = curRow; row < VALUES; row++) {
852  for (int col = 0; col < VALUES; col++) {
853  if (colSet == 0) {
854  col = curCol;
855  colSet = 1;
856  }
857  if (grid[row][col] == valid[0]) {
858  int value = 0;
859  while (1) {
860  value += 1;
861  // Check if value is valid
862  if (value > VALUES) {
863  // Reset current value to '-' if all values failed (rollback)
864  grid[row][col] = valid[0];
865  return 1;
866  }
867  // If invalid for that position, then try with the next one
868  if (valid_in_row(value, row) != 0 || valid_in_col(value, col) != 0 ||
869  valid_in_box(value, row, col) != 0) {
870  continue;
871  }
872  if (verbosity == 2) {
873  printf(ADD "Forced: %i, %i, %i\n", row, col, value);
874  }
875  grid[row][col] = valid[value];
876  // Return if successful till the end, otherwise keep looping
877  if (brute_force_rec(row + (col + 1) / VALUES,
878  (col + 1) % VALUES, verbosity) == 0) {
879  return 0;
880  }
881  }
882  }
883  else if (verbosity == 2) {
884  printf("\x1B[36m' \x1B[0mSkipped: %i, %i\n", row, col);
885  }
886  }
887  }
888  return 0; // End of brute force
889 }

◆ candid_group()

int candid_group ( int  considered[VALUES],
int  curConsidered,
int  candidCount,
int *  candidates,
int  curCandid,
int  targetSize,
int  curSize,
int  pos,
int  mode,
int  verbosity,
int  isHidden 
)

Recursive function to create groups of candidate numbers of the given size.

These are used for both naked and hidden and evaluated when formed. Optimized to exit early if not enough candidates left for forming a group.

Parameters
[in]consideredA bitmap of candidate cells in a house
[in]curConsideredThe current index within considered
[in]candidCountThe number of candidates in considered
[in,out]candidatesThe candidates currently in the group
[in]curCandidThe position of the candidate in order
[in]targetSizeThe desired size of the candidate group
[in]curSizeThe current size of the candidate group
[in]posSpecifies the position of the house
[in]modeMode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[in]isHiddenWhether the group is hidden or naked
[out]removed(Bool) 0: False, 1: True
1480  {
1481  // If the target group size is met, evaluate the group
1482  if (curSize == targetSize) {
1483  if (isHidden == 1) {
1484  return eval_hidden_group(candidates, targetSize, pos, mode, verbosity);
1485  } else {
1486  return eval_naked_group(candidates, targetSize, pos, mode, verbosity);
1487  }
1488  }
1489  // If not enough candidates left to form a group, then quit
1490  if (candidCount - curCandid < targetSize - curSize) {
1491  return 0;
1492  }
1493  // Use curConsidered and candidates to get next curConsidered
1494  int output = 0;
1495  int nextConsidered;
1496  int nextCandid = curCandid;
1497  do {
1498  nextConsidered = -1;
1499  for (int i = curConsidered + 1; i < VALUES; i++) {
1500  if (considered[i] == 1) {
1501  nextConsidered = i;
1502  nextCandid += 1;
1503  break;
1504  }
1505  }
1506  // If valid index, then recurse further
1507  if (nextConsidered != -1) {
1508  candidates[curSize] = nextConsidered;
1509  output = max(candid_group(considered, nextConsidered, candidCount,
1510  candidates, nextCandid, targetSize, curSize + 1, pos, mode, verbosity,
1511  isHidden), output);
1512  // Update curConsidered to allow next value to be considered
1513  curConsidered = nextConsidered;
1514  }
1515  } while (nextConsidered != -1);
1516  return output;
1517  // If invalid index, then return (candidates only used after full assignment)
1518 }

◆ eliminate_redundant()

int eliminate_redundant ( int  verbosity)

Elminates redundant possibilities using the techniques the solver knows.

Adopts an incremental strategy to use more advanced techniques only if no progress is made by the previous strategies, for greedy addition.

Parameters
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1712  {
1713  int output = 0;
1714  int step = 0;
1715  if (verbosity > 0) {
1716  printf(PROCESS "Removing redundant values...\n");
1717  }
1718  while (output == 0 && step < 3) {
1719  switch (step) {
1720  case 0: {
1721  for (int size = 2; size <= 4; size++) {
1722  output = max(output, naked_groups(size, verbosity));
1723  }
1724  break;
1725  }
1726  case 1: {
1727  for (int size = 2; size <= 4; size++) {
1728  output = max(output, hidden_groups(size, verbosity));
1729  }
1730  break;
1731  }
1732  case 2: {
1733  output = max(output, intersection_removal(verbosity));
1734  break;
1735  }
1736  }
1737  step += 1;
1738  }
1739  return output;
1740 }

◆ eval_hidden_group()

int eval_hidden_group ( int *  candidates,
int  size,
int  pos,
int  mode,
int  verbosity 
)

Evaluates a hidden group by checking validity and removing redundancy.

Parameters
[in]candidatesThe positions of the cells within the house
[in]sizeThe number of cells in the group
[in]posSpecifies the house, with mode specifying its type
[in]modeThe mode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1369  {
1370  // Loop through candidates to obtain shared positions
1371  int preShared[VALUES];
1372  memset(preShared, 0, sizeof(preShared));
1373  switch (mode) {
1374  case 0: { // ROW
1375  for (int i = 0; i < size; i++) { // value
1376  for (int col = 0; col < VALUES; col++) {
1377  if (possible[pos][col][candidates[i]] == 1) {
1378  preShared[col] += 1;
1379  }
1380  }
1381  }
1382  break;
1383  }
1384  case 1: { // COL
1385  for (int i = 0; i < size; i++) {
1386  for (int row = 0; row < VALUES; row++) {
1387  if (possible[row][pos][candidates[i]] == 1) {
1388  preShared[row] += 1;
1389  }
1390  }
1391  }
1392  break;
1393  }
1394  case 2: { // SUBGRID
1395  int subRow = (pos / SUB_SIZE) * SUB_SIZE;
1396  int subCol = (pos % SUB_SIZE) * SUB_SIZE;
1397  for (int i = 0; i < size; i++) {
1398  for (int inPos = 0; inPos < VALUES; inPos++) {
1399  int curRow = subRow + (inPos / SUB_SIZE);
1400  int curCol = subCol + (inPos % SUB_SIZE);
1401  if (possible[curRow][curCol][candidates[i]] == 1) {
1402  preShared[inPos] += 1;
1403  }
1404  }
1405  }
1406  }
1407  }
1408 
1409  // Determine if valid hidden group and collect shared positions
1410  int shared[size];
1411  memset(shared, 0, sizeof(shared));
1412  int sharedCounter = 0;
1413  for (int position = 0; position < VALUES; position++) {
1414  if (preShared[position] > 0) {
1415  if (sharedCounter == size) {
1416  return 0; // Too many values to be a hidden group
1417  }
1418  shared[sharedCounter] = position;
1419  sharedCounter += 1;
1420  }
1421  }
1422 
1423  if (sharedCounter < size) {
1424  return 0;
1425  }
1426 
1427  int rows[size];
1428  int cols[size];
1429  memset(rows, 0, sizeof(rows));
1430  memset(cols, 0, sizeof(cols));
1431  // Mode specific construction of rows and cols
1432  switch (mode) {
1433  case 0: { // Row
1434  for (int i = 0; i < size; i++) { // All have same row
1435  rows[i] = pos;
1436  cols[i] = shared[i];
1437  }
1438  break;
1439  }
1440  case 1: { // Col
1441  for (int i = 0; i < size; i++) { // All have same col
1442  rows[i] = shared[i];
1443  cols[i] = pos;
1444  }
1445  break;
1446  }
1447  case 2: { // Box
1448  int subRow = (pos / SUB_SIZE) * SUB_SIZE;
1449  int subCol = (pos % SUB_SIZE) * SUB_SIZE;
1450  for (int i = 0; i < size; i++) {
1451  rows[i] = subRow + (shared[i] / SUB_SIZE);
1452  cols[i] = subCol + (shared[i] % SUB_SIZE);
1453  }
1454  break;
1455  }
1456  }
1457 
1458  return remove_hidden_group(rows, cols, candidates, size, mode, verbosity);
1459 }

◆ eval_naked_group()

int eval_naked_group ( int *  candidates,
int  size,
int  pos,
int  mode,
int  verbosity 
)

Evaluates a naked group by checking validity and removing redundancy.

Parameters
[in]candidatesThe positions of the cells within the house
[in]sizeThe number of cells in the group
[in]posSpecifies the house, with mode specifying its type
[in]modeThe mode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1222  {
1223  // Loop through candidates to obtain shared values
1224  int preShared[VALUES];
1225  memset(preShared, 0, sizeof(preShared));
1226  switch (mode) {
1227  case 0: { // ROW
1228  for (int i = 0; i < size; i++) {
1229  for (int value = 0; value < VALUES; value++) {
1230  if (possible[pos][candidates[i]][value] == 1) {
1231  preShared[value] += 1;
1232  }
1233  }
1234  }
1235  break;
1236  }
1237  case 1: { // COL
1238  for (int i = 0; i < size; i++) {
1239  for (int value = 0; value < VALUES; value++) {
1240  if (possible[candidates[i]][pos][value] == 1) {
1241  preShared[value] += 1;
1242  }
1243  }
1244  }
1245  break;
1246  }
1247  case 2: { // SUBGRID
1248  int subRow = (pos / SUB_SIZE) * SUB_SIZE;
1249  int subCol = (pos % SUB_SIZE) * SUB_SIZE;
1250  for (int i = 0; i < size; i++) {
1251  int curRow = subRow + (candidates[i] / SUB_SIZE);
1252  int curCol = subCol + (candidates[i] % SUB_SIZE);
1253  for (int value = 0; value < VALUES; value++) {
1254  if (possible[curRow][curCol][value] == 1) {
1255  preShared[value] += 1;
1256  }
1257  }
1258  }
1259  }
1260  }
1261 
1262  // Determine if valid naked group and collect shared values
1263  int shared[size];
1264  memset(shared, 0, sizeof(shared));
1265  int sharedCounter = 0;
1266  for (int value = 0; value < VALUES; value++) {
1267  if (preShared[value] > 0) {
1268  if (sharedCounter == size) {
1269  return 0; // Too many values to be a naked group
1270  }
1271  shared[sharedCounter] = value;
1272  sharedCounter += 1;
1273  }
1274  }
1275 
1276  int output = 0;
1277  int rows[size];
1278  int cols[size];
1279  memset(rows, 0, sizeof(rows));
1280  memset(cols, 0, sizeof(cols));
1281  // Mode specific construction of rows and cols
1282  switch (mode) {
1283  case 0: { // Row
1284  for (int i = 0; i < size; i++) { // All have same row
1285  rows[i] = pos;
1286  cols[i] = candidates[i];
1287  }
1288  break;
1289  }
1290  case 1: { // Col
1291  for (int i = 0; i < size; i++) { // All have same col
1292  rows[i] = candidates[i];
1293  cols[i] = pos;
1294  }
1295  break;
1296  }
1297  case 2: { // Box
1298  int subRow = (pos / SUB_SIZE) * SUB_SIZE;
1299  int subCol = (pos % SUB_SIZE) * SUB_SIZE;
1300  for (int i = 0; i < size; i++) {
1301  rows[i] = subRow + (candidates[i] / SUB_SIZE);
1302  cols[i] = subCol + (candidates[i] % SUB_SIZE);
1303  }
1304  break;
1305  }
1306  }
1307 
1308  output = max(output,
1309  remove_naked_group(rows, cols, shared, size, mode, verbosity));
1310 
1311  // Additional shared groups if any
1312  int sharedValue = -1;
1313  int sharesGroup = 1;
1314  if (mode < 2) {
1315  for (int i = 0; i < size; i++) {
1316  if (sharedValue == -1) {
1317  sharedValue = candidates[i] / SUB_SIZE;
1318  } else if (sharedValue != candidates[i] / SUB_SIZE) {
1319  sharesGroup = 0;
1320  break;
1321  }
1322  }
1323  if (sharesGroup == 1) {
1324  output = max(output,
1325  remove_naked_group(rows, cols, shared, size, 2, verbosity));
1326  }
1327  } else {
1328  for (int i = 0; i < size; i++) {
1329  if (sharedValue == -1) {
1330  sharedValue = rows[i];
1331  } else if (sharedValue != rows[i]) {
1332  sharesGroup = 0;
1333  break;
1334  }
1335  }
1336  if (sharesGroup == 1) {
1337  output = max(output,
1338  remove_naked_group(rows, cols, shared, size, 0, verbosity));
1339  }
1340 
1341  sharedValue = -1;
1342  sharesGroup = 1;
1343  for (int i = 0; i < size; i++) {
1344  if (sharedValue == -1) {
1345  sharedValue = cols[i];
1346  } else if (sharedValue != cols[i]) {
1347  sharesGroup = 0;
1348  break;
1349  }
1350  }
1351  if (sharesGroup == 1) {
1352  output = max(output,
1353  remove_naked_group(rows, cols, shared, size, 1, verbosity));
1354  }
1355  }
1356  return output;
1357 }

◆ export_grid()

int export_grid ( char *  outCsv)

Exports the solved grid as a csv file.

Parameters
[in]outCsvThe path (and name) of the file to output to
[out]status0: Success, 1: Failure
1844  {
1845  // Append .csv file extension to the fileName provided if not already present
1846  char* outCsvFull = calloc(strlen(outCsv) + 4, sizeof(char));
1847  strcpy(outCsvFull, outCsv);
1848  if (strstr(outCsvFull, ".csv") !=
1849  &outCsvFull[strlen(outCsvFull) - 4]) {
1850  strcat(outCsvFull, ".csv");
1851  }
1852 
1853  // Create and open csv file to export to
1854  FILE* output = fopen(outCsvFull, "w+");
1855  free(outCsvFull);
1856  if (output == NULL) {
1857  fprintf(stderr, "Output file could not be created\n");
1858  return 1;
1859  }
1860  write_to_file(output);
1861  fclose(output);
1862  return 0;
1863 }

◆ find_group()

int find_group ( int  considered[VALUES],
int  candidCount,
int  size,
int  pos,
int  mode,
int  verbosity,
int  isHidden 
)

Wrapper function for forming naked groups and evaluating them.

Parameters
[in]consideredA bitmap of candidate cells in a house
[in]candidCountThe number of candidates in considered
[in]sizeThe desired size of the candidate group
[in]posSpecifies the position of the house
[in]modeMode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[in]isHiddenWhether the group is hidden or naked
[out]removed(Bool) 0: False, 1: True
1532  {
1533  int candidates[size];
1534  memset(candidates, 0, sizeof(candidates));
1535  return candid_group(considered, -1, candidCount, candidates, -1, size, 0,
1536  pos, mode, verbosity, isHidden);
1537 }

◆ find_single()

int* find_single ( )

Gets the next pair of row and column where there is only one possibility.

Parameters
[out]pairThe position of the single cell or NULL
540  {
541  for (int row = 0; row < VALUES; row++) {
542  for (int col = 0; col < VALUES; col++) {
543  if (possibleCount[row][col] == 1) {
544  return make_pair(row, col);
545  }
546  }
547  }
548  return NULL;
549 }

◆ focus_house()

int focus_house ( int  houseBM[SUB_SIZE])

Returns overlapping house where possible locations of a value lie in.

Parameters
[in]houseBMA bitmap for the house to consult
[out]focusThe relative position of the overlapping house or -1
925  {
926  int focus = -1;
927  for (int i = 0; i < SUB_SIZE; i++) {
928  if (houseBM[i] == 1) {
929  if (focus != -1) { // Re-assignment means no focus
930  return -1;
931  }
932  focus = i;
933  }
934  }
935  return focus;
936 }

◆ get_boxes_bitmap()

void get_boxes_bitmap ( int  array[SUB_SIZE][SUB_SIZE][VALUES])

Stores all columns as a bitmap of numbers.

Parameters
[in,out]arrayThe bitmap of numbers to store the output into
360  {
361  for (int col = 0; col < SUB_SIZE; col++) { // Of main grid
362  for (int row = 0; row < SUB_SIZE; row++) {
363  for (int subCol = 0; subCol < SUB_SIZE; subCol++) { // Of box
364  for (int subRow = 0; subRow < SUB_SIZE; subRow++) {
365  int curRow = row * SUB_SIZE + subRow;
366  int curCol = col * SUB_SIZE + subCol;
367  if (strcmp(grid[curRow][curCol], "-") == 0) {
368  continue;
369  } // If not empty, find and mark value
370  for (int val = 0; val < VALUES; val++) {
371  if (strcmp(grid[curRow][curCol], valid[val + 1]) == 0) {
372  array[row][col][val] = 1;
373  }
374  }
375  }
376  }
377  }
378  }
379 }

◆ get_columns_bitmap()

void get_columns_bitmap ( int  array[VALUES][VALUES])

Stores all columns as a bitmap of numbers.

Parameters
[in,out]arrayThe bitmap of numbers to store the output into
341  {
342  for (int col = 0; col < VALUES; col++) {
343  for (int row = 0; row < VALUES; row++) {
344  if (strcmp(grid[row][col], "-") == 0) {
345  continue;
346  }
347  for (int val = 0; val < VALUES; val++) {
348  if (strcmp(grid[row][col], valid[val + 1]) == 0) {
349  array[col][val] = 1;
350  }
351  }
352  }
353  }
354 }

◆ get_possible_value()

int get_possible_value ( int *  pair)

Assumes there is only one value in the bitmap and retrieves it.

Parameters
[in]pairThe position of the cell to consider
[out]valueThe (0-indexed) value at the cell or -1 if multiple
556  {
557  for (int i = 0; i < VALUES; i++) {
558  if (possible[pair[0]][pair[1]][i] == 1) {
559  return i;
560  }
561  }
562  return -1;
563 }

◆ get_row_bitmap()

void get_row_bitmap ( int  row,
int  array[VALUES] 
)

Stores the current row as a bitmap of numbers.

Parameters
[in]rowThe row number to use as input for conversion
[in,out]arrayThe bitmap of numbers to store the output into
386  {
387  for (int col = 0; col < VALUES; col++) {
388  if (strcmp(grid[row][col], "-") == 0) {
389  continue;
390  }
391  for (int val = 0; val < VALUES; val++) {
392  if (strcmp(grid[row][col], valid[val + 1]) == 0) {
393  array[val] = 1;
394  }
395  }
396  }
397 }

◆ hidden_groups()

int hidden_groups ( int  size,
int  verbosity 
)

Top-level function to find and remove hidden groups.

Loop through all houses and find 2 cells with pair that only appear there, and remove all the other values from those cells

Parameters
[in]sizeThe size of the naked groups to consider
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1621  {
1622  int output = 0;
1623  if (verbosity > 0) {
1624  printf(PROCESS "Finding hidden groups of size %i...\n", size);
1625  }
1626 
1627  // Consider if two or more values appearing between 2 & size
1628  int valueFrequency = 0;
1629  int consideredCount = 0;
1630  int considered[VALUES];
1631 
1632  // ROW
1633  for (int row = 0; row < VALUES; row++) {
1634  consideredCount = 0;
1635  memset(considered, 0, sizeof(considered));
1636  for (int value = 0; value < VALUES; value++) {
1637  valueFrequency = 0;
1638  for (int col = 0; col < VALUES; col++) {
1639  if (possible[row][col][value] == 1) {
1640  valueFrequency++;
1641  }
1642  }
1643  if (valueFrequency >= 2 && valueFrequency <= size) {
1644  considered[value] = 1;
1645  consideredCount += 1;
1646  }
1647  }
1648  // If we consider enough cells, begin to find naked groups
1649  if (valueFrequency >= size) {
1650  output = max(output,
1651  find_group(considered, valueFrequency, size, row, 0, verbosity, 1));
1652  }
1653  }
1654  // COL
1655  for (int col = 0; col < VALUES; col++) {
1656  consideredCount = 0;
1657  memset(considered, 0, sizeof(considered));
1658  for (int value = 0; value < VALUES; value++) {
1659  valueFrequency = 0;
1660  for (int row = 0; row < VALUES; row++) {
1661  if (possible[row][col][value] == 1) {
1662  valueFrequency++;
1663  }
1664  }
1665  if (valueFrequency >= 2 && valueFrequency <= size) {
1666  considered[value] = 1;
1667  consideredCount += 1;
1668  }
1669  }
1670  // If we consider enough cells, begin to find naked groups
1671  if (valueFrequency >= size) {
1672  output = max(output,
1673  find_group(considered, valueFrequency, size, col, 1, verbosity, 1));
1674  }
1675  }
1676  // SUBGRID
1677  for (int subRow = 0; subRow < SUB_SIZE; subRow++) {
1678  for (int subCol = 0; subCol < SUB_SIZE; subCol++) {
1679  consideredCount = 0;
1680  memset(considered, 0, sizeof(considered));
1681  for (int value = 0; value < VALUES; value++) {
1682  valueFrequency = 0;
1683  for (int pos = 0; pos < VALUES; pos++) {
1684  int curRow = subRow * SUB_SIZE + pos / SUB_SIZE;
1685  int curCol = subCol * SUB_SIZE + pos % SUB_SIZE;
1686  if (possible[curRow][curCol][value] == 1) {
1687  valueFrequency++;
1688  }
1689  }
1690  if (valueFrequency >= 2 && valueFrequency <= size) {
1691  considered[value] = 1;
1692  consideredCount += 1;
1693  }
1694  }
1695  // If we consider enough cells, begin to find naked groups
1696  if (valueFrequency >= size) {
1697  output = max(find_group(considered, valueFrequency, size,
1698  (subRow * SUB_SIZE) + subCol, 2, verbosity, 1), output);
1699  }
1700  }
1701  }
1702  return output;
1703 }

◆ initialize_globals()

int initialize_globals ( char  buffer[ROW_BUF_SIZE],
int  isBulk 
)

Allocates and initializes the global variables.

Parameters
[in]bufferA buffer containing the file contents to be parsed from
[in]isBulkWhether the file has a bulk format
[out]status0: Success, 1: Invalid sudoku
154  {
155  // Gets number of values
156  if (!isBulk) {
157  int values = 1;
158  for (int i = 0; buffer[i]; i++) {
159  values += (buffer[i] == ',');
160  }
161  VALUES = values;
162  float root = sqrt((float) VALUES);
163  if (floor(root) != root) { // If not a square number
164  fprintf(stderr, "File doesn't have square number of values\n");
165  return 1;
166  }
167  SUB_SIZE = (int) root;
168  }
169 
170  // Calloc all dependant on values here
171  grid = calloc(VALUES, sizeof(char*));
172  for (int i = 0; i < VALUES; i++) {
173  grid[i] = calloc(VALUES, sizeof(char*));
174  }
175 
176  possible = calloc(VALUES, sizeof(int*));
177  for (int i = 0; i < VALUES; i++) {
178  possible[i] = calloc(VALUES, sizeof(int*));
179  for (int j = 0; j < VALUES; j++) {
180  possible[i][j] = calloc(VALUES, sizeof(int));
181  }
182  }
183 
184  possibleCount = calloc(VALUES, sizeof(int*));
185  for (int i = 0; i < VALUES; i++) {
186  possibleCount[i] = calloc(VALUES, sizeof(int));
187  }
188 
189  valid = calloc(VALUES + 1, sizeof(char*));
190  valid[0] = strdup("-");
191  return 0;
192 }

◆ intersection_removal()

int intersection_removal ( int  verbosity)

Find value in only overlapping house and remove from rest of other houses.

Aggregation of pointing groups and box line reduction techniques.

Parameters
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
944  {
945  int output = 0;
946  if (verbosity > 0) {
947  printf(PROCESS "Using pointing groups...\n");
948  }
949  // SUBGRID
950  int rowBM[SUB_SIZE];
951  int colBM[SUB_SIZE];
952  for (int subRow = 0; subRow < SUB_SIZE; subRow++) {
953  for (int subCol = 0; subCol < SUB_SIZE; subCol++) {
954  for (int value = 1; value <= VALUES; value++) {
955  memset(rowBM, 0, sizeof(rowBM));
956  memset(colBM, 0, sizeof(colBM));
957  for (int pos = 0; pos < VALUES; pos++) {
958  int curRow = subRow * SUB_SIZE + (pos / SUB_SIZE);
959  int curCol = subCol * SUB_SIZE + (pos % SUB_SIZE);
960  if (possible[curRow][curCol][value] == 1) {
961  rowBM[pos / SUB_SIZE] = 1;
962  colBM[pos % SUB_SIZE] = 1;
963  }
964  }
965  int focus = focus_house(rowBM);
966  if (focus != -1) { // Row Ommision by Box (specialize REMOVERS)
967  int printed = 0;
968  int curRow = subRow * SUB_SIZE + focus;
969  for (int i = 0; i < VALUES; i++) {
970  // Not the same box but on the row
971  if (i / SUB_SIZE != subCol && possible[curRow][i][value] == 1) {
972  output = 1;
973  possible[curRow][i][value] = 0;
974  possibleCount[curRow][i] -= 1;
975  if (verbosity > 0) {
976  if (printed == 0) {
977  printf(FOUND "Using pointing groups on %s to remove from"
978  " the box's %ith row:\n", valid[value + 1], focus);
979  printed = 1;
980  }
981  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
982  valid[value + 1], curRow, i);
983  }
984  }
985  }
986  }
987  focus = focus_house(colBM);
988  if (focus != -1) { // Col Ommision by Box (specialize REMOVERS)
989  int printed = 0;
990  int curCol = subCol * SUB_SIZE + focus;
991  for (int i = 0; i < VALUES; i++) {
992  // Not the same box but on the col
993  if (i / SUB_SIZE != subRow && possible[i][curCol][value] == 1) {
994  output = 1;
995  possible[i][curCol][value] = 0;
996  possibleCount[i][curCol] -= 1;
997  if (verbosity > 0) {
998  if (printed == 0) {
999  printf(FOUND "Using pointing groups on %s to remove from"
1000  " the box's %ith column:\n", valid[value + 1], focus);
1001  printed = 1;
1002  }
1003  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
1004  valid[value + 1], i, curCol);
1005  }
1006  }
1007  }
1008  }
1009  }
1010  }
1011  }
1012  if (verbosity > 0) {
1013  printf(PROCESS "Using box line reduction...\n");
1014  }
1015  int houseBM[SUB_SIZE];
1016  // ROW
1017  for (int row = 0; row < VALUES; row++) {
1018  for (int value = 0; value < VALUES; value++) {
1019  memset(houseBM, 0, sizeof(houseBM));
1020  for (int col = 0; col < VALUES; col++) {
1021  if (possible[row][col][value] == 1) {
1022  houseBM[col / SUB_SIZE] = 1;
1023  }
1024  }
1025  int focus = focus_house(houseBM); // Box for the row
1026  if (focus != -1) { // Box Ommision by Row (specialize REMOVERS)
1027  int printed = 0;
1028  int subRow = (row / SUB_SIZE) * SUB_SIZE;
1029  int subCol = focus * SUB_SIZE;
1030  for (int pos = 0; pos < VALUES; pos++) {
1031  // Not the same row but in the box
1032  int curRow = subRow + pos / SUB_SIZE;
1033  int curCol = subCol + pos % SUB_SIZE;
1034  if (curRow != row && possible[curRow][curCol][value] == 1) {
1035  output = 1;
1036  possible[curRow][curCol][value] = 0;
1037  possibleCount[curRow][curCol] -= 1;
1038  if (verbosity > 0) {
1039  if (printed == 0) {
1040  printf(FOUND "Using box line reduction to remove %s from the"
1041  " box except its %ith row:\n", valid[value + 1], focus);
1042  printed = 1;
1043  }
1044  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
1045  valid[value + 1], curRow, curCol);
1046  }
1047  }
1048  }
1049  }
1050  }
1051  }
1052  // COLUMN
1053  for (int col = 0; col < VALUES; col++) {
1054  for (int value = 0; value < VALUES; value++) {
1055  memset(houseBM, 0, sizeof(houseBM));
1056  for (int row = 0; row < VALUES; row++) {
1057  if (possible[row][col][value] == 1) {
1058  houseBM[row / SUB_SIZE] = 1;
1059  }
1060  }
1061  int focus = focus_house(houseBM); // Box for the row
1062  if (focus != -1) { // Box Ommision by Row (specialize REMOVERS)
1063  int printed = 0;
1064  int subRow = focus * SUB_SIZE;
1065  int subCol = (col / SUB_SIZE) * SUB_SIZE;
1066  for (int pos = 0; pos < VALUES; pos++) {
1067  // Not the same col but in the box
1068  int curRow = subRow + pos / SUB_SIZE;
1069  int curCol = subCol + pos % SUB_SIZE;
1070  if (curCol != col && possible[curRow][curCol][value] == 1) {
1071  output = 1;
1072  possible[curRow][curCol][value] = 0;
1073  possibleCount[curRow][curCol] -= 1;
1074  if (verbosity > 0) {
1075  if (printed == 0) {
1076  printf(FOUND "Using box line reduction to remove %s from the"
1077  " box except its %ith column:\n", valid[value + 1], focus);
1078  printed = 1;
1079  }
1080  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
1081  valid[value + 1], curRow, curCol);
1082  }
1083  }
1084  }
1085  }
1086  }
1087  }
1088  return output;
1089 }

◆ main()

int main ( int  argc,
char *  argv[] 
)

Called initially and encompasses the program's functionality.

Parameters
[in]argcThe number of command line arguments
[in]argvThe command line arguments
1999  {
2000  // Read and store arguments in easy to use manner
2001  clock_t time;
2002  char* inCsv = NULL;
2003  char* outCsv = NULL;
2004  char* missing = NULL;
2005  int verbosity = 0; // [0, 1, 2]
2006  int bulkFlag = 0; // [0, 1]
2007  int timeFlag = 0; // [0, 1]
2008  int forceFlag = 0; // [-1, 0, 1]
2009  int debugFlag = 0; // [0, 1]
2010  if (process_arguments(argc, argv, &verbosity, &bulkFlag, &timeFlag,
2011  &forceFlag, &debugFlag, &inCsv, &outCsv, &missing) != 0) {
2012  fprintf(stderr, "\nUsage: ./sudoku.out [[-v]|[-vv]] [-b] [-t] [[-f]|[-nf]]"
2013  " [-d] [-o <new_csv_name>] [-m <missing_arg>] <csv_path>\n"
2014  "\nOptions:\n"
2015  " -v verbose output, lists steps in solving\n"
2016  " -vv extra verbose output, lists brute force steps\n"
2017  " -b solves a batch execution of sudokus (one per line)\n"
2018  " -t shows the benchmarked time for reading and execution\n"
2019  " -f uses brute force for solving immediately\n"
2020  " -nf disables use of brute force\n"
2021  " -d debug mode, shows all possibilities for each cell\n"
2022  " -o exports solved grid to a new csv\n"
2023  " -m provide missing argument if not in the grid\n");
2024  return 1;
2025  }
2026 
2027  // Start benchmarking
2028  if (timeFlag == 1) {
2029  time = clock();
2030  }
2031 
2032  // Load up contents of the file into grid
2033  switch (read_file(inCsv, missing, bulkFlag, forceFlag)) {
2034  case 1: // After Initialized globals
2035  free_globals();
2036  case -1:
2037  fprintf(stderr, "Error reading file\n");
2038  return 1;
2039  }
2040 
2041  // Bulk processing end
2042  if (bulkFlag == 1) {
2043  printf("Bulk file processed\n");
2044  if (timeFlag == 1) {
2045  time = clock() - time; // For benchmarking
2046  printf("Time elasped: %f\n", ((double) time) / CLOCKS_PER_SEC);
2047  }
2048  return 0;
2049  }
2050 
2051  // Show the contents of the file
2052  printf("Input:");
2053  print_grid();
2054 
2055  // Calculate the result
2056  if (forceFlag == 1) {
2057  brute_force(verbosity);
2058  } else {
2059  int cellsLeft = 0;
2060  pre_process(&cellsLeft);
2061  while (process_next(&cellsLeft, verbosity, forceFlag) != 1) {}
2062  }
2063 
2064  // Display solution
2065  printf("\nOutput:");
2066  print_grid();
2067  if (debugFlag == 1) {
2068  print_possible();
2069  }
2070  if (outCsv != NULL) { // If output set, export
2071  if (export_grid(outCsv) != 0) {
2072  free_globals();
2073  return 1;
2074  }
2075  }
2076  free_globals();
2077 
2078  // End benchmarking
2079  if (timeFlag == 1) {
2080  time = clock() - time; // For benchmarking
2081  printf("Time elasped: %f\n", ((double) time) / CLOCKS_PER_SEC);
2082  }
2083 }

◆ make_pair()

int* make_pair ( int  row,
int  col 
)

Dynamically allocated and initializes a pair of values.

Parameters
[in]row,colThe values to fill the pair with
[out]pairAn int array of length two storing row and col
529  {
530  int* pair = calloc(2, sizeof(int));
531  pair[0] = row;
532  pair[1] = col;
533  return pair;
534 }

◆ max()

int max ( int  a,
int  b 
)

Returns the maximum of two integers.

Parameters
[in]a,bThe two integers to compare
[out]maxThe bigger number from a and b
912  {
913  if (a >= b) {
914  return a;
915  } else {
916  return b;
917  }
918 }

◆ naked_groups()

int naked_groups ( int  size,
int  verbosity 
)

Top-level function to find and remove naked groups.

Loop through all houses and find cells containing same group of values, and remove from these values from all other linked houses

Parameters
[in]sizeThe size of the naked groups to consider
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1547  {
1548  int output = 0;
1549  if (verbosity > 0) {
1550  printf(PROCESS "Finding naked groups of size %i...\n", size);
1551  }
1552 
1553  // Consider if two or more cells with possibleCount between 2 & size
1554  int count = 0;
1555  int considered[VALUES];
1556 
1557  // ROW
1558  for (int row = 0; row < VALUES; row++) {
1559  count = 0;
1560  memset(considered, 0, sizeof(considered));
1561  for (int col = 0; col < VALUES; col++) {
1562  if (possibleCount[row][col] >= 2 && possibleCount[row][col] <= size) {
1563  count++;
1564  considered[col] = 1;
1565  }
1566  }
1567  // If we consider enough cells, begin to find naked groups
1568  if (count >= size) {
1569  output = max(output,
1570  find_group(considered, count, size, row, 0, verbosity, 0));
1571  }
1572  }
1573  // COL
1574  for (int col = 0; col < VALUES; col++) {
1575  count = 0;
1576  memset(considered, 0, sizeof(considered));
1577  for (int row = 0; row < VALUES; row++) {
1578  if (possibleCount[row][col] >= 2 && possibleCount[row][col] <= size) {
1579  count++;
1580  considered[row] = 1;
1581  }
1582  }
1583  // If we consider enough cells, begin to find naked groups
1584  if (count >= size) {
1585  output = max(output,
1586  find_group(considered, count, size, col, 1, verbosity, 0));
1587  }
1588  }
1589  // SUBGRID
1590  for (int subRow = 0; subRow < SUB_SIZE; subRow++) {
1591  for (int subCol = 0; subCol < SUB_SIZE; subCol++) {
1592  count = 0;
1593  memset(considered, 0, sizeof(considered));
1594  for (int pos = 0; pos < VALUES; pos++) {
1595  int curRow = subRow * SUB_SIZE + pos / SUB_SIZE;
1596  int curCol = subCol * SUB_SIZE + pos % SUB_SIZE;
1597  int possibilities = possibleCount[curRow][curCol];
1598  if (possibilities >= 2 && possibilities <= size) {
1599  count++;
1600  considered[pos] = 1;
1601  }
1602  }
1603  // If we consider enough cells, begin to find naked groups
1604  if (count >= size) {
1605  output = max(find_group(considered, count, size,
1606  (subRow * SUB_SIZE) + subCol, 2, verbosity, 0), output);
1607  }
1608  }
1609  }
1610  return output;
1611 }

◆ pre_process()

void pre_process ( int *  cellsLeft)

Initializes a bitmap to hold possible values.

Parameters
[in,out]cellsLeftThe number of cells left to solve the sudoku
403  {
404  int allCols[VALUES][VALUES]; // Stores all columns as bitmap before main loop
405  memset(allCols, 0, sizeof(allCols)); // Initialize before use
406  get_columns_bitmap(allCols);
407 
408  int boxes[SUB_SIZE][SUB_SIZE][VALUES];
409  memset(boxes, 0, sizeof(boxes)); // Initialize before use
410  get_boxes_bitmap(boxes);
411 
412  int curRow[VALUES]; // Stores row values as bitmap in the currentRow
413  for (int row = 0; row < VALUES; row++) {
414  memset(curRow, 0, sizeof(curRow)); // Initialize before (re)use
415  get_row_bitmap(row, curRow);
416 
417  // Update the main bitmap to store possibilities per cell
418  for (int col = 0; col < VALUES; col++) {
419  for (int val = 0; val < VALUES; val++) {
420  // Check if already full, is in the row, column or box
421  if (strcmp(grid[row][col], "-") == 0
422  && curRow[val] == 0 && allCols[col][val] == 0
423  && boxes[row / SUB_SIZE][col / SUB_SIZE][val] == 0) {
424  if (possibleCount[row][col] == 0) {
425  *cellsLeft += 1;
426  }
427  possible[row][col][val] = 1; // Mark as possible if none match
428  possibleCount[row][col] += 1;
429  }
430  }
431  }
432  }
433 }

◆ print_array()

void print_array ( int *  array,
int  len,
int  isPadded 
)

Prints a grid cell's possible contents.

Parameters
[in]arrayThe array to be printed out
[in]lenThe length of the array
[in]isPaddedWhether the output should be padded
444  {
445  printf("{");
446  int count = 0;
447  for (int i = 0; i < len; i++) {
448  if (array[i] == 1) {
449  if (count == 0) {
450  printf("%s", valid[i + 1]);
451  count += 1;
452  } else {
453  printf(" %s", valid[i + 1]);
454  count += 2;
455  }
456  }
457  }
458  printf("}");
459  // Prints appropriate padding for formatted output
460  if (isPadded) {
461  count += 2;
462  for (; count < PAD_SIZE; count++) {
463  printf(" ");
464  }
465  }
466 }

◆ print_line()

void print_line ( char  type,
int  length 
)

Prints a line of given length with a given character.

Parameters
[in]typeThe character to use to print the line
[in]lengthThe length of the line to be printed
492  {
493  for (int i = 0; i < length; i++) {
494  printf("%c", type);
495  }
496  printf("\n");
497 }

◆ process_arguments()

int process_arguments ( int  argc,
char *  argv[],
int *  verbosity,
int *  bulkFlag,
int *  timeFlag,
int *  forceFlag,
int *  debugFlag,
char **  inCsv,
char **  outCsv,
char **  missing 
)

Processes the command line arguments provided.

Parameters
[in]argcThe number of command line arguments
[in]argvThe command line arguments
[in,out]verbosityHow verbose the output has been set to
[in,out]bulkFlagWhether we are evaluating a bulk file
[in,out]timeFlagWhether we are benchmarking the time taken
[in,out]forceFlagHow much brute force we can apply
[in,out]debugFlagWhether to print the possibilities at the end
[in,out]inCsvThe input CSV file to read from
[in,out]outCsvThe output CSV file to write to (if at all)
[in,out]missingThe missing token to use (if required)
[out]status0: Success, 1: Invalid arguments
45  {
46  // Check if enough arguments provided
47  if (argc < 2) {
48  fprintf(stderr, "Not enough arguments\n");
49  return 1;
50  }
51  // Go through all arguments (not first since that's the command)
52  int inFound = 0;
53  for (int i = 1; i < argc; i++) {
54  if (strcmp("-v", argv[i]) == 0) { // Verbose flag
55  if (*verbosity < 1) { // Check if already specified
56  *verbosity = 1;
57  } else if (*verbosity == 1) {
58  fprintf(stderr, "Duplicate -v flag found\n");
59  return 1;
60  }
61  } else if (strcmp("-vv", argv[i]) == 0) { // Extra Verbose flag
62  if (*verbosity < 2) { // Check if already specified (overwrites -v)
63  *verbosity = 2;
64  } else {
65  fprintf(stderr, "Duplicate -vv flag found\n");
66  return 1;
67  }
68  } else if (strcmp("-t", argv[i]) == 0) { // Time flag
69  if (*timeFlag == 0) { // Check if already specified
70  *timeFlag = 1;
71  } else {
72  fprintf(stderr, "Duplicate -t flag found\n");
73  return 1;
74  }
75  } else if (strcmp("-b", argv[i]) == 0) { // Bulk flag
76  if (*bulkFlag == 0) { // Check if already specified
77  *bulkFlag = 1;
78  } else {
79  fprintf(stderr, "Duplicate -b flag found\n");
80  return 1;
81  }
82  } else if (strcmp("-d", argv[i]) == 0) { // Debug flag
83  if (*debugFlag == 0) { // Check if already specified
84  *debugFlag = 1;
85  } else {
86  fprintf(stderr, "Duplicate -d flag found\n");
87  return 1;
88  }
89  } else if (strcmp("-o", argv[i]) == 0) { // Out flag + path
90  if (*outCsv == NULL) { // Check if already specified
91  if (++i != argc && argv[i][0] != '-') { // Check next argument
92  *outCsv = argv[i];
93  } else {
94  fprintf(stderr, "Output file path for -o not specified\n");
95  return 1;
96  }
97  } else {
98  fprintf(stderr, "Duplicate -o flag found\n");
99  return 1;
100  }
101  } else if (strcmp("-m", argv[i]) == 0) { // Mising flag + arg
102  if (*missing == NULL) { // Check if already specified
103  if (++i != argc && argv[i][0] != '-') { // Check next argument
104  *missing = argv[i];
105  } else {
106  fprintf(stderr, "Missing argument for -m not specified\n");
107  return 1;
108  }
109  } else {
110  fprintf(stderr, "Duplicate -m flag found\n");
111  return 1;
112  }
113  } else if (strcmp("-f", argv[i]) == 0) { // Force flag [exclusive]
114  if (*forceFlag == 0) { // Check if already specified
115  *forceFlag = 1;
116  } else {
117  if (*forceFlag == 1) {
118  fprintf(stderr, "Duplicate -f flag found\n");
119  } else {
120  fprintf(stderr, "Conflicting -f flag found\n");
121  }
122  return 1;
123  }
124  } else if (strcmp("-nf", argv[i]) == 0) { // Force flag [disable]
125  if (*forceFlag == 0) { // Check if already specified
126  *forceFlag = -1;
127  } else {
128  if (*forceFlag == -1) {
129  fprintf(stderr, "Duplicate -nf flag found\n");
130  } else {
131  fprintf(stderr, "Conflicting -nf flag found\n");
132  }
133  return 1;
134  }
135  } else { // Input path
136  if (inFound == 0) { // Check if already specified
137  *inCsv = argv[i];
138  inFound++;
139  } else {
140  fprintf(stderr, "Unexpected argument found\n");
141  return 1;
142  }
143  }
144  }
145  return 0;
146 }

◆ process_next()

int process_next ( int *  cellsLeft,
int  verbosity,
int  forceFlag 
)

Process the next iteration of the loop.

Parameters
[in,out]cellsLeftThe number of cells left to solve the sudoku
[in]verbosityThe verbosity of the output (print or hide)
[in]forceFlagHow much brute force we can apply
[out]status0: Success, 1: Failure (Stalemate)
1752  {
1753  if (*cellsLeft == 0) {
1754  return 1;
1755  }
1756  int value = -1;
1757  int* pair = find_single(); // Find cell with only one left
1758  if (pair == NULL) { // If not found, try other methods
1759  for (int mode = 0; mode < 3; mode++) { // Loop through row, col, box
1760  if ((pair = unique_in_range(mode, &value, verbosity)) != NULL) {
1761  break; // If found, stop checking
1762  }
1763  }
1764  if (pair == NULL) { // If there still is no pair found, remove redundancy
1765  // If nothing was eliminated (stalemate), then brute force or fail
1766  if (eliminate_redundant(verbosity) == 1) {
1767  return 0;
1768  } else {
1769  if (verbosity > 0 && forceFlag == -1) {
1770  fprintf(stderr, "Didn't find next pair. Cells left: %i\n",
1771  *cellsLeft);
1772  }
1773  if (forceFlag != -1) { // If brute force not disabled, use it to finish
1774  brute_force(verbosity);
1775  }
1776  return 1;
1777  }
1778  }
1779  }
1780  // Only for find_single, since unique in range sets appropriate value
1781  if (value == -1) {
1782  value = get_possible_value(pair);
1783  if (verbosity > 0 && value != -1) {
1784  printf(ADD "Added %s at (%i, %i) as single possibility\n",
1785  valid[value + 1], pair[0], pair[1]);
1786  }
1787  }
1788  if (value == -1) {
1789  fprintf(stderr, "Empty cell processed\n");
1790  return 1;
1791  }
1792 
1793  grid[pair[0]][pair[1]] = valid[value + 1];
1794  // Clear filled cell of all possibilities
1795  for (int val = 0; val < VALUES; val++) {
1796  possible[pair[0]][pair[1]][val] = 0;
1797  }
1798  possibleCount[pair[0]][pair[1]] = 0;
1799  *cellsLeft -= 1;
1800 
1801  int mask[VALUES];
1802  memset(mask, 0, sizeof(mask));
1803  mask[pair[1]] = 1;
1804  remove_value_from_row(pair[0], value, mask, 0);
1805  mask[pair[1]] = 0;
1806  mask[pair[0]] = 1;
1807  remove_value_from_column(pair[1], value, mask, 0);
1808  mask[pair[0]] = 0;
1809  remove_value_from_box(pair[0], pair[1], value, mask, 0);
1810 
1811  free(pair);
1812  return 0;
1813 }

◆ process_token()

int process_token ( int *  tokenCount,
char *  token,
int  row,
int  col 
)

Sanitize, and set the grid to reference a valid token.

Parameters
[in,out]tokenCountThe number of tokens currently parsed
[in]tokenA candidate token for the array of valid tokens
[in]row,colThe position of the token
[out]status0: Success, 1: Invalid sudoku
252  {
253  if (strchr(token, '\n') != NULL) { // Sanitize token
254  printf("\\n converted at (%i, %i)\n", row, col);
255  *strchr(token, '\n') = '\0';
256  }
257  if (strchr(token, '\r') != NULL) {
258  *strchr(token, '\r') = '\0';
259  }
260 
261  if (addToValid(tokenCount, token) != 0) { // If not in valid, add to it
262  return 1;
263  };
264 
265  for (int i = 0; i < *tokenCount; i++) { // Set reference to valid string
266  if (strcmp(valid[i], token) == 0) {
267  grid[row][col] = valid[i];
268  }
269  }
270  return 0;
271 }

◆ read_bulk()

int read_bulk ( const char *  fileName,
int  forceFlag 
)

Reads, solves and checks all sudokus in a bulk sudoku file.

This is dependant on single character value tokens as opposed to strings.

Parameters
[in]fileNameThe path of the file to read from
[out]status-1: Invalid pre-global, 0: Success, 1: Failure
1872  {
1873  FILE* input = fopen(fileName, "r");
1874  if (input == NULL) {
1875  fprintf(stderr, "Bulk CSV file could not be opened for reading\n");
1876  return -1;
1877  }
1878  int solvedCount = 0;
1879  char buffer[ROW_BUF_SIZE];
1880  char* current = buffer;
1881  fgets(buffer, ROW_BUF_SIZE, input); // Skip first line
1882  while (fgets(buffer, ROW_BUF_SIZE, input)) {
1883  current = buffer;
1884  // Use buffer upto comma char to parse in question
1885  int len = strcspn(current, ",");
1886  // Check if len corresponds to square grid
1887  VALUES = (int) sqrt(len);
1888  float root = sqrt((float) VALUES);
1889  if (floor(root) != root) { // If not a square number
1890  fprintf(stderr, "File doesn't have square number of values\n");
1891  fclose(input);
1892  return 1;
1893  }
1894  SUB_SIZE = (int) root;
1895  if (initialize_globals(NULL, 1) != 0) { // Don't need to provide buffer
1896  return -1;
1897  }
1898 
1899  // Parse into grid (0 equivalent to '-')
1900  int tokenCount = 1;
1901  for (int curChar = 0; curChar < len; curChar++) { // All char in question
1902  // Get character, make it a token and process it
1903  char inChar = current[curChar];
1904  if (inChar == '0') { // Replace 0 with '-'
1905  inChar = '-';
1906  }
1907  char token[] = {inChar, '\0'}; // Make char into temp string
1908 
1909  if (process_token(&tokenCount, token,
1910  curChar / VALUES, curChar % VALUES) != 0) {
1911  fclose(input);
1912  return 1;
1913  }
1914  }
1915 
1916  if (tokenCount == VALUES) { // If missing one value
1917  valid[VALUES] = strdup("X");
1918  } else if (tokenCount < VALUES) { // If not enough values
1919  fprintf(stderr, "Not enough values provided, expected: %i\n", VALUES);
1920  return 1;
1921  }
1922 
1923  // Calculate the result
1924  if (forceFlag == 1) {
1925  brute_force(0);
1926  } else {
1927  int cellsLeft = 0;
1928  pre_process(&cellsLeft);
1929  while (process_next(&cellsLeft, 0, forceFlag) != 1) {}
1930  }
1931 
1932  // Set current to be first char of solution
1933  current = &buffer[len + 1];
1934  int diff = strcspn(current, ",") - len; // counts potential \n
1935  if (diff < 0) { // If solution smaller
1936  fprintf(stderr, "Solution smaller than the question\n");
1937  fclose(input);
1938  return 1;
1939  }
1940 
1941  // Check if solution matches
1942  int matches = 1;
1943  char missing = 'X';
1944  for (int curChar = 0; curChar < len - diff; curChar++) { // Solution's chars
1945  char inChar = current[curChar];
1946  if (inChar == '0') { // Replace 0 with '-'
1947  inChar = '-';
1948  }
1949 
1950  // Check this token against one in grid (only need to compare first char)
1951  if (inChar != grid[curChar / VALUES][curChar % VALUES][0]) {
1952  if (grid[curChar / VALUES][curChar % VALUES][0] == 'X') {
1953  missing = inChar;
1954  } else {
1955  matches = 0; // Set to not matching
1956  break;
1957  }
1958  }
1959  }
1960 
1961  if (!matches) {
1962  fprintf(stderr, "Sudoku solution does not match expected answer\n");
1963  fclose(input);
1964  return 1;
1965  }
1966 
1967  free_globals(); // Ready for next sudoku
1968 
1969  solvedCount += 1;
1970  printf("Solved %i\n", solvedCount);
1971  }
1972  fclose(input);
1973 
1974  return 0;
1975 }

◆ read_csv()

int read_csv ( const char *  fileName,
char *  missing 
)

Reads from a csv file and loads into the grid.

Parameters
[in]fileNameThe path to the file to use as input and read from
[in]missingThe missing token to use (if required)
[out]status-1: Invalid pre-global, 0: Success, 1: Invalid
279  {
280  FILE* input = fopen(fileName, "r");
281  if (input == NULL) {
282  fprintf(stderr, "CSV file could not be opened for reading\n");
283  return -1;
284  }
285  char buffer[ROW_BUF_SIZE];
286  int row = 0;
287 
288  if (initialize_globals(fgets(buffer, ROW_BUF_SIZE, input), 0) != 0) {
289  return -1;
290  }
291  int tokenCount = 1;
292 
293  do { // Read through file
294  buffer[strcspn(buffer, "\n")] = 0;
295  char* token = strtok(buffer, ","); // Split the line with ',' as delimiter
296  int col = 0;
297  while (token != NULL && col < VALUES) { // Store all values into the grid
298  if (process_token(&tokenCount, token, row, col) != 0) {
299  fclose(input);
300  return 1;
301  }
302  token = strtok(NULL, ",");
303  col++;
304  }
305  if (col < VALUES) { // If fewer values than expected detected
306  fprintf(stderr,"Too few columns in row %i: Expected %i, not %i\n",
307  row + 1, VALUES, col);
308  fclose(input);
309  return 1;
310  }
311  row++;
312  memset(buffer, 0, sizeof(buffer));
313  } while (fgets(buffer, ROW_BUF_SIZE, input) && row < VALUES);
314  fclose(input);
315  if (row < VALUES) { // If fewer rows than expected detected
316  fprintf(stderr, "Too few rows in grid: Expected %i, not %i\n", VALUES, row);
317  return 1;
318  }
319 
320  if (tokenCount == VALUES) { // If missing one value
321  if (missing != NULL) {
322  valid[VALUES] = strdup(missing);
323  } else {
324  fprintf(stderr, "Missing one values so X substituted\n");
325  valid[VALUES] = strdup("X");
326  }
327  } else if (tokenCount < VALUES) { // If not enough values
328  fprintf(stderr, "Not enough values provided, expected: %i\n", VALUES);
329  return 1;
330  }
331  return 0;
332 }

◆ read_file()

int read_file ( char *  inCsv,
char *  missing,
int  isBulk,
int  forceFlag 
)

Adapter function to handle the reading of a file depending on its structure.

Parameters
[in]inCsvThe input CSV file to read from
[in]missingThe missing token to use (if required)
[in]bulkFlagWhether we are evaluating a bulk file
[in]forceFlagHow much brute force we can apply
[out]status-1: Invalid pre-global, 0: Success, 1: Failure
1985  {
1986  if (isBulk) {
1987  return read_bulk(inCsv, forceFlag);
1988  } else {
1989  return read_csv(inCsv, missing);
1990  }
1991 }

◆ remove_hidden_group()

int remove_hidden_group ( int *  rows,
int *  cols,
int *  shared,
int  size,
int  mode,
int  verbosity 
)

Removes values according to the contents of a known hidden group.

Parameters
[in]rows,colsThe positions of the cells in the group
[in]sharedThe values shared by the group
[in]sizeThe number of cells in the group
[in]modeThe mode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1169  {
1170  int output = 0;
1171  if (verbosity > 0) {
1172  printf(FOUND "Found hidden group (");
1173  for (int i = 0; i < size - 1; i++) {
1174  printf("%s, ", valid[shared[i] + 1]);
1175  }
1176  printf("%s) using %s at ", valid[shared[size - 1] + 1], MODE_LABELS[mode]);
1177  for (int i = 0; i < size; i++) {
1178  printf("(%i, %i) ", rows[i], cols[i]);
1179  }
1180  printf("\n");
1181  }
1182 
1183  // Use shared as mask
1184  for (int pos = 0; pos < size; pos++) {
1185  for (int value = 0; value < VALUES; value++) {
1186  // Skip values in shared (use as mask)
1187  int skip = 0;
1188  for (int i = 0; i < size; i++) {
1189  if (value == shared[i]) {
1190  skip = 1;
1191  break;
1192  }
1193  }
1194  if (skip == 1) {
1195  continue;
1196  }
1197  // Remove possibility from cells
1198  if (possible[rows[pos]][cols[pos]][value] == 1) {
1199  output = 1;
1200  possible[rows[pos]][cols[pos]][value] = 0;
1201  possibleCount[rows[pos]][cols[pos]] -= 1;
1202  if (verbosity > 0) {
1203  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
1204  valid[value + 1], rows[pos], cols[pos]);
1205  }
1206  }
1207  }
1208  }
1209  return output;
1210 }

◆ remove_naked_group()

int remove_naked_group ( int *  rows,
int *  cols,
int *  shared,
int  size,
int  mode,
int  verbosity 
)

Removes values according to the contents of a known naked group.

Parameters
[in]rows,colsThe positions of the cells in the group
[in]sharedThe values shared by the group
[in]sizeThe number of cells in the group
[in]modeThe mode of operation {0: row, 1: column, 2: box}
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
1101  {
1102  int output = 0;
1103  if (verbosity > 0) {
1104  printf(FOUND "Found naked group (");
1105  for (int i = 0; i < size - 1; i++) {
1106  printf("%s, ", valid[shared[i] + 1]);
1107  }
1108  printf("%s) using %s at ", valid[shared[size - 1] + 1], MODE_LABELS[mode]);
1109  for (int i = 0; i < size; i++) {
1110  printf("(%i, %i) ", rows[i], cols[i]);
1111  }
1112  printf("\n");
1113  }
1114 
1115  int mask[VALUES];
1116  memset(mask, 0, sizeof(mask));
1117  switch (mode) {
1118  case 0: { // ROW
1119  // Apply mask to all columns
1120  for (int i = 0; i < size; i++) {
1121  mask[cols[i]] = 1;
1122  }
1123  for (int i = 0; i < size; i++) {
1124  output = max(output,
1125  remove_value_from_row(rows[i], shared[i], mask, verbosity));
1126  }
1127  break;
1128  }
1129  case 1: { // COL
1130  // Apply mask to all rows
1131  for (int i = 0; i < size; i++) {
1132  mask[rows[i]] = 1;
1133  }
1134  for (int i = 0; i < size; i++) {
1135  output = max(output,
1136  remove_value_from_column(cols[i], shared[i], mask, verbosity));
1137  }
1138  break;
1139  }
1140  case 2: { // SUBGRID (same one)
1141  // Apply mask to all positions
1142  for (int i = 0; i < size; i++) {
1143  mask[SUB_SIZE * (rows[i] % SUB_SIZE) + cols[i] % SUB_SIZE] = 1;
1144  }
1145  for (int i = 0; i < size; i++) {
1146  output = max(output,
1147  remove_value_from_box(rows[i], cols[i], shared[i], mask, verbosity));
1148  }
1149  break;
1150  }
1151  default: {
1152  fprintf(stderr, "Invalid mode for removing naked group.");
1153  exit(1);
1154  }
1155  }
1156  return output;
1157 }

◆ remove_value_from_box()

int remove_value_from_box ( int  row,
int  col,
int  value,
int  mask[VALUES],
int  verbosity 
)

Removes a value from the possibilities of all others in the same box.

Parameters
[in]row,colThe position of a cell in the box to be removed from
[in]valueThe value to be removed
[in]maskA bitmap with positions to ignore in removal
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
766  {
767  int output = 0;
768  int subRow = (row / SUB_SIZE) * SUB_SIZE;
769  int subCol = (col / SUB_SIZE) * SUB_SIZE;
770  for (int i = 0; i < VALUES; i++) {
771  int inRow = i / SUB_SIZE;
772  int inCol = i % SUB_SIZE;
773  if (mask[i] == 1 || (inCol + subCol == col && inRow + subRow == row)) {
774  continue;
775  }
776  if (possible[subRow + inRow][subCol + inCol][value] == 1) {
777  output = 1;
778  possible[subRow + inRow][subCol + inCol][value] = 0;
779  possibleCount[subRow + inRow][subCol + inCol] -= 1;
780  if (verbosity > 0) {
781  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
782  valid[value + 1], subRow + inRow, subCol + inCol);
783  }
784  }
785  }
786  return output;
787 }

◆ remove_value_from_column()

int remove_value_from_column ( int  col,
int  value,
int  mask[VALUES],
int  verbosity 
)

Removes a value from the possibilities of all others in the same column.

Parameters
[in]colThe column to be removed from
[in]valueThe value to be removed
[in]maskA bitmap with positions to ignore in removal
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
737  {
738  int output = 0;
739  for (int row = 0; row < VALUES; row++) {
740  if (mask[row] == 1) {
741  continue;
742  }
743  // Only if the value is in its possibilities
744  if (possible[row][col][value] == 1) {
745  output = 1;
746  possible[row][col][value] = 0;
747  possibleCount[row][col] -= 1;
748  if (verbosity > 0) {
749  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
750  valid[value + 1], row, col);
751  }
752  }
753  }
754  return output;
755 }

◆ remove_value_from_row()

int remove_value_from_row ( int  row,
int  value,
int  mask[VALUES],
int  verbosity 
)

Removes a value from the possibilities of all others in the same row.

Parameters
[in]rowThe row to be removed from
[in]valueThe value to be removed
[in]maskA bitmap with positions to ignore in removal
[in]verbosityThe verbosity of the output (print or hide)
[out]removed(Bool) 0: False, 1: True
708  {
709  int output = 0;
710  for (int col = 0; col < VALUES; col++) {
711  if (mask[col] == 1) {
712  continue;
713  }
714  // Only if the value is in its possibilities
715  if (possible[row][col][value] == 1) {
716  output = 1;
717  possible[row][col][value] = 0;
718  possibleCount[row][col] -= 1;
719  if (verbosity > 0) {
720  printf(REMOVE "Eliminated: %s at (%i, %i)\n",
721  valid[value + 1], row, col);
722  }
723  }
724  }
725  return output;
726 }

◆ unique_in_box()

int* unique_in_box ( int *  value)

Gets position which is the only place for a possible value in a box.

]

Parameters
[in,out]valueThe value in this position (0-indexed)
[out]pairThe position of the unique cell or NULL
631  {
632  int checkArray[VALUES][2]; // checkArray[X][0] = frequency, [1] = last found
633  for (int col = 0; col < SUB_SIZE; col++) { // Of main grid
634  for (int row = 0; row < SUB_SIZE; row++) {
635  memset(checkArray, 0, sizeof(checkArray)); // Reset array
636  for (int subCol = 0; subCol < SUB_SIZE; subCol++) { // Of box
637  for (int subRow = 0; subRow < SUB_SIZE; subRow++) {
638  int curRow = row * SUB_SIZE + subRow;
639  int curCol = col * SUB_SIZE + subCol;
640  // Go through possibilities in cell and add to checkArray
641  for (int val = 0; val < VALUES; val++) {
642  if (possible[curRow][curCol][val] == 1) {
643  checkArray[val][0] += 1; // Increase the frequency of the val
644  int curIndex = curRow * SUB_SIZE + curCol;
645  checkArray[val][1] = curIndex; // Set last found to curIndex
646  }
647  }
648  }
649  }
650  // Go through all values and see if frequency is 1, if so, output it
651  for (int val = 0; val < VALUES; val++) {
652  if (checkArray[val][0] == 1) {
653  *value = val;
654  int pairRow = (row * SUB_SIZE) + (checkArray[val][1] / SUB_SIZE);
655  int pairCol = (col * SUB_SIZE) + (checkArray[val][1] % SUB_SIZE);
656  return make_pair(pairRow, pairCol);
657  }
658  }
659  }
660  }
661  return NULL;
662 }

◆ unique_in_col()

int* unique_in_col ( int *  value)

Gets position which is only place for a possible value in a col.

Parameters
[in,out]valueThe value in this position (0-indexed)
[out]pairThe position of the unique cell or NULL
602  {
603  int checkArray[VALUES][2]; // checkArray[X][0] = frequency, [1] = last found
604  for (int col = 0; col < VALUES; col++) { // Check in each box
605  memset(checkArray, 0, sizeof(checkArray)); // Reset array
606  for (int row = 0; row < VALUES; row++) { // Go through cells in box
607  // Go through possibilities in cell and add to checkArray
608  for (int val = 0; val < VALUES; val++) {
609  if (possible[row][col][val] == 1) {
610  checkArray[val][0] += 1; // Increase the frequency of the value
611  checkArray[val][1] = row; // Set last found to this row
612  }
613  }
614  }
615  // Go through all values and see if frequency is 1, if so, output it
616  for (int val = 0; val < VALUES; val++) {
617  if (checkArray[val][0] == 1) {
618  *value = val;
619  return make_pair(checkArray[val][1], col);
620  }
621  }
622  }
623  return NULL;
624 }

◆ unique_in_range()

int* unique_in_range ( int  mode,
int *  value,
int  verbosity 
)

Gets position which is only place for a possible value in a house.

Uses the other three "unique_in" functions to achieve this.

Parameters
[in,out]valueThe value in this position
[out]pairThe position of the unique cell or NULL
670  {
671  int* output = NULL;
672  if ((output = unique_in_row(value)) != NULL) {
673  if (verbosity == 1) {
674  printf(ADD "Added %s at (%i, %i) as unique in row\n",
675  valid[*value + 1], output[0], output[1]);
676  }
677  return output;
678  }
679  if ((output = unique_in_col(value)) != NULL) {
680  if (verbosity == 1) {
681  printf(ADD "Added %s at (%i, %i) as unique in column\n",
682  valid[*value + 1], output[0], output[1]);
683  }
684  return output;
685  }
686  if ((output = unique_in_box(value)) != NULL) {
687  if (verbosity == 1) {
688  printf(ADD "Added %s at (%i, %i) as unique in box\n",
689  valid[*value + 1], output[0], output[1]);
690  }
691  return output;
692  }
693  return NULL;
694 }

◆ unique_in_row()

int* unique_in_row ( int *  value)

Gets position which is only place for a possible value in a row.

Parameters
[in,out]valueThe value in this position (0-indexed)
[out]pairThe position of the unique cell or NULL
573  {
574  int checkArray[VALUES][2]; // checkArray[X][0] = frequency, [1] = last found
575  for (int row = 0; row < VALUES; row++) { // Check in each row
576  memset(checkArray, 0, sizeof(checkArray)); // Reset array
577  for (int col = 0; col < VALUES; col++) { // Update value in checkArray
578  // Go through possibilities in cell and add to checkArray
579  for (int val = 0; val < VALUES; val++) {
580  if (possible[row][col][val] == 1) {
581  checkArray[val][0] += 1; // Increase the frequency of the value
582  checkArray[val][1] = col; // Set last found to this column
583  }
584  }
585  }
586  // Go through all values and see if frequency is 1, if so, output it
587  for (int val = 0; val < VALUES; val++) {
588  if (checkArray[val][0] == 1) {
589  *value = val;
590  return make_pair(row, checkArray[val][1]);
591  }
592  }
593  }
594  return NULL;
595 }

◆ valid_in_box()

int valid_in_box ( int  value,
int  row,
int  col 
)

Checks if the value is valid (no duplicates) in the given box.

Parameters
[in]valueThe value to check the validity of
[in]row,colA position in the box to check the validity within
[out]valid(Bool) 0: False, 1: True
828  {
829  int subRow = SUB_SIZE * (row / SUB_SIZE);
830  int subCol = SUB_SIZE * (col / SUB_SIZE);
831  for (int pos = 0; pos < VALUES; pos++) {
832  if (grid[subRow + (pos / SUB_SIZE)][subCol + (pos % SUB_SIZE)]
833  == valid[value]) {
834  return 1;
835  }
836  }
837  return 0;
838 }

◆ valid_in_col()

int valid_in_col ( int  value,
int  col 
)

Checks if the value is valid (no duplicates) in the given column.

Parameters
[in]valueThe value to check the validity of
[in]colThe col to check the validity within
[out]valid(Bool) 0: False, 1: True
813  {
814  for (int row = 0; row < VALUES; row++) {
815  if (grid[row][col] == valid[value]) {
816  return 1;
817  }
818  }
819  return 0;
820 }

◆ valid_in_row()

int valid_in_row ( int  value,
int  row 
)

Checks if the value is valid (no duplicates) in the given row.

Parameters
[in]valueThe value to check the validity of
[in]rowThe row to check the validity within
[out]valid(Bool) 0: False, 1: True
798  {
799  for (int col = 0; col < VALUES; col++) {
800  if (grid[row][col] == valid[value]) {
801  return 1;
802  }
803  }
804  return 0;
805 }

◆ write_to_file()

void write_to_file ( FILE *  output)

Writes the contents of the grid to a file in csv format.

Parameters
[in]outputThe file to write the output to
1822  {
1823  char buffer[ROW_BUF_SIZE];
1824  for (int row = 0; row < VALUES; row++) {
1825  int bufPos = 0;
1826  memset(buffer, '\0', sizeof(buffer));
1827  for (int col = 0; col < VALUES; col++) {
1828  int len = strlen(grid[row][col]);
1829  strncpy(&buffer[bufPos], grid[row][col], len);
1830  bufPos += len;
1831  buffer[bufPos] = ',';
1832  bufPos += 1;
1833  }
1834  buffer[bufPos - 1] = '\0';
1835  fprintf(output, "%s\n", buffer);
1836  }
1837 }
make_pair
int * make_pair(int row, int col)
Dynamically allocated and initializes a pair of values.
Definition: sudoku.c:529
find_group
int find_group(int considered[VALUES], int candidCount, int size, int pos, int mode, int verbosity, int isHidden)
Wrapper function for forming naked groups and evaluating them.
Definition: sudoku.c:1531
unique_in_box
int * unique_in_box(int *value)
Gets position which is the only place for a possible value in a box.
Definition: sudoku.c:631
free_globals
void free_globals()
Frees the dynamically allocated global variables.
Definition: sudoku.c:195
print_possible
void print_possible()
Prints all possible values for each cell in the grid (FOR TESTING).
Definition: sudoku.c:469
grid
char *** grid
A string 2-D array storing known or determined values.
Definition: sudoku.c:18
max
int max(int a, int b)
Returns the maximum of two integers.
Definition: sudoku.c:912
process_token
int process_token(int *tokenCount, char *token, int row, int col)
Sanitize, and set the grid to reference a valid token.
Definition: sudoku.c:252
find_single
int * find_single()
Gets the next pair of row and column where there is only one possibility.
Definition: sudoku.c:540
read_csv
int read_csv(const char *fileName, char *missing)
Reads from a csv file and loads into the grid.
Definition: sudoku.c:279
pre_process
void pre_process(int *cellsLeft)
Initializes a bitmap to hold possible values.
Definition: sudoku.c:403
candid_group
int candid_group(int considered[VALUES], int curConsidered, int candidCount, int *candidates, int curCandid, int targetSize, int curSize, int pos, int mode, int verbosity, int isHidden)
Recursive function to create groups of candidate numbers of the given size.
Definition: sudoku.c:1478
remove_value_from_row
int remove_value_from_row(int row, int value, int mask[VALUES], int verbosity)
Removes a value from the possibilities of all others in the same row.
Definition: sudoku.c:707
read_bulk
int read_bulk(const char *fileName, int forceFlag)
Reads, solves and checks all sudokus in a bulk sudoku file.
Definition: sudoku.c:1872
initialize_globals
int initialize_globals(char buffer[ROW_BUF_SIZE], int isBulk)
Allocates and initializes the global variables.
Definition: sudoku.c:154
process_next
int process_next(int *cellsLeft, int verbosity, int forceFlag)
Process the next iteration of the loop.
Definition: sudoku.c:1752
VALUES
int VALUES
The number of values that the sudoku can hold.
Definition: sudoku.c:22
focus_house
int focus_house(int houseBM[SUB_SIZE])
Returns overlapping house where possible locations of a value lie in.
Definition: sudoku.c:925
naked_groups
int naked_groups(int size, int verbosity)
Top-level function to find and remove naked groups.
Definition: sudoku.c:1547
get_boxes_bitmap
void get_boxes_bitmap(int array[SUB_SIZE][SUB_SIZE][VALUES])
Stores all columns as a bitmap of numbers.
Definition: sudoku.c:360
intersection_removal
int intersection_removal(int verbosity)
Find value in only overlapping house and remove from rest of other houses.
Definition: sudoku.c:944
hidden_groups
int hidden_groups(int size, int verbosity)
Top-level function to find and remove hidden groups.
Definition: sudoku.c:1621
read_file
int read_file(char *inCsv, char *missing, int isBulk, int forceFlag)
Adapter function to handle the reading of a file depending on its structure.
Definition: sudoku.c:1985
eval_hidden_group
int eval_hidden_group(int *candidates, int size, int pos, int mode, int verbosity)
Evaluates a hidden group by checking validity and removing redundancy.
Definition: sudoku.c:1368
valid_in_col
int valid_in_col(int value, int col)
Checks if the value is valid (no duplicates) in the given column.
Definition: sudoku.c:813
write_to_file
void write_to_file(FILE *output)
Writes the contents of the grid to a file in csv format.
Definition: sudoku.c:1822
process_arguments
int process_arguments(int argc, char *argv[], int *verbosity, int *bulkFlag, int *timeFlag, int *forceFlag, int *debugFlag, char **inCsv, char **outCsv, char **missing)
Processes the command line arguments provided.
Definition: sudoku.c:43
export_grid
int export_grid(char *outCsv)
Exports the solved grid as a csv file.
Definition: sudoku.c:1844
remove_hidden_group
int remove_hidden_group(int *rows, int *cols, int *shared, int size, int mode, int verbosity)
Removes values according to the contents of a known hidden group.
Definition: sudoku.c:1168
eval_naked_group
int eval_naked_group(int *candidates, int size, int pos, int mode, int verbosity)
Evaluates a naked group by checking validity and removing redundancy.
Definition: sudoku.c:1221
valid_in_box
int valid_in_box(int value, int row, int col)
Checks if the value is valid (no duplicates) in the given box.
Definition: sudoku.c:828
eliminate_redundant
int eliminate_redundant(int verbosity)
Elminates redundant possibilities using the techniques the solver knows.
Definition: sudoku.c:1712
valid
char ** valid
Stores the valid tokens that can be used in the sudoku grid.
Definition: sudoku.c:21
unique_in_col
int * unique_in_col(int *value)
Gets position which is only place for a possible value in a col.
Definition: sudoku.c:602
remove_value_from_box
int remove_value_from_box(int row, int col, int value, int mask[VALUES], int verbosity)
Removes a value from the possibilities of all others in the same box.
Definition: sudoku.c:765
print_grid
void print_grid()
Prints the current state of the grid in a formatted output.
Definition: sudoku.c:500
addToValid
int addToValid(int *tokenCount, char *token)
Add the token to the valid array if not full.
Definition: sudoku.c:227
get_possible_value
int get_possible_value(int *pair)
Assumes there is only one value in the bitmap and retrieves it.
Definition: sudoku.c:556
valid_in_row
int valid_in_row(int value, int row)
Checks if the value is valid (no duplicates) in the given row.
Definition: sudoku.c:798
unique_in_range
int * unique_in_range(int mode, int *value, int verbosity)
Gets position which is only place for a possible value in a house.
Definition: sudoku.c:670
possible
int *** possible
Stores all possible values for each cell in the grid.
Definition: sudoku.c:19
SUB_SIZE
int SUB_SIZE
The root of the values; i.e. the length of the boxes.
Definition: sudoku.c:23
get_columns_bitmap
void get_columns_bitmap(int array[VALUES][VALUES])
Stores all columns as a bitmap of numbers.
Definition: sudoku.c:341
possibleCount
int ** possibleCount
Stores the number of possibilities for each cell.
Definition: sudoku.c:20
brute_force_rec
int brute_force_rec(int curRow, int curCol, int verbosity)
Attempts to solve using a brute force mechanism.
Definition: sudoku.c:846
brute_force
void brute_force(int verbosity)
Wrapper function to handle result of recursive brute force appropriately.
Definition: sudoku.c:895
get_row_bitmap
void get_row_bitmap(int row, int array[VALUES])
Stores the current row as a bitmap of numbers.
Definition: sudoku.c:386
remove_naked_group
int remove_naked_group(int *rows, int *cols, int *shared, int size, int mode, int verbosity)
Removes values according to the contents of a known naked group.
Definition: sudoku.c:1100
unique_in_row
int * unique_in_row(int *value)
Gets position which is only place for a possible value in a row.
Definition: sudoku.c:573
remove_value_from_column
int remove_value_from_column(int col, int value, int mask[VALUES], int verbosity)
Removes a value from the possibilities of all others in the same column.
Definition: sudoku.c:736