The source code should be self-explanatory. I found it fun to write because it is tricky to build macros that map a puzzle to appropriate code. (Note that I seemed to need the ``comma'' operator.)
This is just a miniature version of my logic puzzle software. I now produce such puzzles as a vocation, and don't feel like showing all my tools here (though I might share in response to a serious inquiry).
The source contains the general solver, and details of a specific puzzle (though not one good enough to sell to a magazine). Normally the latter would be in a separate included file; indeed much of the macro setup is to facilitate that. This is very similar to code submitted a decade ago to the Rec.Puzzles Archive, though this version fixes a bug seen only with unusually difficult problems.
#include#define EITHER if (S[1] = S[0], ! setjmp((S++)->jb)) { #define OR } else EITHER #define REJECT longjmp((--S)->jb, 1) #define END_EITHER } else REJECT; #define EASY_END } /* values in tmat: */ #define T_UNK 0 #define T_YES 1 #define T_NO 2 #define Val(t1,t2) (S->tmat[t1][t2]) #define CLASS(x) \ (((x) / NUM_ITEM) * NUM_ITEM) #define EVERY_TOKEN(x) \ (x = 0; x < TOT_TOKEN; x++) #define EVERY_ITEM(x, class) \ (x = CLASS(class); x < CLASS(class) + NUM_ITEM; x++) #define No2(b, cl) { \ no(b, cl+0); no(b, cl+1); } #define No3(b, cl) { \ No2(b, cl); no(b, cl+2); } #define No4(b, cl) { \ No3(b, cl); no(b, cl+3); } #define No5(b, cl) { \ No4(b, cl); no(b, cl+4); } #define lessthan3(a, b, cl) if (a != -1) { \ EITHER yes(a, cl+0); no(b, cl); \ OR yes(a, cl+1); No2(b, cl); \ END_EITHER } #define lessthan4(a, b, cl) { \ EITHER lessthan3(a, b, cl); \ OR yes(a, cl+2); No3(b, cl); \ END_EITHER } #define lessthan5(a, b, cl) { \ EITHER lessthan4(a, b, cl); \ OR yes(a, cl+3); No4(b, cl); \ END_EITHER } #define lessthan6(a, b, cl) { \ EITHER lessthan5(a, b, cl); \ OR yes(a, cl+4); No5(b, cl); \ END_EITHER } #define MAXTTOT 30 struct state { char tmat[MAXTTOT][MAXTTOT]; long flug[2]; jmp_buf jb; } States[4000], *S = States; termin() { printf("All solutions found\n"); exit(0); } #define PROB_BEGIN \ main() { register token; \ int xx_1; \ int aa1, aa2, aa3, \ aa4, aa5, aa6; \ for EVERY_TOKEN(token) yes(token, token); EITHER #define PROB_END \ solveit(); OR termin(); END_EITHER } /* Here is the problem-specific data */ #define NUM_ITEM 6 #define NUM_CLASS 4 #define TOT_TOKEN (NUM_ITEM * NUM_CLASS) #define CO_1 0 #define CO_5 1 #define CO_10 2 #define CO_25 3 #define CO_50 4 #define CO_100 5 #define ANNIE (NUM_ITEM + 0) #define BETTY (NUM_ITEM + 1) #define CHERYL (NUM_ITEM + 2) #define DELLA (NUM_ITEM + 3) #define ELAINE (NUM_ITEM + 4) #define FANNY (NUM_ITEM + 5) #define REYNOLD (NUM_ITEM*2 + 0) #define TUKEY (NUM_ITEM*2 + 1) #define VITTEL (NUM_ITEM*2 + 2) #define MORRIS (NUM_ITEM*2 + 3) #define UPDIKE (NUM_ITEM*2 + 4) #define STOUT (NUM_ITEM*2 + 5) #define MONDAY (NUM_ITEM*3 + 0) #define TUESDA (NUM_ITEM*3 + 1) #define WEDNES (NUM_ITEM*3 + 2) #define THURSD (NUM_ITEM*3 + 3) #define FRIDAY (NUM_ITEM*3 + 4) #define SATURD (NUM_ITEM*3 + 5) char *Names[] = { "CO_1", "CO_5", "CO_10", "CO_25", "CO_50", "CO_100", "ANNIE", "BETTY", "CHERYL", "DELLA", "ELAINE", "FANNY", "REYNOLD", "TUKEY", "VITTEL", "WALKER", "UPDIKE", "STOUT", "MONDAY", "TUESDA", "WEDNES", "THURSD", "FRIDAY", "SATURD", }; PROB_BEGIN /* Clue 1: Annie's coin was twice the value of Tukey's */ EITHER yes(ANNIE, CO_10); yes(TUKEY, CO_5); OR yes(ANNIE, CO_50); yes(TUKEY, CO_25); OR yes(ANNIE, CO_100); yes(TUKEY, CO_50); END_EITHER /* Clue 2: Wednes coin was five times the value of FANNY's coin */ EITHER yes(WEDNES, CO_5); yes(FANNY, CO_1); OR yes(WEDNES, CO_25); yes(FANNY, CO_5); OR yes(WEDNES, CO_50); yes(FANNY, CO_10); END_EITHER /* Clue 3: Updike threw before Elaine; Dime was thrown before Vittel' throw */ lessthan6(UPDIKE, ELAINE, MONDAY); lessthan6(CO_10, VITTEL, MONDAY); /* Clue 4: BETTY's coin is less than REYNOLD's */ lessthan6(BETTY, REYNOLD, CO_1); /* Clue 5: Saturd coin was five times the value of STOUT's coin */ EITHER yes(SATURD, CO_5); yes(STOUT, CO_1); OR yes(SATURD, CO_25); yes(STOUT, CO_5); OR yes(SATURD, CO_50); yes(STOUT, CO_10); END_EITHER /* Clue 6: The nickel came some day after Elaine */ lessthan6(ELAINE, CO_5, MONDAY); /* Clue 7: 50-cent piece came some day after STOUT */ lessthan6(STOUT, CO_50, MONDAY); /* Clue 8: Tuesday's coin was five times Cheryl's */ EITHER yes(TUESDA, CO_25); yes(CHERYL, CO_5); OR yes(TUESDA, CO_50); yes(CHERYL, CO_10); OR yes(TUESDA, CO_5); yes(CHERYL, CO_1); END_EITHER /* Clue 9: Della went earlier in the week than the penny */ lessthan6(DELLA, CO_1, MONDAY); PROB_END /* End of problem-specific data */ no(a1, a2) { int non1, non2, token; if (a1 == -1 || a2 == -1) return; if (a1 == a2 || Val(a1, a2) == T_YES) REJECT; else if (Val(a1, a2) == T_UNK) { Val(a1, a2) = T_NO; no(a2, a1); non1 = non2 = -1; for EVERY_ITEM(token, a1) if (Val(token, a2) != T_NO) if (non1 == -1) non1 = token; else break; if (non1 == -1) REJECT; else if (token == CLASS(a1) + NUM_ITEM) yes(non1, a2); for EVERY_TOKEN(token) if (Val(token, a1) == T_YES) no(a2, token); } } yes(a1, a2) { int token; if (a1 == -2 || a2 == -2) return; if (a1 == -1 || a2 == -1) REJECT; if (Val(a1, a2) == T_NO) REJECT; else if (Val(a1, a2) == T_UNK) { Val(a1, a2) = T_YES; yes(a2, a1); for EVERY_ITEM(token, a1) if (token != a1) no(token, a2); for EVERY_TOKEN(token) if (Val(token, a1) == T_YES) yes(a2, token); else if (Val(token, a1) == T_NO) no(a2, token); } } solveit() { int token, tok2; for EVERY_TOKEN(token) for (tok2 = token; tok2 < TOT_TOKEN; tok2++) if (Val(token, tok2) == T_UNK) { EITHER yes(token, tok2); OR no(token, tok2); END_EITHER; solveit(); REJECT; } printf("Solution:\n"); for EVERY_ITEM(token, 0) { printf("\t%s", Names[token]); for (tok2 = NUM_ITEM; tok2 < TOT_TOKEN; tok2++) if (Val(token, tok2) == T_YES) printf("\t%s", Names[tok2]); printf("\n"); } REJECT; }