Solution to In C
This puzzle is a listing of 53 lines of C code, very vaguely ordered by length. Some of the lines are identical; others are clearly not valid C. We may suspect that our goal is to construct a valid program that follows the rules in the opening comment, and then see what it does when run.
Our first clue is the title and copyright year. In C is a piece of music composed in 1964 that consists of 53 melodic fragments, to be repeated any number of times or skipped at the discretion of each performer, but remaining in order. Each fragment thus corresponds to one line of the program; identical fragments give identical lines. The correspondence is based on the pitch of each note:
A | hexadecimal number |
B♭ | control flow keyword |
B | function |
C | type or type-related keyword |
D | character literal |
E | variable |
F | decimal number |
F♯ | ++/−− (variable increment/decrement) |
G | &/* (pointer reference/dereference) |
rest | comment |
In other words, the order in which these categories of thing appear in each line of C matches the order of the notes in the corresponding In C fragment. Things not in these categories (for example, parentheses and arithmetic or logical operators) have no musical counterpart; tied notes and consecutive rests count as one thing for matching purposes.
Using the score of In C, we can match everything up and determine the correct ordering of the lines, but this does not form a valid program. We still have to figure out how many times to repeat each line. We can do this by going through step by step and examining what the lines do, using the comments as signposts, and remembering that we have to set all the array entries. The final order and repeat counts are:
1 | int N; float Y; complex Z; | Declare scratch variables. |
1 | float F[23] = {Y = | Declare the main array: 22 entries and a sentinel. |
1 | /* The first array entry is used as scratch space, and so is one of the only entries that will be modified more than once. */ N = 41}; F[ | F[0] is comparatively easy to access via *F, so as the comment says, it houses some temporary values. |
1 | /* This entry, around the middle of the array, is being set to its final value. */ N -= 32] = *F; | This sets F[9]. |
1 | F[3] = *F; /* The value set here will, after the end of the program, be read as the letter E. */ | This sets F[3]. |
1 | typedef | This is the first line that is duplicated later on. |
1 | /* The code up to here has run pretty normally, */ float T; T /* but this will soon no longer be true. */ | These types are just here to soak up some Cs. |
1 | *P = 10 + | OK: here we create P as a pointer into F, so it can move around over time and set various array entries. |
1 | ((isxdigit(*F) * /* The final program should not end up showing nondeterministic behavior. */ | isxdigit returns false, or 0, which cancels out the rand call... |
1 | +rand())[&F]); | ...so this really just puts P at F + 10. |
2 | -8, *P = (abs(*P) << abs(*P)) | The first repeated line. We need to fill this array entry, but running the line once leaves it at 0, and running it more than twice overflows, which is undefined behavior. |
1 | % 69; *P /= fabs((float) | Afterward, we just do some arithmetic for a while. This sets F[10]. |
1 | log(*P)); *(P + 4) = *P + /* The value set here will, after the end of the program, be read as the letter N. */ *F - | However, we do see here the first use of -~/~-, which is very useful to fudge integers by one without using up a numeric literal. This sets F[14]. |
1 | ~(int) asinh(*P); ++P; | |
1 | *P /* The value set here is a float that might not look exact, but will still pretty clearly give the letter S in the end. */ | This sets F[11]. |
1 | = *F * log10((int) trunc( | |
0 | free() volatile sin(); restrict exit() /* Uh-oh. */ | This line is nonsense. |
1 | Y)); N--; Z = ~-(--P - F) + N * | |
1 | /* At this time, 5 out of 22 array entries should be set to their final values. */ *F; | |
1 | Z += N-- - (F - --P); *(F + N) = Z++ / ~-N; N++; (P | From here we start using N as another index into F. This sets F[7]. |
3 | , ++P | Here we increment P three times to skip past the already-set entries to the next blank one. |
1 | )[N] = F[-~N]; Y = F[N--] = *F * 0x1.a895dp0; (exit | Hexadecimal float literals are valid C, but only if they have an exponent (in binary). This sets F[20] and F[8]. |
0 | , F[--N] = ++N + N-- / (++P - P--) + *P - 0x.accp4, exit | This concurrent modification is undefined behavior, however. |
0 | ); F[--N] = *(P - &Y + &N) / *F + *(P + 0xf); exit; | And this is invalid pointer arithmetic. |
1 | , F[--N] = *F + 0x1.b1aep21 / (0xdead + 0xbeef + 0xcafe + 0xf00d)), exit | This is fine. This sets F[6]. |
1 | ; F[--N] = *F = 0x.9b3p5; exit - exit + exit - exit + exit, | This is also fine (exit behaves as a pointer). This sets F[5] (and resets *F). |
1 | Z -= N++; errno[P++] = *(F + -~N); *P = --Z / ~-N; (P++[N] = ( | errno is presumably 0 here, as nothing could have set it. This sets F[12] and F[13]. (Entry 19 is also set, but it will be modified again later.) |
1 | Y)); N--; Z = ~-(--P - F) + N * | Here Z finally becomes a complex number by using the imaginary constant I. |
1 | I; *(F - ~(int) ( | This sets F[17]. |
0 | typedef | Duplicated from before; invalid here. |
1 | (*(F + 16) = *(F + lround(*F - tan( | This sets F[16]. |
1 | 58)))) / *F + 13)) = *P * tan(208); *F = | This sets F[0] (finally). |
2 | *(P += 4); /* At this time, P should point to the last meaningful array entry. */ | Run twice so that the comment is true. |
1 | *P = 10 + | This sets F[21]. |
19 | -81 + (*F + sin(*F - cos(*F - tan(*F - fabs(*F))))); /* This line sets two array entries to their final values, */ if(*P == 0x0) *P = fabs((0x.29c2p0 * *F) * Z) - *F; --P; /* for a total of 18 out of 22. */ Z *= .638424 * | Each repetition of this line sets *P if unset, then decrements P. This sets F[15] and F[4], and also passes the 18th entry but does not give it its final value. We stop when P is in position at F + 2 to fill in the next unset entry. |
2 | -8, *P = (abs(*P) << abs(*P)) | Repeat twice for the same reason as last time. |
1 | -0; *P /= | This sets F[2]. |
2 | -0.795 * *F + sqrt( | Repeat twice to make the parentheses line up. |
1 | lround(*P)) + 3 ** P - !exit * (long) ( | Not exponentiation, just pointer dereference. |
0 | +raise(9) | Do not kill the program. |
1 | +rand())[&F]); | !exit gives 0 to cancel out the rand again. |
0 | long time(); 0xe5cap3 void | This line is nonsense. |
1 | 18[F] = 1[F] = P[~-N] - (N ^ 12); Y | This sets F[18] and F[1]. Y is set to 9 / 5. |
1 | = 9. / N; P -= (int) | |
1 | '}' - '{'; *(P | |
0 | ) = *(F + 'b' - Y * '5') /* Parse the extraction as follows: */ = *F /* ???? ?? ???? ? ?????? ???? ? (N), */ = *F /* using the current value of N at this time. */ = *F = *F + '\0'; P -= '\x4'; | Y is a float and so invalid in pointer arithmetic. The comments are valid, however. At this point, N is 5; see the extraction below. |
1 | + '\x13') = (N = ' ', | This sets F[19], the last entry. N is set to 32. |
1 | *F - *P / 30); | |
2 | 0[&N]; if(&Y) do { *P -= | Here starts the final loop. This line must repeat twice for the braces to line up. What the loop does is subtract N = 32 from each array entry... |
1 | -0; *P /= | ...and then divide it by Y = 9 / 5. |
1 | 0[&Y]; } while | |
1 | (!&N); continue; } | &N is guaranteed to be true, which breaks the inner do-while immediately, but the first iteration still goes off. |
1 | while (*(++P)); | The outer do-while repeats until the sentinel at the end of F. (Using a G to represent a dereference of an increment is, technically, cheating, but it was the only reasonable way to make the loop work...) |
Note: Usually, this might be where you’d expect a rigorous derivation or logical solve path. My confession is that I can’t give you that. When I say that we need an array entry to be set, that only means that we should generally expect the program to do things that have an effect; given the long cluephrase and constrained code, there isn’t much room to perform useless or redundant operations. I haven’t demonstrated that there aren’t other solutions that also leave the array filled but in a different state, and maybe that makes me a bad puzzle constructor.
The final program is available here. When compiled and run, it leaves a sequence of 22 values in F; promisingly, these values are all close to integers, and the first four convincingly spell out LINE in A1Z26. But then there are some negative values — what’s going on? Well, this is a C program, so it isn’t just A1Z26; it's actually ASCII minus 64. Using this we can extract the rest of the message:
0 | 12.001271 | L |
1 | 8.999988 | I |
2 | 14.015537 | N |
3 | 5.000000 | E |
4 | -11.998359 | 4 |
5 | -7.000868 | 9 |
6 | 13.999988 | N |
7 | 15.000000 | O |
8 | 19.999989 | T |
9 | 5.000000 | E |
10 | -10.995931 | 5 |
11 | 18.957855 | S |
12 | 15.000000 | O |
13 | 21.000002 | U |
14 | 14.004070 | N |
15 | 4.004642 | D |
16 | 18.957855 | S |
17 | 12.001271 | L |
18 | 8.999988 | I |
19 | 11.008636 | K |
20 | 5.000000 | E |
21 | -27.956041 | $ |
22 | 0.000000 |
In C fragment 49 note 5 is a B♭, and it corresponds to the keyword do in the first line of the final loop. This is supposed to sound like something with five letters (according to the last comment) that means “$”. If we pronounce “do” as in C, we won’t get anywhere. But if we pronounce it as in “doe, a deer” (fittingly representing C in fixed-note solfege), then it sounds like slang for money: DOUGH.