Programming Example inspired by Dell Logic Puzzles magazine


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;
}

 

Please send me some e-mail.
Go back to my home page.