From 480f5b874e53f9a7c075053446009425b7d53fbe Mon Sep 17 00:00:00 2001 From: Cody Boone Ferguson <53008573+xexyl@users.noreply.github.com> Date: Tue, 10 Oct 2023 04:07:50 -0700 Subject: [PATCH] Fix 2013/hou + format 2013/hou/README.md The way the Makefile was constructed meant that the program just decompressed the real program which is only the first part of the program. As the judges noted it could be said to break rule 2 (in a way?) because the code actually is a decompressor of the real code. So in order to get the entry to do what it's supposed to do the Makefile rule was modified to be: ${PROG}: ${PROG}.c ${CC} ${CFLAGS} $< -o $@ ${LDFLAGS} ./${PROG} | ${CC} ${CFLAGS} -x c - -o $@ ${LDFLAGS} which first compiles the program to hou and then runs hou, piping the output to the compiler to compile it into hou again. This way when one runs hou (after running make) the actual program is run, saving viewers/users from the extra step (and potential confusion). The README.md file also shows how to just generate the decompressed source code and notes the author's remarks about a.c.gz and a.c (what was included in what was a.c.gz) so as to clarify that the file is not missing but rather that it's not included and does not need to be included either. Since the real code uses math.h the LDFLAGS were modified to include -lm. The .gitignore file was updated to have the PPM files that are generated by the program. Added an INAFIAB section to the README.md and the bugs.md file: it does not terminate on its own and since the author mentioned it wrt a rule it felt like it should be put in such a section. Format fixed README.md. With this commit only a final pass should be necessary to say this entry has been finished though it's entirely possible I discover another problem I guess. --- 2013/hou/.gitignore | 6 ++ 2013/hou/Makefile | 4 +- 2013/hou/README.md | 220 +++++++++++++++++++++++++++++++++----------- bugs.md | 7 ++ thanks-for-fixes.md | 31 ++++++- 5 files changed, 212 insertions(+), 56 deletions(-) diff --git a/2013/hou/.gitignore b/2013/hou/.gitignore index b802b96e6..abea76200 100644 --- a/2013/hou/.gitignore +++ b/2013/hou/.gitignore @@ -2,4 +2,10 @@ a a.c hou hou.orig +hou.tmp +hou.tmp.c prog.orig +luna.ppm +old_default.ppm +4old_default.ppm +otherroom.ppm diff --git a/2013/hou/Makefile b/2013/hou/Makefile index c34686c99..d4bff0c4e 100644 --- a/2013/hou/Makefile +++ b/2013/hou/Makefile @@ -73,7 +73,7 @@ CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT} # Libraries needed to build # -LDFLAGS= +LDFLAGS= -lm # C compiler to use # @@ -130,6 +130,8 @@ all: data ${TARGET} ${PROG}: ${PROG}.c ${CC} ${CFLAGS} $< -o $@ ${LDFLAGS} + ./${PROG} | ${CC} ${CFLAGS} -x c - -o $@ ${LDFLAGS} + # alternative executable # diff --git a/2013/hou/README.md b/2013/hou/README.md index 7341b172c..d81469098 100644 --- a/2013/hou/README.md +++ b/2013/hou/README.md @@ -1,13 +1,13 @@ # Best use of 1 Infinite Loop - Qiming Hou - - +Qiming Hou
+
+
## To build: ```sh -make +make all ``` ## To run: @@ -16,8 +16,9 @@ make ./hou [scene-file-name] [options] ``` -Follow the instructions in stdout, preferably with an auto-refreshing PPM image viewer ready. -Refresh the image every time the output refreshes, all effects should be more or less recognizable when you see 16. +Follow the instructions in `stdout`, preferably with an auto-refreshing PPM +image viewer ready. Refresh the image every time the output refreshes, all +effects should be more or less recognizable when you see 16. ## Try: @@ -28,6 +29,23 @@ Refresh the image every time the output refreshes, all effects should be more or ./hou otherroom.scene NAIVE ``` +Try opening the file and then let the program run a while and every so often +reopen the file, to see how it changes. The author suggests that you leave the +program running overnight to see what happens. If however you do not have all +night :-) then the following JPEG files should show you what happens with some +of the invocations above: + +![./hou BIG - Luna](luna.jpg) + +![./hou old_default.scene - Old default](old_default.jpg) + +![./hou otherroom.scene - Other room](otherroom.jpg) + +### INABIAF - it's not a bug it's a feature! :-) + +This program does not terminate by itself: you must kill `hou` (but not Qiming +Hou :-) ) in order to end the program. This should not be fixed. + ## Judges' remarks: You could consider that this program violates the source code size @@ -36,87 +54,183 @@ really just a decompressor to generate the real source code of the program. This program will loop infinitely while progressively refining a -raytraced image. +[raytraced](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) image. + +NOTE: the author refers to `a.c`, placed in a gzipped file `a.c.gz`. We do not +include it but it can be generated like: + +```sh +cc -Wall hou.c -o hou -lm +./hou > a.c +``` + +but there is no need to do this as the Makefile takes care of it without even +needing to create a temporary file. ## Author's remarks: ### Using hou -This program is a programmable rendering engine with a built-in default scene. The standard command line is: +This program is a programmable rendering engine with a built-in default scene. +The standard command line is: - ./hou [scene-file-name] [options] +```sh +./hou [scene-file-name] [options] +``` -As hou runs, it writes a progressively refining image to a ppm file specified in the scene. The initialization may take a while, but once it's done, a rough preview should be available in seconds. Leave hou running for the night, and you get a high quality result like the attached *.jpg files. Kill hou manually after you're satisfied with the image quality. +As `hou` runs, it writes a progressively refining image to a ppm file specified +in the scene. The initialization may take a while, but once it's done, a rough +preview should be available in seconds. Leave `hou` running for the night, and you +get a high quality result like the attached `*.jpg` files. Kill `hou` manually +after you're satisfied with the image quality. -To save time and energy for the judges, rendered images for all provided scenes are provided as attached files. +To save time and energy for the judges, rendered images for all provided scenes +are provided as attached files. ### Features - * Fully programmable: Almost every stage of the renderer is programmable with a shader, i.e., a short script that does something rendering-related. In particular, each scene consists of one or more geometry shaders, a camera shader, and one or more material shaders. Shaders are written in an interpreted language that supports basic arithmetic, a few math functions, variables and procedural call in the CPS (Continuation Passing Style). - - * Fast preview: This renderer isn't just a naive path tracer, it actually uses a modern global illumination algorithm which is robust against challenging scene configurations. You can mostly place light sources and specify surface properties as you please without worrying too much about the convergence speed -- you always get a nice fast preview within one or two minutes. As proof, there is a comparison: "./hou otherroom.scene" gives a rough idea about the overall illumination at 16 samples per pixel (i.e., when the program prints 16), whereas the naive approach "./hou otherroom.scene NAIVE" only produces a mess of white dots. - - * Rich visual effects: The algorithm samples all light path types so most physically based effects can be produced. The default scene demonstrates quite a few of them: reflection, area light, soft shadows, color bleeding, caustics, and depth-of-field blur. Of course, one can also get programmable-shading effects like procedural texturing and approximated Fresnel terms. - - * Accelerated ray tracing: The ray tracing part uses an algorithm better than the brute-force intersection of everything. The renderer even builds an acceleration data structure! Despite the double precision and the single-threaded-ness, it still runs at about 0.5 million rays per second on the author's machine. - - * Pause and resume: Can't run ./hou overnight? No problem! You can kill and resume a rendering session whenever you want. The renderer automatically saves the progress every 16 samples and resumes where it's left off when restarted. Also, each scene/parameter combination gets a different saved session so you don't have to worry about conflicts. +* Fully programmable: Almost every stage of the renderer is programmable with +a shader, i.e., a short script that does something rendering-related. In +particular, each scene consists of one or more geometry shaders, a camera +shader, and one or more material shaders. Shaders are written in an +interpreted language that supports basic arithmetic, a few math functions, +variables and procedural calls in the CPS (Continuation Passing Style). +* Fast preview: This renderer isn't just a naive path tracer, it actually uses a +modern global illumination algorithm which is robust against challenging scene +configurations. You can mostly place light sources and specify surface +properties as you please without worrying too much about the convergence speed; +you always get a nice fast preview within one or two minutes. As proof, there +is a comparison: `./hou otherroom.scene` gives a rough idea about the overall +illumination at 16 samples per pixel (i.e., when the program prints 16), whereas +the naive approach `./hou otherroom.scene NAIVE` only produces a mess of white +dots. +* Rich visual effects: The algorithm samples all light path types so most +physically based effects can be produced. The default scene demonstrates quite a +few of them: reflection, area light, soft shadows, color bleeding, caustics, and +depth-of-field blur. Of course, one can also get programmable-shading effects +like procedural texturing and approximated Fresnel terms. +* Accelerated ray tracing: The ray tracing part uses an algorithm better than +the brute-force intersection of everything. The renderer even builds an +acceleration data structure! Despite the double precision and the +single-threadedness, it still runs at about 0.5 million rays per second on the +author's machine. +* Pause and resume: Can't run `./hou` overnight? No problem! You can kill and +resume a rendering session whenever you want. The renderer automatically saves +the progress every 16 samples and resumes where it's left off when restarted. +Also, each scene/parameter combination gets a different saved session so you +don't have to worry about conflicts. ### Abuse of the rules - * hou.c uses compression to get around the size limit. The compression fully complies with the rules and the guidelines (at least the portion that shows up in grep "size limit"). Please see the "Self-imposed restrictions" section below for more details. - - * hou does not terminate (as suggested by the second line of rule 6). +* [hou.c](hou.c) uses compression to get around the size limit. The compression +fully complies with the rules and the guidelines (at least the portion that +shows up in `grep` "size limit"). Please see the [Self-imposed +restrictions](self-imposed-restrictions) section below for more details. +* `hou` does not terminate (as suggested by the second line of rule 6). ### Self-imposed restrictions - * The building process does not involve any OS tool beyond cc and make. No gzip compression! a.c.gz doesn't fit in 2053 bytes, anyway. - - * Neither hou.c nor a.c (the *real* decompressed source) uses #define (or cc -D) at all. - - * The source code is not required at runtime. - - * a.c does not drop optional features to reduce size. There are pure optimization code that can be dropped without affecting the converged output (only affecting the ray tracing speed / convergence rate). All files are properly fopened with "rb" / "wb" for Windows compatibility. The PPM header has a comment line for non-standard-compliant viewers (specifically, my old HDRShop 1.0). And there is a nice text message saying "please wait...". - - * Despite the messy look, a.c and hou.c compiles warning-free (hou.c even wastes 18 bytes on #include just for putchar). a.c compiles mostly clean in the C99/ANSI modes of clang and gcc (with -Wall --pedantic). The only warning generated is a pedantic one: "string constant too long". +* The building process does not involve any OS tool beyond cc and make. No gzip +compression! `a.c.gz` doesn't fit in 2053 bytes, anyway. +* Neither [hou.c](hou.c) nor `a.c` (the *real* decompressed source) uses +`#define` (or `cc -D`) at all. +* The source code is not required at runtime. +* `a.c` does not drop optional features to reduce size. There are pure +optimization code that can be dropped without affecting the converged output +(only affecting the ray tracing speed / convergence rate). All files are +properly `fopen()`ed with `"rb"` / `"wb"` for Windows compatibility. The PPM +header has a comment line for non-standard-compliant viewers (specifically, my +old HDRShop 1.0). And there is a nice text message saying "please wait...". +Despite the messy look, `a.c` and [hou.c](hou.c) compile warning-free +([hou.c](hou.c) even wastes 18 bytes on `#include` just for putchar). +`a.c` compiles mostly clean in the C99/ANSI modes of clang and gcc (with `-Wall +--pedantic`). The only warning generated is a pedantic one: `"string constant +too long"`. ### Comments and why obfuscated - * Both the compression and the rendering use mathematically involved algorithms. Understanding the C doesn't help much if one isn't familiar with the math. +* Both the compression and the rendering use mathematically involved algorithms. +Understanding the C doesn't help much if one isn't familiar with the math. +* Modern renderers provide shader access to just about any internal state. This +one follows suit and reuses the same shader-accessible array for all important +internal states. +* Infinity and NaN (not-a-number) are used during normal course of execution. +* Previous image-generating entries may take care to hide the "IOCCC" string in +the code, but they leave the text clear in the *result*. This entry takes it +further and obfuscates the output image as well. Can you find the text in the +image? Hint: look up. +* `a.c` leaves all shaders in plain text, but the plain text shader code can't +be taken for its face value; the arithmetic rules subtly diverge from our common +sense. +* `a.c` is less portable than [hou.c](hou.c) itself. [hou.c](hou.c) only depends +on ASCII and should run just fine on 16-bit, small memory, or +floating-point-incapable machines. `a.c`, while still reasonably portable, is +quite memory consuming, requires IEEE754-compliant double, and assumes int to be +32-bit. - * Modern renderers provide shader access to just about any internal state. This one follows suit and reuses the same shader-accessible array for all important internal states. - - * Infinity and not-a-number are used during normal course of execution. - - * Previous image-generating entries may take care to hide the "IOCCC" string in the code, but they leave the text clear in the *result*. This entry takes it further and obfuscates the output image as well. Can you find the text in the image? Hint: look up. - - * a.c leaves all shaders in plain text, but the plain text shader code can't be taken for its face value -- the arithmetic rules subtly diverge from our common sense. - - * a.c is less portable than hou.c itself. hou.c only depends on ASCII and should run just fine on 16-bit, small memory, or float-incapable machines. a.c, while still reasonably portable, is quite memory consuming, requires IEEE754-compliant double, and assumes int to be 32-bit. - - * Though technically endian-dependent, a.c remains portable providing that one doesn't copy saved sessions across endians. +* Though technically endian-dependent, `a.c` remains portable providing that one +doesn't copy saved sessions across different endianness. ### Spoiler +``` 3225 3225 3225 9 9 3225 3225 1 1 1 1 1 1 1 1 1 1 4225 1226 1 1 1 1 1222 1226 1 1 1 1 1 1 1 1 1 4226 1 4226 8 4222 4226 1 1 +``` - The program consists of a recursive-descend interpreter, a 3DDDA (3D Discrete Differential Analysis) ray tracer, a PSSMLT (Primary Sample Space Metropolis Light Transport) light path sampler, all squeezed into the size limit using a PPM compressor (Prediction by Partial Matching, and yes, the output format is chosen for the pun). +The program consists of a recursive-descent interpreter, a 3DDDA (3D Discrete +Differential Analysis) ray tracer, a PSSMLT (Primary Sample Space Metropolis +Light Transport) light path sampler, all squeezed into the size limit using a +PPM compressor (Prediction by Partial Matching, and yes, the output format is +chosen for the pun..). - PSSMLT uses the Metropolis-Hasting algorithm to sample a 32D unit hypercube. Each point in the hypercube is interpreted as a sequence of random numbers, and is sent to a path tracer to generate a light path. The point's Metropolis-Hasting energy is then defined as the corresponding path's contribution value to the final image. Since each path is sampled with a probability proportional to its energy, the sample distribution directly corresponds to the final image, which can then be produced as a simple per-pixel histogram of all generated paths. The robustness comes from a state mutation strategy that actively tries to explore the neighborhood of high energy peaks (e.g. paths that happen to hit the light source in otherroom.scene). In addition, a rudimentary form of lens path stratification is added to balance the attention each pixel receives. The Metropolis-Hasting process completely avoids the tell-tale pixel sampling loop required in most other image generation methods. +PSSMLT uses the Metropolis-Hasting algorithm to sample a 32D unit hypercube. +Each point in the hypercube is interpreted as a sequence of random numbers, and +is sent to a path tracer to generate a light path. The point's +Metropolis-Hasting energy is then defined as the corresponding path's +contribution value to the final image. Since each path is sampled with a +probability proportional to its energy, the sample distribution directly +corresponds to the final image, which can then be produced as a simple per-pixel +histogram of all generated paths. The robustness comes from a state mutation +strategy that actively tries to explore the neighborhood of high energy peaks +(e.g. paths that happen to hit the light source in +[otherroom.scene](otherroom.scene)). In addition, a rudimentary form of lens +path stratification is added to balance the attention each pixel receives. The +Metropolis-Hasting process completely avoids the tell-tale pixel sampling loop +required in most other image generation methods. - The 3DDDA tracer is chosen for scalability: its performance doesn't get much worse as scene complexity increases. Another benefit is that with the DDA code in place one can naturally use hierarchical grids as an acceleration structure. The downside, of course, is that the setup involves quite a few divisions, which naturally turns into division-by-zeros. Fortunately, the IEEE754 standard has a nice set of rules just for this purpose and the arithmetics are organized in a specific way to take advantage of this. The shader interpreter component is relatively straightforward, just an expression evaluator stripped to the bare minimum -- it doesn't even support numerical constants natively. A final little bit is a just-good-enough PRNG (Pseudo Random Number Generator) to replace the low precision Windows rand() and the non-C99 Unix drand48(). An overnight session would run through its short period many times, but that doesn't necessarily map to the same set of paths in PSSMLT. After all, Metropolis et al. used an even worse PRNG in their 1953 paper. +The 3DDDA tracer is chosen for scalability: its performance doesn't get much +worse as scene complexity increases. Another benefit is that with the DDA code +in place one can naturally use hierarchical grids as an acceleration structure. +The downside, of course, is that the setup involves quite a few divisions, which +naturally turns into divisions-by-zero. Fortunately, the IEEE754 standard has a +nice set of rules just for this purpose and the arithmetics are organized in a +specific way to take advantage of this. The shader interpreter component is +relatively straightforward, just an expression evaluator stripped to the bare +minimum -- it doesn't even support numerical constants natively. A final little +bit is a just-good-enough PRNG (Pseudo Random Number Generator) to replace the +low precision Windows rand() and the non-C99 Unix drand48(). An overnight +session would run through its short period many times, but that doesn't +necessarily map to the same set of paths in PSSMLT. After all, +Metropolis-Hasting used an even worse PRNG in their 1953 paper. - The PPM compressor uses statically weighted fixed order contexts with an arithmetic encoder tweaked for iocccsize. The encoder emits octet-space pairs where each octet encodes ~6.5 bits of information and each space encodes 2 bits (thanks to the generous definition of "space" in iocccsize.c). The compressor actively shuffles the variable names around until the compressed string happens to contain enough "{}; " to pass the final iocccsize test. There are a few other tweaks: - - * The PPM model uses mostly whitespace characters for weights. - - * The encoder never emits '"' and '\\'. +The PPM compressor uses statically weighted fixed order contexts with an +arithmetic encoder tweaked for [iocccsize.c](../iocccsize.c). The encoder emits octet-space pairs +where each octet encodes ~6.5 bits of information and each space encodes 2 bits +(thanks to the generous definition of "space" in [iocccsize.c](../iocccsize.c)). The compressor +actively shuffles the variable names around until the compressed string happens +to contain enough `{}; ` to pass the final [iocccsize.c](../iocccsize.c) test. +There are a few other tweaks: - * The decoder uses an O(n^2) algorithm to avoid the gigabytes-sized hash table frequently found in other PPM implementations. +* The PPM model uses mostly whitespace characters for weights. + +* The encoder never emits `'"'` and `'\\'`. + +* The decoder uses an O(n^2) algorithm to avoid the gigabytes-sized hash table +frequently found in other PPM implementations. ## Copyright and CC BY-SA 4.0 License: diff --git a/bugs.md b/bugs.md index 7eda8c0d9..74507f0fe 100644 --- a/bugs.md +++ b/bugs.md @@ -1852,6 +1852,13 @@ exercise to see if you understand the code. You can try and answer the questions, too: when will it crash? When will it draw something funny (or will it? :-) ) and when will it just do nothing? +## [2013/hou](2013/hou/hou.c) ([README.md](2013/hou/README.md)) +## STATUS: INABIAF - please **DO NOT** fix + +This program will not terminate on its own; you must kill `hou` (but not Qiming +Hou :-) ) yourself. This should not be fixed. + + # 2014 diff --git a/thanks-for-fixes.md b/thanks-for-fixes.md index beafc6292..9d011a616 100644 --- a/thanks-for-fixes.md +++ b/thanks-for-fixes.md @@ -1639,9 +1639,36 @@ exists or can be read as that will be handled by the shell/program. ## [2013/hou](2013/hou/hou.c) ([README.md](2013/hou/README.md)) -After the file 2013/hou/doc/example.markdown was moved to +Cody fixed the Makefile so that this would work properly. Before this the use of +the program just did what the judges' remarks said as far as how it might +violate rule 2: the program is really just a decompressor to generate the +real source of the program. So the source of the entry has to be compiled and +then run, and the output has to be compiled to be `hou`. This allows the real +program to be used. Thus the Makefile rule looks like: + +```makefile +${PROG}: ${PROG}.c + ${CC} ${CFLAGS} $< -o $@ ${LDFLAGS} + ./${PROG} | ${CC} ${CFLAGS} -xc - -o $@ ${LDFLAGS} +``` + + + +which then compiles like: + +```sh +cc -std=gnu11 -Wall -Wextra -pedantic -Wno-sign-compare -Wno-strict-prototypes -O3 hou.c -o hou -lm +./hou | cc -std=gnu11 -Wall -Wextra -pedantic -Wno-sign-compare -Wno-strict-prototypes -O3 -xc - -o hou -lm +``` + +The `LDFLAGS` were updated to have `-lm` as the author suggested it uses the +`math.h` library which not all systems link in by default (linux for instance +does not). + +Further, after the file 2013/hou/doc/example.markdown was moved to [2013/hou/doc/example.md](2013/hou/doc/example.md) to match the rest of the repo -this broke `make` which Cody fixed. +this broke `make` which Cody also fixed. + ## [2013/morgan1](2013/morgan1/morgan1.c) ([README.md](2013/morgan1/README.md))