From 1a523b12f60b0f6d6d4c16ff53a62b4d8781afab Mon Sep 17 00:00:00 2001 From: Cody Boone Ferguson <53008573+xexyl@users.noreply.github.com> Date: Fri, 14 Apr 2023 05:47:07 -0700 Subject: [PATCH] Update 1995/vanschnitz Added spoiler.c from their uuencoded text. Noted in README.md that they encourage readers to try and figure it out first but the idea (told to me by Landon) is that the README.md should no longer (generally speaking) hide details. Format fixes in README.md. Note that this is one of the entries noted in bugs.md in commit 704f7224c0520d822e9cc99d0edeef7f03dc6516 but I don't believe spoiler.c is the same as the missing file. That might be me reading it wrong in which case that can be removed from bugs.md but it appears to not be: one is a spoiler and another is different functionality. --- 1995/vanschnitz/README.md | 60 ++++++---- 1995/vanschnitz/spoiler.c | 239 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 1995/vanschnitz/spoiler.c diff --git a/1995/vanschnitz/README.md b/1995/vanschnitz/README.md index 6180eef8d..23544dd91 100644 --- a/1995/vanschnitz/README.md +++ b/1995/vanschnitz/README.md @@ -1,24 +1,38 @@ # Worst Abuse of the C Preprocessor and Most Likely To Amaze - Mark Schnitzius & David Van Brackle - ISX Corporation - 1165 Northchase Parkway - Suite 120 - Marietta, GA 30067 - USA +Mark Schnitzius & David Van Brackle +ISX Corporation +1165 Northchase Parkway +Suite 120 +Marietta, GA 30067 +USA ## To build: - make all +```sh +make all +``` ## To run: - ./vanschnitz +```sh +./vanschnitz +``` ## Try: - make clobber all LEVEL=6 - ./vanschnitz +```sh +make clobber all LEVEL=6 +./vanschnitz +``` + +### Alternate code: + +The authors provided a spoiler version of the program. Originally uuencoded we +have decoded it for the wider audience in [spoiler.c](spoiler.c). The +`uuencode`d blob is still available in their remarks below. They encourage you +to first try and figure it out without it. + ## Judges' remarks: @@ -44,30 +58,32 @@ solution. How do you trick a compiler into actually solving the problem? First, you must tell it how many disks you wish to solve it -for. This is done by defining the symbol 'n' on the compile +for. This is done by defining the symbol `n` on the compile line. For instance, to cause the compiler to solve the Towers of Hanoi problem with four disks, you would compile the program like this: - gcc hanoi.c -o hanoi -Dn=4 +```sh +gcc hanoi.c -o hanoi -Dn=4 +``` -A default value of 5 will be used for n if you do not define -it on the command line. The value of n cannot be greater than +A default value of `5` will be used for `n` if you do not define +it on the command line. The value of `n` cannot be greater than fifteen (the compiler we used to test has a limit on the #include depth). The compiler then solves the problem using binary arithmetic based on whether particular symbols are defined or not. -To loop, the program #include's itself. This is, of course, -expensive; one compile we did with n=14 took about fifty -minutes to compile on our system (compiling with n=15 -caused our system to crash). +To loop, the program `#include`s itself. This is, of course, +expensive; one compile we did with `n=14` took about fifty +minutes to compile on our system (compiling with `n=15` caused our system to +crash). The resulting program that the compiler generates simply prints out the answer. Did I say "simply"? Actually, the whole resulting program consists of a single printf statement, consisting of a massive string constant of length `35*(2^n-1)`, followed by `3*(2^n-1)` integers which get formatted into the -string. For our n=14 run, this adds up to a string constant +string. For our `n=14` run, this adds up to a string constant of length 573405, followed by 49149 integers delimited by commas. (Generating the string constant depends on the ANSI C feature in which adjacent character strings are @@ -75,14 +91,16 @@ catenated; a version that does not use this feature has been included for people who can only run K&R). A good way to see the resulting program (on a Unix system) is to do the command - gcc hanoi.c -E -Dn=5 | grep -v \# | grep -v ^\$ +```sh +gcc hanoi.c -E -Dn=5 | grep -v \# | grep -v ^\$ +``` For an odd number of disks, the program will provide a solution wherein the disks end up on peg 2; for an even number of disks, they will end on peg 3. This should provide some hint as to what sort of algorithm is used. -We have included a "spoiler" version of the program, with +We have included a [spoiler](spoiler.c) version of the program, with meaningful symbol names and comments, but we encourage you to try to decipher the program without it... diff --git a/1995/vanschnitz/spoiler.c b/1995/vanschnitz/spoiler.c new file mode 100644 index 000000000..d22d60cd5 --- /dev/null +++ b/1995/vanschnitz/spoiler.c @@ -0,0 +1,239 @@ +#ifndef INITIALIZED + +/* + * If n isn't defined, make it 5 + */ +#ifndef n +#define n 05 +#endif + +/* + * Some useful variables + */ +#define THIS_FILE __FILE__ +#define FORMAT_STRING "Move disk %d from peg %d to peg %d\n" +#define INITIALIZED + +/* + * Translate n into bits + */ +#if n&01 +#define bit0 +#endif +#if n&02 +#define bit1 +#endif +#if n&04 +#define bit2 +#endif +#if n>>3 +#define bit3 +#endif + +/* + * Here's the program! + */ +main(){ +printf( +#include THIS_FILE +#define NOW_DO_THE_NUMBERS +#include THIS_FILE +);} + +#else + +/* + * Check to see if the number is zero (bottom of recursion) + */ +#ifdef bit0 +#define N_IS_NOT_ZERO +#endif +#ifdef bit1 +#define N_IS_NOT_ZERO +#endif +#ifdef bit2 +#define N_IS_NOT_ZERO +#endif +#ifdef bit3 +#define N_IS_NOT_ZERO +#endif + +#ifdef N_IS_NOT_ZERO +#undef N_IS_NOT_ZERO + +/* + * Decrement n + */ +#ifndef bit0 +#define bit0 +#ifndef bit1 +#define bit1 +#ifndef bit2 +#define bit2 +#ifndef bit3 +#define bit3 +#else +#undef bit3 +#endif +#else +#undef bit2 +#endif +#else +#undef bit1 +#endif +#else +#undef bit0 +#endif + +/* + * Here's recursion + */ +#include THIS_FILE + +/* + * Note: Odd numbered disks always cycle from 1 => 2 => 3 => 1 => 2 => 3 + * Even numbered disks always cycle from 1 => 3 => 2 => 1 => 3 => 2 + */ + +/* + * Check to see if n is zero. If so, we're moving disk 1. + * In general, for any n we're moving disk n+1. That means that + * when n is odd, we're moving an even disk, and vice versa. + */ +#ifndef bit0 +#ifndef bit1 +#ifndef bit2 +#ifndef bit3 +#define N_IS_ZERO +#endif +#endif +#endif +#endif + +#ifdef N_IS_ZERO +#undef N_IS_ZERO + +#ifndef NOW_DO_THE_NUMBERS +FORMAT_STRING +#else + +/* + * If disk 1 is on peg 2, move it 2 => 3 + */ +#ifdef DISK_ONE_IS_ON_PEG_TWO +,1,2,3 +#undef DISK_ONE_IS_ON_PEG_TWO +#define DISK_ONE_IS_ON_PEG_THREE +#else + +/* + * If disk 1 is on peg 3, move it 3 => 1 + */ +#ifdef DISK_ONE_IS_ON_PEG_THREE +,1,3,1 +#undef DISK_ONE_IS_ON_PEG_THREE +#else + +/* + * Disk 1 must be on peg 1. Move it 1 => 2 + */ +,1,1,2 +#define DISK_ONE_IS_ON_PEG_TWO +#endif +#endif +#endif +#else + +/* + * Here, we handle disks >1. Remember that odds cycle 1 => 2 => 3 => 1, + * and evens cycle 1 => 2 => 3 => 1. Combine this with the knowledge + * of where disk 1 is, and we can determine how to move any disk. + */ +#ifndef NOW_DO_THE_NUMBERS +FORMAT_STRING +#else + +/* + * Print the disk number. Remember, disk number = n+1 + */ +,1 +#ifdef bit0 ++1 +#endif +#ifdef bit1 ++2 +#endif +#ifdef bit2 ++4 +#endif +#ifdef bit3 ++8 +#endif + +/* + * Disk 1 on peg 2 + */ +#ifdef DISK_ONE_IS_ON_PEG_TWO +#ifdef bit0 +,1,3 /* Even: 1 => 3 (n is odd, so the disk is even) */ +#else +,3,1 /* Odd: 3 => 1 */ +#endif +#else + +/* + * Disk 1 on peg 3 + */ +#ifdef DISK_ONE_IS_ON_PEG_THREE +#ifdef bit0 +,2,1 /* Even: 2 => 1 */ +#else +,1,2 /* Odd: 1 => 2 */ +#endif +#else + +/* + * Disk 1 on peg 1 + */ +#ifdef bit0 +,3,2 /* Even: 3 => 2 */ +#else +,2,3 /* Odd: 2 => 3 */ +#endif +#endif +#endif +#endif +#endif + +/* + * More recursion + */ +#include THIS_FILE + +/* + * Increment n + */ +#ifdef bit0 +#undef bit0 +#ifdef bit1 +#undef bit1 +#ifdef bit2 +#undef bit2 +#ifdef bit3 +#undef bit3 +#else +#define bit3 +#endif +#else +#define bit2 +#endif +#else +#define bit1 +#endif +#else +#define bit0 +#endif + +#endif /* #ifdef N_IS_NOT_ZERO */ + +#endif /* #else of #ifndef INITIALIZED */