release winning entries of IOCCC28 under the year 2024

Congratulations to:

* 2024/anders - Prize in sound coding - layered music generator
* 2024/burton - Prize in pentagrammatology - wordle hard mode solver
* 2024/cable1 - Prize in bot talk - LLM inference engine
* 2024/cable2 - Prize in murky waters - salmon recipe
* 2024/carlini - Prize in perfect timing - Intel 4004 emulation
* 2024/codemeow - Prize in tray planting - bonsai generator
* 2024/endoh1 - Prize in patient pointillism - slow ray tracer in CPP
* 2024/endoh2 - Prize in solid body physics - rotating rigid body simulation
* 2024/ferguson1 - Prize in diabolical logistics - Oregon trail pact with the devil
* 2024/ferguson2 - Prize in yil-tas - Navajo code talker translator
* 2024/howe - Nice accent eh-ward - mini vi
* 2024/kramer - Prize in linguistic arithmetic - natural language numerical calculator
* 2024/kurdyukov1 - Prize in phased periodicity - phase of the Moon
* 2024/kurdyukov2 - Prize in art restoration - JPEG artifact removal
* 2024/kurdyukov3 - Prize in virtual quietus - gaming VM and doom
* 2024/kurdyukov4 - Prize in embiggening - encode without literals
* 2024/maffiodo - Prize in creative interpretation - Jav\*script interpreter
* 2024/mills - Prize in ℤ₃ - Infocom v3 story interpreter
* 2024/stedolan - Best one liner - icon from MD5
* 2024/straadt - Prize in imitative rebooting - OpenRISC CPU and Linux
* 2024/tmarrec - Prize in cyclonic coding - 3D tornado simulation
* 2024/tompng - Prize in quasi bijection - vortex encryptor decryptor
* 2024/weaver - Sur prize - rickroll

Ran `make update` to perform the above.
 	new file:   2024/burton/.entry.json
This commit is contained in:
Landon Curt Noll committed 2025-08-02 14:43:07 -07:00
1 parent 10512e6c79
commit 09750f8899
443 files changed
+182938 -231

No files matched your search

+516
View File
@@ -0,0 +1,516 @@
## To build:
``` <!---sh-->
make all
```
## Try:
``` <!---sh-->
./try.sh
```
**HINT**: You have to be **VERY** patient while the above script runs to completion.
## Judges' remarks:
When you `make all` you see what looks like a C program, `rt.c`.
However if you try to compile `rt.c`, you will fail: even if you try to
define the symbols `X`, `Y`, `W`, and `H`.
Well, the propose of the C program, `rt.c` is to generate the red, green, and blue colors of
a single pixel, in the C Preprocessor!!!
For the patient, a image may be rendered a slow pixel at a time.
Think of this as digital [Pointillism](https://en.wikipedia.org/wiki/Pointillism). :-)
For the truly impatient who wishes to get right to the point without trying to dot around
waiting for the [try.sh](%%REPO_URL%%/2024/endoh1/try.sh) to finish, you may view these images:
* [tiny 8x8 image](ref.8.png)
* [small 32x32 image](ref.32.png)
* [medium 128x128 image](ref.128.png)
* [large 512x512 image](ref.512.png)
**FUN FACT**: We were able to determine on an "_x86-64 processor_" running
"_RHEL 9.6_" using "_gcc version 11.5.0_", one could theoretically render
a **2147483647 x 2147483647** pixel image. At the rate it took to render
the **512 x 512** pixel image, and without using any parallelism, such an
image would take about **169 billion years** to fully render on a single
CPU core! Fully rendering such large images is **NOT** recommended. :-)
### Parallel make
**SUGGESTION**: The [2024/endoh1/Makefile](%%REPO_URL%%/2024/endoh1/Makefile),
by default, uses a single CPU core, can make use of parallel operations.
If you have access to a "_parallel make_", can achieve the above in a shorter
around of time. If you have "_n_" CPU cores available, consider running
a parallel make with "_n-1_" jobs that run simultaneously. For example:
``` <!---sh-->
RESERVE="-2"
NPROC=$(nproc)
((JOBS=NPROC-RESERVE))
SIZE=512
make clobber all
nohup time make -j "$JOBS" out.ppm H="$SIZE" W="$SIZE" > make.out 2>&1 < /dev/null &
```
Where `SIZE` is the size of the image to generate.
Where `RESERVE` is the number of CPU cores to reserve for other things
such as the operating system, daemons, terminal window, etc. You can even
"_oversubscribe_" by setting RESERVE to a negative number (e.g., `RESERVE="-2"`).
**NOTE**: The [try.sh](%%REPO_URL%%/2024/endoh1/try.sh) script follows the above
example for `SIZE` values of 8, 32, 128, and then 512.
The optimal `RESERVE` will depend on your hardware, operating system,
a C compiler. Making `RESERVE` too small (i.e., too negative) may slow
down the result. We recommend that experiment various values pf `RESERVE`
with moderate `SIZE` of **32** in order to try to find an optimal `JOBS`
value before attempting to try a larger size such as **512** and beyond.
<div id="larger">
### Be first to build images larger than 512x512
</div>
**QUESTION**: Do you have the patience to build an even larger than a 512x512 image?
If you do, **using the program as it is written of course**, please [send the IOCCC](../../judges.html)
the following:
* Image Height x Width in pixels
* URL of the fully rendered image (in PPM form or better yet converted to PNG)
* How long it took (perhaps using the time(1) command or using the `time` shell builtin)
* The starting and ending dates (using `date -u`) of your rendering
* Number of CPU cores, Operating system, Compiler, Compiler version, and make version
* The number of jobs you ran in parallel
* Anything else you may have done to optimize
* Your name, (GitHub handle (optional)), (Mastodon handle (optional)), (home page URL (optional)), Country (or XX to be anonymous)
**PLEASE** do not attempt to optimize the general algorithm used by this entry to build PPM images.
Please remain "_faithful to the spirit_" of this wonderful entry when attempting to build larger PPM images.
The first people to render a new record sized image **using the general algorithm used by this entry**,
will be given credit in the [thanks for the help](../../thanks-for-help.html) web page.
## Author's remarks:
### A Ray Tracer in the C Preprocessor
Ever since the iconic 1988 IOCCC winning entry [1988/applin](../../1988/applin/index.html),
bending the C preprocessor to one's will has been a time-honored tradition in the IOCCC.
This entry sets out to be the culmination of that legacy: a ray tracer implemented entirely with C macros!
#### How to Run
The initial program, `./prog` (compiled from `prog.c`), outputs the main code, `rt.c`.
To get started, compile and execute it:
``` <!---sh-->
make all
```
Note: `rt.c` is not a valid C program -- it's written for C preprocessor.
To get the color of a single pixel at position (16, 16) in a 32 x 32 image, run:
``` <!---sh-->
cc -E -P -DX=16 -DY=16 -DW=32 -DH=32 rt.c
```
This will produce:
```
129
62
129
```
These three values represent the red, green, and blue components respectively.
* Red = 129 (0x81)
* Green = 62 (0x3e)
* Blue = 129 (0x81)
In other words, this indicates that the pixel at position (16, 16) has the color #813e81.
#### How to Generate the Full Image
To generate the entire image, you need to run the preprocessor for every pixel.
For example, to form a 32x32 image:
``` <!---sh-->
mkdir -p pixel
cc -E -P -DX=0 -DY=0 -DW=32 -DH=32 rt.c > pixel/pixel-00-00.txt
cc -E -P -DX=1 -DY=0 -DW=32 -DH=32 rt.c > pixel/pixel-00-01.txt
cc -E -P -DX=2 -DY=0 -DW=32 -DH=32 rt.c > pixel/pixel-00-02.txt
...
cc -E -P -DX=30 -DY=0 -DW=32 -DH=32 rt.c > pixel/pixel-00-30.txt
cc -E -P -DX=31 -DY=0 -DW=32 -DH=32 rt.c > pixel/pixel-00-31.txt
cc -E -P -DX=0 -DY=1 -DW=32 -DH=32 rt.c > pixel/pixel-01-00.txt
cc -E -P -DX=1 -DY=1 -DW=32 -DH=32 rt.c > pixel/pixel-01-01.txt
cc -E -P -DX=2 -DY=1 -DW=32 -DH=32 rt.c > pixel/pixel-01-02.txt
...
cc -E -P -DX=30 -DY=31 -DW=32 -DH=32 rt.c > pixel/pixel-31-30.txt
cc -E -P -DX=31 -DY=31 -DW=32 -DH=32 rt.c > pixel/pixel-31-31.txt
```
Then, concatenate the results with a PPM header to create the image:
``` <!---sh-->
echo P3 32 32 > out.ppm
echo 255 >> out.ppm
find pixel -type f -print | LANG=C sort | xargs cat >> out.ppm
```
Now open `out.ppm` with any image viewer that supports the PPM format.
#### Automating with Makefile
To form a 32x32 image:
``` <!---sh-->
make clean targets out.ppm H=32 W=32
```
Now open `out.ppm` with any image viewer that supports the PPM format.
#### Generating a Larger Image
To form a 256x256 image:
``` <!---sh-->
make clean targets out.ppm H=256 W=256
```
Now open `out.ppm` with any image viewer that supports the PPM format.
For reference, the 512x512 image took about 10 hours to render.
#### Technical Challenges
Below is a technical explanation of the implementation.
##### Copying a Macro Definition
One core challenges was how to "copy" the value of a macro.
For example, in C macros:
``` <!---c-->
#define X 10
#define Y X
#undef X
#define X 20
Y // expands to "20", not "10"
```
The C preprocessor uses lazy evaluation, meaning macro values are always resolved at the time of use.
With this observation, some people claim it's impossible to copy a macro's value.
One known workaround is encoding one bit by whether or not a macro is defined (as seen in `applin.c` from IOCCC 1988 and `vik2.c` from IOCCC 2004).
However, this approach is too inefficient for our purposes -- ray tracing requires operations like multiplication, and even square root.
Instead, we discovered an efficient way to "copy" a macro's value.
The key observation is that `#if` eagerly evaluates the definition of a macro at that point in preprocessing.
For example:
``` <!---c-->
#if X % 2
# define X_b0 1 // The LSB of X is 1
#else
# define X_b0 0 // The LSB of X is 0
#endif
```
This technique copies the least significant bit of `X`.
By applying this for each bit, we can reconstruct the full value:
``` <!---c-->
#define X 10
#if X % 2
# define X_b0 1
#else
# define X_b0 0
#endif
#if X / 2 % 2
# define X_b1 2
#else
# define X_b1 0
#endif
#if X / 4 % 2
# define X_b2 4
#else
# define X_b2 0
#endif
...
#define Y (X_b0 + X_b1 + X_b2 + ...)
#undef X
#define X 20
Y // replaced with "0 + 2 + 0 + 8 + ..." = 10
```
Even if `X` is later redefined, `Y` remains unchanged.
This technique resembles ["information flow"][information-flow] in the information security.
To our best knowledge, there has been no literature that mentions this technique in terms of the C preprocessor programming.
[information-flow]: https://en.wikipedia.org/wiki/Information_flow_(information_theory)
##### Macro Expansion Explosion
Using macros for complex expressions often causes memory issues.
For instance, consider this Newton's method implementation for square roots:
``` <!---c-->
#define sqrt0(x, guess) (guess ? (x/guess+guess)/2 : 0)
#define sqrt(x) sqrt0(x,sqrt0(x,sqrt0(x,sqrt0(x,sqrt0(x,10000)))))
```
The subroutine `sqrt0` is one step of Newton's method, and `sqrt` applies it five times.
There are three occurences of the argument `guess` in the body of `sqrt0`.
Thus, applying `sqrt` repeats its argument expression at least 5^3 = 243 times.
This easily leads to the explosion of macro expansion.
We avoid this issue by using the earlier "copy" trick -- reducing the size of macro expansions by materializing their values.
##### Encoding Vectors
In theory, it is trivial to encode a 3D vector in the C preprocessor: you can just define three macros for x, y, and z components.
However, it creates bloated code -- unsuitable for IOCCC size limits.
Instead, we encode vectors as simple tuples:
``` <!---c-->
#define vec 1,2,3
```
The vector projection can be implemented as follows.
``` <!---c-->
#define fst(x,y,z) x
#define snd(x,y,z) y
#define thd(x,y,z) z
#define call(f,a) f(a)
```
Example usage:
``` <!---c-->
call(fst, vec) // expands to 1
call(snd, vec) // expands to 2
call(thd, vec) // expands to 3
```
The auxiliary macro `call` is required.
This is a hassle of the C preprocessor, but, `call(fst, vec)` is expanded to `fst(1,2,3)`, which results in `1`.
We can define a vector addition by using the projections.
``` <!---c-->
#define vec3_add(a,b) fst(a)+fst(b),snd(a)+snd(b),thd(a)+thd(b)
```
Now, we can simply write `vec3_add(v1, v2)` to add two vectors.
One identifier represents one vector.
It is also elegantly possible to nest expressions like `vec3_add(v1, vec3_add(v1, v2))`.
``` <!---c-->
#define v1 0,1,2
#define v2 2,1,0
int main() {
printf("(%d %d %d)\n", vec3_add(v1, v2)); // prints "(2 2 2)"
printf("(%d %d %d)\n", vec3_add(v1, vec3_add(v1, v2))); // prints "(2 3 4)"
}
```
##### Ray Tracing via Self-Inclusion
Ray tracing involves recursive calls for reflection.
In `rt.c`, this is done via:
``` <!---c-->
#include __FILE__
```
In the implementation, only spheres reflect rays, and only one sphere exists.
Thus, the recursion depth is limited to one.
Theoretically, it is possible to support multiple reflections, but this would require implementing a "stack" to keep "local variables" in a stack frame.
##### Outputting a Pixel
The preprocessed result of `rt.c` contains some preprocessor directives (starting with `#`) and three integers (red, green, and blue).
``` <!---sh-->
cc -E -DX=16 -DY=16 -DW=32 -DH=32 rt.c
```
This will produce:
``` <!---c-->
# 0 "rt.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "rt.c"
# 3481 "rt.c"
# 1 "rt.c" 1
# 2090 "rt.c"
# 1 "rt.c" 1
# 2091 "rt.c" 2
# 3482 "rt.c" 2
# 3582 "rt.c"
129
# 3684 "rt.c"
62
# 3786 "rt.c"
129
```
Interestingly, these are valid in the PPM format (lines starting with `#` are commments in PPM).
Thus, just concatenating the results for all pixels (and prefixing a small header) will work to create a PPM image file.
When render a large image, we recommend use the `-E -P` flags as in:
``` <!---sh-->
cc -E -P -DX=16 -DY=16 -DW=32 -DH=32 rt.c
```
This will only produce:
```
129
62
129
```
Note that values appear as expressions like `(0 + 2 + 0 + 8 + ...)` because we use the "copy" trick.
To print them as to ordinary decimal values, we use macro logic like:
``` <!---c-->
// assume red is (0 + 2 + 0 + 8)
#if red / 100 % 10 == 0
# define digit0(digits) 0##digits
#elif red / 100 % 10 == 1
# define digit0(digits) 1##digits
#else
# define digit0(digits) 2##digits
#endif
#if red / 10 % 10 == 0
# define digit1(digits) digits0(0##digits)
#elif red / 10 % 10 == 1
# define digit1(digits) digits0(1##digits)
#elif ...
...
#endif
#if red % 10 == 0
# define digits digit1(0)
#elif red % 10 == 1
# define digits digit1(1)
#elif ...
...
#endif
digits // outputs "10"
```
#### Customizing the Scene
You can modify the scene by editing the top of `rt.c`.
##### Example Defaults
``` <!---c-->
#define p 0,500,4000 // sphere center position
#define q 2000 // sphere radius
#define r 300,300,300 // sphere color (default: light gray)
#define s(x) 900,200+K*7100,900 // plane color (default: checkered colors of white for K=1 and purple for K=0)
#define t -2000,4000,1000 // light position
#define u 50000,50000,50000 // light color (default: white)
#define v(x) 500-x,500-x,500 // background color (default: gradation from blue to white)
```
##### Variant Example
``` <!---c-->
#define p 0,0,8000
#define q 2000
#define r 300,300,900
#define s(x) 200,500-x*300,200+x*800
#define t 2000,4000,3000
#define u 30000,40000,30000
#define v(x) 500,x,10
```
Features:
* Grounded blue sphere
* Blue and green checkered floor
* Sunset-style lighting
See `ref.512-variant.png` for the rendered result.
#### Limitations
Currently, this program supports:
* One sphere
* One floor
* One light
Adding more lights is relatively simple.
Adding multiple spheres would require stack support for recursive ray tracing (see "Ray Tracing by Self-Inclusion" section).
Note: we recommend GCC for performance; Clang is slower for C preprocessor-heavy tasks like this one.
<!--
Copyright © 1984-2025 by Landon Curt Noll and Leonid A. Broukhis. All Rights Reserved.
You are free to share and adapt this file under the terms of this license:
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see:
https://creativecommons.org/licenses/by-sa/4.0/
-->