From e5168ad315fa00ecf8fd700ca6b7b326a911a4d8 Mon Sep 17 00:00:00 2001 From: NicoPang Date: Mon, 13 Dec 2021 18:28:41 -0500 Subject: [PATCH] Uploaded files Yes! --- Honors/Makefile | 11 + Honors/Report.pdf | Bin 0 -> 64176 bytes Honors/cells.c | 84 ++++++ Honors/cells.h | 29 ++ Honors/demo1sol.txt | 8 + Honors/demoLarge.txt | 8 + Honors/guessless.txt | 23 ++ Honors/solver.c | 663 +++++++++++++++++++++++++++++++++++++++++++ Honors/solver.h | 65 +++++ Honors/sudoku.c | 260 +++++++++++++++++ Honors/sudoku.h | 46 +++ Honors/thousands.txt | 22 ++ Honors/trail.c | 108 +++++++ Honors/trail.h | 44 +++ 14 files changed, 1371 insertions(+) create mode 100644 Honors/Makefile create mode 100644 Honors/Report.pdf create mode 100644 Honors/cells.c create mode 100644 Honors/cells.h create mode 100644 Honors/demo1sol.txt create mode 100644 Honors/demoLarge.txt create mode 100644 Honors/guessless.txt create mode 100644 Honors/solver.c create mode 100644 Honors/solver.h create mode 100644 Honors/sudoku.c create mode 100644 Honors/sudoku.h create mode 100644 Honors/thousands.txt create mode 100644 Honors/trail.c create mode 100644 Honors/trail.h 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 0000000000000000000000000000000000000000..806b5682a2d734d638b6954eeae3df3d91feaac7 GIT binary patch literal 64176 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 literal 0 HcmV?d00001 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