diff --git a/Honors/Makefile b/Honors/Makefile new file mode 100644 index 0000000..4507a4e --- /dev/null +++ b/Honors/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -std=c99 -lm + +%.o: %.c + $(CC) -g -c $(CFLAGS) $< + +solver: cells.o trail.o sudoku.o solver.o + $(CC) -g $(CFLAGS) -o $@ $^ -pthread + +clean: + rm -rf *~ *.o cells trail sudoku solver diff --git a/Honors/Report.pdf b/Honors/Report.pdf new file mode 100644 index 0000000..806b568 Binary files /dev/null and b/Honors/Report.pdf differ diff --git a/Honors/cells.c b/Honors/cells.c new file mode 100644 index 0000000..6eba491 --- /dev/null +++ b/Honors/cells.c @@ -0,0 +1,84 @@ +#include +#include +#include "cells.h" +#include "trail.h" + +// Basic Cell Functions + +Cell* makeCell(int s, int id) { + Cell* c = (Cell*)malloc(sizeof(Cell)); + int* guesses = (int*)malloc(sizeof(int) * (s + 1)); + c->id = id; + c->val = 0; + c->ngs = s; + c->gs = guesses; + for (int i = 1; i <= s; i++) + c->gs[i] = 1; + return c; +} + +void freeCell(Cell* c) { + free(c->gs); + free(c); +} + +void setValue(Cell* c, int v, int sz) { + c->val = v; + c->ngs = 0; + for (int i = 1; i <= sz; i++) + c->gs[i] = 0; +} + +int removeGuess(Cell* c, int n) { + if (c->gs[n] == 1) { + c->gs[n] = 0; + c->ngs--; + } + return c->ngs; +} + +void printCell(Cell* c, int size) { + printf("Cell ID: %d with %d Guesses: { \n", c->id, c->ngs); + for (int i = 1; i <= size; i++) { + printf("%d ", c->gs[i]); + } + printf("}\n"); +} + +Cell* copyCell(Cell* orig, int s) { + Cell* new = (Cell*)malloc(sizeof(Cell)); + int* guesses = (int*)malloc(sizeof(int) * (s + 1)); + new->id = orig->id; + new->val = orig->val; + new->ngs = orig->ngs; + new->gs = guesses; + for (int i = 1; i <= s; i++) + new->gs[i] = orig->gs[i]; + return new; +} + +// Basic Group Functions + +Group* makeGroup(int max) { + Group* g = (Group*)malloc(sizeof(Group)); + Cell** cells = (Cell**)malloc(sizeof(Cell*) * max); + g->max = max; + g->ncs = 0; + g->cs = cells; + return g; +} + +int addCell(Group* g, Cell* c) { + if (g->ncs == g->max) { + printf("Error: cannot add more cells to group.\n"); + return 0; + } + g->cs[g->ncs++] = c; + return 1; +} + +void freeGroup(Group* g) { + free(g->cs); + free(g); +} + diff --git a/Honors/cells.h b/Honors/cells.h new file mode 100644 index 0000000..3211e81 --- /dev/null +++ b/Honors/cells.h @@ -0,0 +1,29 @@ +#ifndef CELLS_H +#define CELLS_H + +typedef struct Cell { + int id; + int val; + int ngs; + int* gs; +} Cell; + +typedef struct Group { + int max; + int ncs; + Cell** cs; +} Group; + +// Basic Cell Functions +Cell* makeCell(int s, int id); +void freeCell(Cell* c); +void setValue(Cell* c, int v, int sz); +int removeGuess(Cell* c, int n); +void printCell(Cell* c, int size); +Cell* copyCell(Cell* orig, int s); +// Basic Group Functions +Group* makeGroup(int max); +int addCell(Group* g, Cell* c); +void freeGroup(Group* g); + +#endif diff --git a/Honors/demo1sol.txt b/Honors/demo1sol.txt new file mode 100644 index 0000000..5e6b869 --- /dev/null +++ b/Honors/demo1sol.txt @@ -0,0 +1,8 @@ +i +9 +guessless.txt +r +yes +1 +yes +q diff --git a/Honors/demoLarge.txt b/Honors/demoLarge.txt new file mode 100644 index 0000000..c9c3c1b --- /dev/null +++ b/Honors/demoLarge.txt @@ -0,0 +1,8 @@ +i +9 +thousands.txt +r +yes +4 +yes +q diff --git a/Honors/guessless.txt b/Honors/guessless.txt new file mode 100644 index 0000000..e05ecfa --- /dev/null +++ b/Honors/guessless.txt @@ -0,0 +1,23 @@ +1 2 3 +1 3 9 +1 4 5 +2 4 8 +2 8 7 +3 5 1 +3 7 9 +3 9 4 +4 1 1 +4 4 4 +4 9 3 +6 3 7 +6 7 8 +6 8 6 +7 3 6 +7 4 7 +7 6 8 +7 7 2 +8 2 1 +8 5 9 +8 9 5 +9 6 1 +9 9 8 diff --git a/Honors/solver.c b/Honors/solver.c new file mode 100644 index 0000000..088bac5 --- /dev/null +++ b/Honors/solver.c @@ -0,0 +1,663 @@ +#include +#include +#include +#include +#include +#include "cells.h" +#include "trail.h" +#include "sudoku.h" +#include "solver.h" + +// Sudoku Scanning + +int findSingleton(Cell* c, int sz) { + // Assumes singleton already exists + for (int i = 1; i <= sz; i++) { + if (c->gs[i] == 1) { + return i; + } + } + //printf("FATAL ERROR at: findSingleton() for cell %d", c->id); + return -1; +} + +int findSingletons(Sudoku* s, Trail* t) { + int singletons = 0; + for (int i = 0; i < s->sz * s->sz; i++) { + if (s->cs[i]->val == 0 && s->cs[i]->ngs == 1) { + singletons++; + int digit = findSingleton(s->cs[i], s->sz); + int error; + + if (digit == -1) + return -2; + error = setCellByID(s, digit, i, t); + switch (error) { + case 1: + break; + case -2: + return -1; + default: + //printf("FATAL ERROR at: findSingletons() due to impossible error with id %d\n", error); + return -1; + } + } + } + return singletons; +} +int findHiddenSinglesGroup(Sudoku* s, Group* g, Trail* t) { + // -1 not found, >=0 sole instance, -2 multiple instances + int firstInstances[s->sz + 1]; + for (int k = 1; k <= s->sz; k++) + firstInstances[k] = -1; + for (int i = 0; i < g->ncs; i++) { + Cell* c = g->cs[i]; + for (int j = 1; j <= s->sz; j++) { + if (c->gs[j] == 1) { + if (firstInstances[j] == -1) { + firstInstances[j] = c->id; + } else if (firstInstances[j] >= 0) { + firstInstances[j] = -2; + } + } + } + } + + int noHS = 0; + int error; + + for (int m = 1; m <= s->sz; m++) { + if (firstInstances[m] >= 0) { + noHS++; + error = setCellByID(s, m, firstInstances[m], t); + if (error != 1) { + return -1; + } + } + } + return noHS; +} +int findHiddenSingles(Sudoku* s, Trail* t) { + int noHS = 0; + for (int i = 0; i < s->sz; i++) { + Group* row = getFilteredRow(s, i); + Group* col = getFilteredCol(s, i); + Group* box = getFilteredBox(s, i); + + int rowHS = findHiddenSinglesGroup(s, row, t); + free(row); + int colHS = findHiddenSinglesGroup(s, col, t); + free(col); + int boxHS = findHiddenSinglesGroup(s, box, t); + free(box); + + if (rowHS == -1 || colHS == -1 || boxHS == -1) + return -1; + noHS += rowHS + colHS + boxHS; + } + return noHS; +} + +int findPreemptiveSetAux(Sudoku* s, Group* g, int curr, int noin, int inIDs[], int nogs, int gs[], Trail* t) { + if (curr == g->ncs) { + return 0; + } + + Cell* c = g->cs[curr]; + int newnoin = noin; + int newinIDs[g->ncs]; + for (int x = 0; x < g->ncs; x++) + newinIDs[x] = inIDs[x]; + int newnogs = nogs; + int newgs[s->sz + 1]; + for (int y = 1; y <= s->sz; y++) + newgs[y] = gs[y]; + + newnoin++; + newinIDs[curr] = 1; + for (int i = 1; i <= s->sz; i++) { + if (c->gs[i] == 1 && newgs[i] == 0) { + newgs[i] = 1; + newnogs++; + } + } + if (newnogs < newnoin) { + //printf("Error: number of guesses cannot be smaller than cells\n"); + return -1; + } else if (newnoin == newnogs) { + int removed = 0; + for (int j = 0; j < g->ncs; j++) { + for (int k = 1; k <= s->sz; k++) { + if (newinIDs[j] == 0 && newgs[k] == 1 && g->cs[j]->gs[k] == 1) { + removed++; + int remgs = removeGuessT(g->cs[j], k, t); + if (remgs == 0) { + //printf("Error: no more remaining guesses for cell with id %d\n", g->cs[j]->id); + return -1; + } + } + } + } + return removed; + } else { + int include = findPreemptiveSetAux(s, g, curr + 1, newnoin, newinIDs, newnogs, newgs, t); + int exclude = findPreemptiveSetAux(s, g, curr + 1, noin, inIDs, nogs, gs, t); + + if (include == -1 || exclude == -1) + return -1; + return include + exclude; + } +} + +int findPreemptiveSet(Sudoku* s, Group* g, Trail* t) { + int inIDs[g->ncs]; + for (int i = 0; i < g->ncs; i++) + inIDs[i] = 0; + int gs[s->sz + 1]; + for (int j = 1; j <= s->sz; j++) + gs[j] = 0; + return findPreemptiveSetAux(s, g, 0, 0, inIDs, 0, gs, t); +} + +int findPreemptiveSets(Sudoku* s, Trail* t) { + int removed = 0; + for (int i = 0; i < s->sz; i++) { + Group* row = getFilteredRow(s, i); + Group* col = getFilteredCol(s, i); + Group* box = getFilteredBox(s, i); + + int rowR = findPreemptiveSet(s, row, t); + free(row); + int colR = findPreemptiveSet(s, col, t); + free(col); + int boxR = findPreemptiveSet(s, box, t); + free(box); + if (rowR == -1 || colR == -1 || boxR == -1) + return -1; + removed += rowR + colR + boxR; + } + return removed; +} + +int scanSudoku(Sudoku* s, Trail* t) { + //printf("Starting new scan!\n"); + int singletons = 0, HS = 0, removed = 0; + do { + //printf("SINGLETONS\n"); + singletons = findSingletons(s, t); + //printf("HIDDEN SINGLES\n"); + HS = findHiddenSingles(s, t); + //printf("PREEMPTIVE SETS\n"); + removed = findPreemptiveSets(s, t); + //printSudoku(s); + //printf("singletons: %d HS: %d removed: %d\n", singletons, HS, removed); + } while ((singletons > 0 || HS > 0 || removed > 0) && (singletons != -1 && HS != -1 && removed != -1)); + + if (singletons < 0 || HS < 0 || removed < 0) { + return -1; + } else + return 0; +} + +// Sudoku Guessing +void makeGuess(Marks* m, Trail* t, Sudoku* s, int ID, int guess) { + //printf("Making guess %d for cell %d\n", guess, ID); + //printCell(s->cs[ID], s->sz); + addMark(m, t->sz); + setCellByID(s, guess, ID, t); +} + +int findGuessCell(Sudoku* s) { + for(int i = 0; i < s->sz * s->sz; i++) { + if (s->cs[i]->ngs > 0) { + return i; + } + } + return -1; +} + +int findGuess(Cell* c, int sz) { + for (int i = 1; i <= sz; i++) { + if (c->gs[i] == 1) { + return i; + } + } + return -1; +} + +void restore(Marks* m, Trail* t, Sudoku* s) { + int mark = extractMark(m); + while (t->sz != mark) { + Data change = extractChange(t); + if (change.type == 1) { + s->cs[change.cellID]->val = 0; + s->rem++; + } else { + if (s->cs[change.cellID]->gs[change.value] == 0) { + s->cs[change.cellID]->gs[change.value] = 1; + s->cs[change.cellID]->ngs++; + } + } + } +} + +int chainRestore(Marks* m, Trail* t, Sudoku* s, int undos) { + //printf("Starting chain restore.\n"); + //printf("ORIGINAL:\n"); + //printSudoku(s); + restore(m, t, s); + //printf("RESTORED:\n"); + //printSudoku(s); + + int guessID = findGuessCell(s); + if (guessID == -1) { + return -1; + } + int guess = findGuess(s->cs[guessID], s->sz); + //printCell(s->cs[guessID], s->sz); + if (s->cs[guessID]->ngs == 1) { + if (m->sz == 0) { + return -1; + } else { + return chainRestore(m, t, s, undos + 1); + } + } else { + //printf("removing guess %d\n", guess); + if (m->sz == 0) { + removeGuessT(s->cs[guessID], guess, NULL); + return undos; + } + removeGuessT(s->cs[guessID], guess, t); + return undos; + } +} + +// Sudoku Solving + +void solveSudoku(Sudoku* s) { + Trail* t = makeTrail(); + Marks* m = createMarks(); + + int scanEr, restEr, solutions = 0; + scanEr = scanSudoku(s, NULL); + //printSudoku(s); + if (scanEr == -1) { + //printf("Sudoku cannot be solved.\n"); + freeTrail(t); + freeMarks(m); + return; + } + + if (isSolved(s)) { + solutions++; + //printf("Solution:\n"); + //printSudoku(s); + printf("There were %d solutions found.\n", solutions); + freeTrail(t); + freeMarks(m); + return; + } + + // At this point, guessing is required. + while (1) { + int guessID = findGuessCell(s); + int guess = findGuess(s->cs[guessID], s->sz); + makeGuess(m, t, s, guessID, guess); + + scanEr = scanSudoku(s, t); + if (scanEr == -1) { + //printf("Scanning resulted in an error:\n"); + //printSudoku(s); + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } + } else if (isSolved(s)) { + solutions++; + //printf("Solution:\n"); + //printSudoku(s); + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } + } + } + printf("There were %d solutions found.\n", solutions); +} + +// Thread Object Manipulation + +Solutions* makeSStack() { + Solutions* s = (Solutions*)malloc(sizeof(Solutions)); + s->numSols = 0; + s->maxSols = 1; + + Sudoku** solutions = (Sudoku**)malloc(sizeof(Sudoku*) * s->maxSols); + s->solutions = solutions; + return s; +} + +void reallocSStack(Solutions* s) { + s->maxSols *= 2; + s->solutions = (Sudoku**)realloc(s->solutions, sizeof(Sudoku*) * s->maxSols); +} + +void freeSStack(Solutions* s) { + for (int i = 0; i < s->numSols; i++) { + freeSudoku(s->solutions[i]); + } + free(s->solutions); + free(s); +} + +// Threading + +void* trailBlaze(void* args) { + // Extract relevant information from info + TBInfo* info = args; + Sudoku* s = info->s; + Trail* t = info->t; + Marks* m = info->m; + SharedInfo* shr = info->SI; + + //printf("Trailblazer has extracted info.\n"); + + // Create necessary vars + int guesses = 0; + int restEr, scanEr; + + // Create jobs + while (1) { + // Undo if max guesses reached + if (guesses == info->maxDepth) { + // Add current state to jobs + Sudoku* copy = copySudoku(s); + Job j = {copy, guesses}; + // TODO: Concurrency stuff; add job to joblist + // - Obtain lock + // - insert job + // - increase numJobs + // - wake another guy up + // - Unlock + pthread_mutex_lock(&shr->mtx); + shr->jobs[shr->numJobs++] = j; + pthread_cond_signal(&shr->done); + pthread_mutex_unlock(&shr->mtx); + + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + // No more jobs. + break; + } else { + guesses -= restEr; + } + } + int guessID = findGuessCell(s); + int guess = findGuess(s->cs[guessID], s->sz); + makeGuess(m, t, s, guessID, guess); + guesses++; + + scanEr = scanSudoku(s, t); + if (scanEr == -1) { + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } else { + guesses -= restEr; + } + } else if (isSolved(s)) { + // TODO: Concurrency add solution to shared info + // - Obtain lock + // - Increase num solutions + // - Copy solution to Solution stack + // - Unlock + //printf("Found a solution!\n"); + pthread_mutex_lock(&shr->mtx); + if (shr->solutions->numSols == shr->solutions->maxSols) { + reallocSStack(shr->solutions); + } + shr->solutions->solutions[shr->solutions->numSols++] = copySudoku(s); + pthread_mutex_unlock(&shr->mtx); + //printf("Successfully added solution!\n"); + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } else { + guesses -= restEr; + } + } + } + + // Done with jobs + // TODO: Concurrency end trailblazing phase + // - Obtain lock + // - Turn off isBranching + // - Unlock + pthread_mutex_lock(&shr->mtx); + shr->stillBranching = 0; + pthread_mutex_unlock(&shr->mtx); + freeMarks(m); + freeTrail(t); +} + +void* solveThread(void* args) { + // Unpack info + ThreadInfo* info = args; + Trail* t = info->t; + Marks* m = info->m; + SharedInfo* shr = info->SI; + Job* job = info->job; + Sudoku* s; + int sol = 0, jobs = 0; + while (1) { + // Get Lock + // Step 1: Look for job + // - Increase waitThreads + // - While isBranching && jobs == 0 + // - Wait + // - If waitThreads == numThreads && jobs == 0 + // - Unlock + // - Break + // - Else + // - Grab job + // - Decrease jobs and waitThreads by 1 + pthread_mutex_lock(&shr->mtx); + shr->waitThreads++; + while (shr->stillBranching && shr->numJobs == 0) { + //printf("Still Branching: %d\n", shr->stillBranching); + pthread_cond_wait(&shr->done, &shr->mtx); + } + //printf("Testing...\n"); + if (shr->numJobs == 0) { + if (s != NULL) + freeSudoku(s); + freeTrail(t); + freeMarks(m); + shr->waitThreads--; + pthread_mutex_unlock(&shr->mtx); + break; + } else { + //printf("Grabbed job.\n"); + } + job = &shr->jobs[shr->numJobs-- - 1]; + shr->waitThreads--; + pthread_mutex_unlock(&shr->mtx); + // Step 2: Extract from job + if (s != NULL) + freeSudoku(s); + s = job->s; + freeTrail(t); + freeMarks(m); + t = makeTrail(); + m = createMarks(); + // Step 3: Solve job + while (1) { + int scanEr, restEr; + int guessID = findGuessCell(s); + int guess = findGuess(s->cs[guessID], s->sz); + makeGuess(m, t, s, guessID, guess); + job->ngs++; + + scanEr = scanSudoku(s, t); + if (scanEr == -1) { + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } + } else if (isSolved(s)) { + // TODO: Concurrency add solution to shared info + // - Obtain lock + // - Increase num solutions + // - Copy solution to Solution stack + // - Unlock + pthread_mutex_lock(&shr->mtx); + if (shr->solutions->numSols == shr->solutions->maxSols) { + reallocSStack(shr->solutions); + } + shr->solutions->solutions[shr->solutions->numSols] = copySudoku(s); + + shr->solutions->numSols++; + pthread_mutex_unlock(&shr->mtx); + + //printf("Solutions: %d\n", ++sol); + restEr = chainRestore(m, t, s, 1); + if (restEr == -1) { + break; + } + } + } + //printf("Completed job %d!\n", ++jobs); + } +} + +void* solveSudokuThreads(Sudoku* s, int nt) { + Sudoku* copy = copySudoku(s); + SharedInfo shr; + shr.stillBranching = 1; + shr.numJobs = 0; + shr.maxJobs = 100; + Job* jobs = (Job*)malloc(sizeof(Job) * shr.maxJobs); + shr.jobs = jobs; + shr.waitThreads = 0; + shr.numThreads = nt; + shr.solutions = makeSStack(); + pthread_mutex_init(&shr.mtx, NULL); + pthread_cond_init(&shr.done, NULL); + + TBInfo tb; + tb.maxDepth = 1; + tb.s = copy; + tb.t = makeTrail(); + tb.m = createMarks(); + tb.SI = &shr; + + pthread_create(&tb.name, NULL, trailBlaze, &tb); + + ThreadInfo ti[nt]; + for (int i = 0; i < nt; i++) { + ti[i].job = NULL; + ti[i].t = makeTrail(); + ti[i].m = createMarks(); + ti[i].SI = &shr; + } + for (int i = 0; i < nt; i++) { + pthread_create(&ti[i].name, NULL, solveThread, &ti[i]); + } + pthread_join(tb.name, NULL); + for (int i = 0; i < nt; i++) { + pthread_join(ti[i].name, NULL); + } + pthread_mutex_destroy(&shr.mtx); + pthread_cond_destroy(&shr.done); + printf("Success! There are %d solutions.\n", shr.solutions->numSols); + printf("View solutions? (yes/no)\n"); + char buffer[128]; + char ch; + int i = 0; + while (i < sizeof(buffer) && (ch = getchar()) != '\n' && ch != EOF) + buffer[i++] = ch; + buffer[i] = 0; + if (strcmp("yes", buffer) == 0) { + for (int i = 0; i < shr.solutions->numSols; i++) { + printSudoku(shr.solutions->solutions[i]); + } + } + freeSStack(shr.solutions); + free(jobs); + freeSudoku(copy); +} + +// Main (for testing purposes only) + +void printCommands() { + printf("help - print a list of commands\n"); + printf("quit - quit the program\n"); + printf("import - import a sudoku"); +} + +int main(int argc, char* argv[]) { + char buffer[128]; + int running = 1; + Sudoku* s = NULL; + while (running) { + // Receive command + printf("> "); fflush(stdout); + int i = 0; + char ch; + while (i < sizeof(buffer) && (ch = getchar()) != '\n' && ch != EOF) + buffer[i++] = ch; + buffer[i] = 0; + if (strcmp("q", buffer) == 0 || strcmp("quit", buffer) == 0) { + if (s != NULL) + freeSudoku(s); + running = 0; + } else if (strcmp("h", buffer) == 0 || strcmp("help", buffer) == 0) { + printCommands(); + } else if (strcmp("i", buffer) == 0 || strcmp("import", buffer) == 0) { + int sz; + printf("What size sudoku are you importing?\n"); + int er = scanf("%d", &sz); + while(ch = getchar() != '\n'){} + if (er < 1 || sz < 4 || sz > 81 || sqrt(sz) * sqrt(sz) != sz) { + printf("Invalid size. Aborting import.\n"); + continue; + } + printf("What file would you like to import?\n"); + i = 0; + while (i < sizeof(buffer) && (ch = getchar()) != '\n' && ch != EOF) + buffer[i++] = ch; + buffer[i] = 0; + s = importSudoku(buffer, sz); + if (s == NULL) { + printf("File name invalid. Aborting import..\n"); + } + } else if (strcmp("r", buffer) == 0 || strcmp("run", buffer) == 0) { + if (s == NULL) { + printf("No sudoku available. Please import/make a sudoku first.\n"); + continue; + } + printf("Use threads? (yes/no)\n"); + i = 0; + while (i < sizeof(buffer) && (ch = getchar()) != '\n' && ch != EOF) + buffer[i++] = ch; + buffer[i] = 0; + if (strcmp("yes", buffer) == 0) { + printf("How many?\n"); + int nt; + int er = scanf("%d", &nt); + while(ch = getchar() != '\n'){} + if (er < 1 || nt < 1) { + printf("Invalid size. Aborting import.\n"); + continue; + } + solveSudokuThreads(s, 4); + } else if (strcmp("no", buffer) == 0) { + solveSudoku(s); + } else { + printf("Not a yes/no. Aborting.\n"); + } + } else { + printf("Invalid command. Please try again.\n"); + } + } +} diff --git a/Honors/solver.h b/Honors/solver.h new file mode 100644 index 0000000..69373b4 --- /dev/null +++ b/Honors/solver.h @@ -0,0 +1,65 @@ +#ifndef SOLVER_H +#define SOLVER_H + +typedef struct Solutions { + int numSols; + int maxSols; + Sudoku** solutions; +} Solutions; + +typedef struct Job { + Sudoku* s; + int ngs; +} Job; + +typedef struct SharedInfo { + int stillBranching; + int numJobs; + int maxJobs; + Job* jobs; + int waitThreads; + int numThreads; + Solutions* solutions; + pthread_mutex_t mtx; + pthread_cond_t done; +} SharedInfo; + +typedef struct ThreadInfo { + Job* job; + Trail* t; + Marks* m; + SharedInfo* SI; + pthread_t name; +} ThreadInfo; + +typedef struct TBInfo { + int maxDepth; + Sudoku* s; + Trail* t; + Marks* m; + SharedInfo* SI; + pthread_t name; +} TBInfo; + +// Sudoku Scanning +int findSingleton(Cell* c, int sz); +int findSingletons(Sudoku* s, Trail* t); +int findHiddenSinglesGroup(Sudoku* s, Group* g, Trail* t); +int findHiddenSingles(Sudoku* s, Trail* t); +int findPreemptiveSetAux(Sudoku* s, Group* g, int curr, int noin, int inIDs[], int nogs, int gs[], Trail* t); +int findPreemptiveSet(Sudoku* s, Group* g, Trail* t); +int findPreemptiveSets(Sudoku* s, Trail* t); +int scanSudoku(Sudoku* s, Trail* t); + +// Sudoku Guessing +void makeGuess(Marks* m, Trail* t, Sudoku* s, int ID, int guess); +int findGuessCell(Sudoku* s); +int findGuess(Cell* c, int sz); +void restore(Marks* m, Trail* t, Sudoku* s); +int chainRestore(Marks* m, Trail* t, Sudoku* s, int undos); + +//Sudoku Solving +void solveSudoku(Sudoku* s); + +#endif + diff --git a/Honors/sudoku.c b/Honors/sudoku.c new file mode 100644 index 0000000..6e4bcc7 --- /dev/null +++ b/Honors/sudoku.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include "cells.h" +#include "trail.h" +#include "sudoku.h" + +// Basic Sudoku Functions +Sudoku* makeSudoku(int size) { + Sudoku* s = (Sudoku*)malloc(sizeof(Sudoku)); + Cell** cells = (Cell**)malloc(sizeof(Cell*) * size * size); + s->sz = size; + s->rem = size * size; + s->cs = cells; + + for (int i = 0; i < size * size; i++) + s->cs[i] = makeCell(size, i); + return s; +} + +void freeSudoku(Sudoku* s) { + for (int i = 0; i < s->sz * s->sz; i++) + freeCell(s->cs[i]); + free(s->cs); + free(s); +} + +int removeGuesses(int guess, Group* g, int ignore, Trail* t) { + int violations = 0; + for (int i = 0; i < g->ncs; i++) { + if (g->cs[i]->id != ignore && removeGuessT(g->cs[i], guess, t) == 0) { + violations++; + } + } + return violations; +} + +int setCell(Sudoku* s, int v, int r, int c, Trail* t) { + Cell* cell = s->cs[getID(r, c, s->sz)]; + if (cell->val > 0) + return 0; + if (cell->gs[v] == 0) + return -1; + Group* row = getFilteredRow(s, r); + Group* col = getFilteredCol(s, c); + Group* box = getFilteredBox(s, getBoxByID(cell->id, s->sz)); + int rowV = removeGuesses(v, row, cell->id, t); + freeGroup(row); + int colV = removeGuesses(v, col, cell->id, t); + freeGroup(col); + int boxV = removeGuesses(v, box, cell->id, t); + freeGroup(box); + setValueT(cell, v, s->sz, t); + s->rem--; + if (rowV == 0 && colV == 0 && boxV == 0) + return 1; + else + return -2; +} + +int setCellByID(Sudoku* s, int v, int id, Trail* t) { + return setCell(s, v, getRowByID(id, s->sz), getColByID(id, s->sz), t); +} + +int isSolved(Sudoku* s) { + return s->rem == 0; +} + +Sudoku* copySudoku(Sudoku* orig) { + Sudoku* new = (Sudoku*)malloc(sizeof(Sudoku)); + Cell** cells = (Cell**)malloc(sizeof(Cell*) * orig->sz * orig->sz); + new->sz = orig->sz; + new->rem = orig->rem; + new->cs = cells; + + for (int i = 0; i < new->sz * new->sz; i++) + new->cs[i] = copyCell(orig->cs[i], new->sz); + return new; +} + +// Sudoku sectioning + +Group* getRow(Sudoku* s, int r) { + Group* row = makeGroup(s->sz); + for (int i = 0; i < s->sz; i++) + addCell(row, s->cs[getID(r, i, s->sz)]); + return row; +} + +Group* getFilteredRow(Sudoku* s, int r) { + Group* row = makeGroup(s->sz); + for (int i = 0; i < s->sz; i++) { + Cell* cell = s->cs[getID(r, i, s->sz)]; + if (cell->val == 0) { + addCell(row, cell); + } + } + return row; +} + +Group* getCol(Sudoku* s, int c) { + Group* col = makeGroup(s->sz); + for (int i = 0; i < s->sz; i++) + addCell(col, s->cs[getID(i, c, s->sz)]); + return col; +} + +Group* getFilteredCol(Sudoku* s, int c) { + Group* col = makeGroup(s->sz); + for (int i = 0; i < s->sz; i++) { + Cell* cell = s->cs[getID(i, c, s->sz)]; + if (cell->val == 0) { + addCell(col, cell); + } + } + return col; + +} + +Group* getBox(Sudoku* s, int bxID) { + int root = (int)sqrt(s->sz); + Group* box = makeGroup(s->sz); + int bxRow = (bxID - (bxID % root))/root; + int rowSt = bxRow * root; + int bxCol = bxID % root; + int colSt = bxCol * root; + for (int r = rowSt; r < rowSt + root; r++) { + for (int c = colSt; c < colSt + root; c++) { + addCell(box, s->cs[getID(r, c, s->sz)]); + } + } + return box; +} + +Group* getFilteredBox(Sudoku* s, int bxID) { + int root = (int)sqrt(s->sz); + Group* box = makeGroup(s->sz); + int bxRow = (bxID - (bxID % root))/root; + int rowSt = bxRow * root; + int bxCol = bxID % root; + int colSt = bxCol * root; + for (int r = rowSt; r < rowSt + root; r++) { + for (int c = colSt; c < colSt + root; c++) { + Cell* cell = s->cs[getID(r, c, s->sz)]; + if (cell->val == 0) { + addCell(box, cell); + } + } + } + return box; +} + +// Sudoku helper functions + +int getID(int r, int c, int sz) { + return r * sz + c; +} +int getRowByID(int id, int sz) { + return id / sz; +} +int getColByID(int id, int sz) { + return id % sz; +} +int getBoxByID(int id, int sz) { + int root = (int)sqrt(sz); + int bxRow = id / sz / root; + int bxCol = id % sz / root; + return bxRow * root + bxCol; +} + +// Sudoku printing + +void printSudoku(Sudoku* s) { + int root = (int)sqrt(s->sz); + + int row = 0; + for (int i = 0; i < root; i++) { + printDivider(s->sz); + for (int j = 0; j < root; j++) { + printRow(s, row); + row++; + } + } + printDivider(s->sz); +} + + +void printRow(Sudoku* s, int r) { + Group* row = getRow(s, r); + int root = (int)sqrt(s->sz); + + printf("|"); + int col = 0; + for (int i = 0; i < root; i++) { + for (int j = 0; j < root; j++) { + printf("|"); + if (row->cs[col]->ngs != 0) + printf(" "); + else if (row->cs[col]->val == 0) + printf(" ?"); + else + printf("%2d", row->cs[col]->val); + col++; + } + printf("|"); + } + printf("|\n"); + freeGroup(row); +} + + +void printDivider(int size) { + int root = (int)sqrt(size); + for (int i = 0; i < root; i++) { + printf("[]"); + for (int j = 0; j < 3 * root - 1; j++) + printf("="); + } + printf("[]\n"); +} + +// Sudoku importing +Sudoku* importSudoku(char* path, int sz) { + // Setup Path + FILE* file = fopen(path, "r"); + if (file == NULL) { + return NULL; + } + Sudoku* s = makeSudoku(sz); + char* line = (char*)malloc(sizeof(char) * 10); + int max = 10; + while ((line = fgets(line, max, file)) != NULL) { + int row, col, val; + if (sscanf(line, "%d %d %d\n", &row, &col, &val) == 3) { + if (row < 1 || row > 9 || col < 1 || col > 9 || val < 1 || val > 9) { + printf("INVALID: Unacceptable values for -> %s", line); + } else { + int error; + error = setCell(s, val, row - 1, col - 1, NULL); + switch (error) { + case -2 : + printf("WARNING: Setting (%d, %d) to %d leads to an impossible sudoku.\n", row, col, val); + break; + case -1 : + printf("INVALID: Setting (%d, %d) to %d is impossible due to an immediate violation of sudoku rules.\n", row, col, val); + break; + case 0: + printf("WARNING: (%d, %d) not set to %d due to there already being a value there.\n", row, col, val); + default: + printf("SUCCESS: (%d, %d) set to %d\n", row, col, val); + } + } + } else + printf("INVALID: Unacceptable line -> %s", line); + } + free(line); + return s; +} +Sudoku* createSudoku(int sz); + diff --git a/Honors/sudoku.h b/Honors/sudoku.h new file mode 100644 index 0000000..353b246 --- /dev/null +++ b/Honors/sudoku.h @@ -0,0 +1,46 @@ +#ifndef SUDOKU_H +#define SUDOKU_H + +#define IMPORT 0 +#define CREATE 1 + +typedef struct Sudoku { + int sz; + int rem; + Cell** cs; +} Sudoku; + +// Basic Sudoku Functions +Sudoku* makeSudoku(int size); +void freeSudoku(Sudoku* s); +int removeGuesses(int guess, Group* g, int ignore, Trail* t); +int setCell(Sudoku* s, int v, int r, int c, Trail* t); +int setCellByID(Sudoku* s, int v, int id, Trail* t); +int isSolved(Sudoku* s); +Sudoku* copySudoku(Sudoku* orig); + +// Sudoku Sectioning + +Group* getRow(Sudoku* s, int r); +Group* getFilteredRow(Sudoku* s, int r); +Group* getCol(Sudoku* s, int c); +Group* getFilteredCol(Sudoku* s, int c); +Group* getBox(Sudoku* s, int bxID); +Group* getFilteredBox(Sudoku* s, int bxID); + +// Sudoku Helper Functions +int getID(int r, int c, int sz); +int getRowByID(int id, int sz); +int getColByID(int id, int sz); +int getBoxByID(int id, int sz); + +// Sudoku Printing +void printSudoku(Sudoku* s); +void printRow(Sudoku* s, int r); +void printDivider(int size); + +// Sudoku Importing +Sudoku* importSudoku(char* path, int sz); +Sudoku* createSudoku(int sz); + +#endif diff --git a/Honors/thousands.txt b/Honors/thousands.txt new file mode 100644 index 0000000..8ddcd5a --- /dev/null +++ b/Honors/thousands.txt @@ -0,0 +1,22 @@ +2658 +1 4 1 +1 6 2 +1 7 9 +2 7 3 +2 9 1 +3 6 8 +3 9 6 +4 5 3 +5 3 2 +6 3 9 +6 5 1 +6 6 6 +7 3 8 +7 5 6 +7 9 7 +8 3 4 +8 7 1 +8 8 9 +9 6 4 +9 8 2 + diff --git a/Honors/trail.c b/Honors/trail.c new file mode 100644 index 0000000..9aa91b7 --- /dev/null +++ b/Honors/trail.c @@ -0,0 +1,108 @@ +#include +#include +#include "cells.h" +#include "trail.h" + +// Data creation +Data makeData(int type, int ID, int v) { + Data d = {type, ID, v}; + return d; +} + +void printData(Data d) { + printf("{%d %d %d}\n", d.type, d.cellID, d.value); +} + +// Trail functions + +Trail* makeTrail() { + Trail* t = (Trail*)malloc(sizeof(Trail)); + t->sz = 0; + t->max = MAX_CHANGES; + t->changes = (Data*)malloc(sizeof(Data) * t->max); + return t; +} + +Trail* reallocTrail(Trail* t) { + t->max *= 2; + t->changes = (Data*)realloc(t->changes, t->max); + return t; +} + +void freeTrail(Trail* t) { + free(t->changes); + free(t); +} + +void makeChange(Trail* t, int type, int ID, int v) { + if (t->sz == t->max) { + reallocTrail(t); + } + Data data = makeData(type, ID, v); + //printf("Saving data: "); + //printData(data); + t->changes[t->sz++] = data; +} + +Data extractChange(Trail* t) { + Data data = t->changes[t->sz - 1]; + //printf("Restoring data: "); + //printData(data); + t->sz--; + return data; +} + +// Cell functions with Trail +void setValueT(Cell* c, int v, int sz, Trail* t) { + if (t != NULL) { + for (int i = 1; i <= sz; i++) { + if (c->gs[i] == 1) { + makeChange(t, 0, c->id, i); + } + } + makeChange(t, 1, c->id, v); + } + setValue(c, v, sz); +} + +int removeGuessT(Cell* c, int n, Trail* t) { + if (t != NULL && c->gs[n] == 1) { + makeChange(t, 0, c->id, n); + } + + return removeGuess(c, n); +} + +// Mark Functions + +Marks* createMarks() { + Marks* m = (Marks*)malloc(sizeof(Marks)); + m->sz = 0; + m->max = 1; + m->marks = (int*)malloc(sizeof(int) * m->max); + return m; +} + +Marks* reallocMarks(Marks* m) { + m->max *= 2; + m->marks = (int*)realloc(m->marks, sizeof(int) * m->max); + return m; +} + +void freeMarks(Marks* m) { + free(m->marks); + free(m); +} + +void addMark(Marks* m, int index) { + if (m->sz == m->max) { + reallocMarks(m); + } + m->marks[m->sz++] = index; +} + +int extractMark(Marks* m) { + int mark = m->marks[m->sz - 1]; + m->sz--; + return mark; +} diff --git a/Honors/trail.h b/Honors/trail.h new file mode 100644 index 0000000..1de6321 --- /dev/null +++ b/Honors/trail.h @@ -0,0 +1,44 @@ +#ifndef TRAIL_H +#define TRAIL_H + +#define GUESS 0 +#define VALUE 1 + +#define MAX_CHANGES 1000 + +typedef struct Data { + int type; + int cellID; + int value; +} Data; + +typedef struct Trail { + int sz; + int max; + Data* changes; +} Trail; + +typedef struct Marks { + int sz; + int max; + int* marks; +} Marks; + +Data makeData(int type, int ID, int v); + +Trail* makeTrail(); +Trail* reallocTrail(Trail* t); +void freeTrail(Trail* t); +void makeChange(Trail* t, int type, int ID, int v); +Data extractChange(Trail* t); + +void setValueT(Cell* c, int v, int sz, Trail* t); +int removeGuessT(Cell* c, int n, Trail* t); + +Marks* createMarks(); +Marks* reallocMarks(Marks* m); +void freeMarks(Marks* m); +void addMark(Marks* m, int index); +int extractMark(Marks* m); + +#endif