fix Makefile for 2020/carlini and test a README.md style

This commit is contained in:
Landon Curt Noll committed 2023-03-10 21:06:21 -08:00
1 parent 4a93ba2ca6
commit 34e612b95a
2 files changed
+223 -440

No files matched your search

+104 -61
View File
@@ -1,47 +1,56 @@
# Best of show - abuse of libc
```
Nicholas Carlini <nicholas@carlini.com>
<https://nicholas.carlini.com>
```
## To build:
The code for this entry can be found in prog.c
```sh
make
```
## Judges' comments:
### To use:
make
./prog
### To run:
```sh
./prog
```
### Try:
# Play with a friend, P1 and P2, squares are numbered 1..9
./prog
```sh
# Play with a friend, P1 and P2, squares are numbered 1..9
./prog
```
### Selected Judges Remarks:
## Judges' comments:
This program consists of a single print statement wrapped in a while loop. You
would not think that this would amount to much, but you would be very, very
wrong. A clue to what is happening and how this works is encoded in the
ASCII art of the program source.
## Author's comments:
HOW ABOUT A NICE GAME OF <strike>CHESS</strike>TIC-TAC-TOE?
### USAGE
cc -o prog prog.c
./prog
```sh
cc -o prog prog.c
./prog
```
Alternates between P1 and P2. Enter a digit [1-9] to move:
1 | 2 | 3
---------
4 | 5 | 6
---------
7 | 8 | 9
```
1 | 2 | 3
---------
4 | 5 | 6
---------
7 | 8 | 9
```
The game ends if:
@@ -49,7 +58,6 @@ The game ends if:
* All squares are taken; neither player wins
* A player makes an illegal move; their opponent wins
### LIMITATIONS
Bad things may happen if the entered move is outside of the range [1..9]
@@ -69,16 +77,19 @@ compilers unhappy, and others slow. (For example, passing GCC the flags -Wall
Under some terminals the program may exit early. The following should always work:
echo "1 2 3 4 5 6 7" | ./prog
```sh
echo "1 2 3 4 5 6 7" | ./prog
```
### OBFUSCATION
The entirety of the program consists of a single iterated call to printf.
int main() {
while(*d) printf(fmt, arg);
}
```c
int main() {
while(*d) printf(fmt, arg);
}
```
Here, `fmt` is a single string, and `arg` is a series of arguments to printf.
@@ -102,7 +113,6 @@ This program uses three printf format specifiers.
- `%n` takes a pointer and writes (!!) the number of bytes printed so far.
Okay, everyone probably knows this. Let's get a bit more advanced.
Format specifiers can take extra "arguments".
@@ -115,14 +125,17 @@ Format specifiers can take extra "arguments".
- `"%3$.*4$d"`: print argument 3 to printf with as many zeros as in argument 4.
For example, the following expression
printf("%1$.*2$d%3$hhn", 5, 10, &x)
```c
printf("%1$.*2$d%3$hhn", 5, 10, &x)
```
will have the same effect as if we had written
x = 10;
```c
x = 10;
```
because it will print out `0000000005` (5 padded to size 10) and then write the
number of bytes written to x.
@@ -143,27 +156,39 @@ We can use format strings to compute the OR/NOT of arbitrary "bits".
We'll start with the simplest, OR:
printf("%1$s%2$s%3$hhn", a, b, c)
```c
printf("%1$s%2$s%3$hhn", a, b, c)
```
will compute
*c = strlen(a) + strlen(b)
```c
*c = strlen(a) + strlen(b)
```
but given that `strlen(x)` is `1` for a 1-bit and `0` for a 0-bit, we have
*c = a | b
```c
*c = a | b
```
Computing the NOT of a single value is also easy:
printf("%1$255d%1$s%hhn", a, b)
```c
printf("%1$255d%1$s%hhn", a, b)
```
will compute
*b = (strlen(a)+255)%256 = strlen(a)-1
```c
*b = (strlen(a)+255)%256 = strlen(a)-1
```
and again, because `strlen(x)` is either `1` or `0` we have
*c = !b
```c
*c = !b
```
From here we can compute any binary circuit. Doing something efficient,
though, still takes work.
@@ -177,11 +202,13 @@ To detect who has won, we implement the following logic. Let A, B, and C be
pointers to three squares in a row to test, and D be where to save if there is a
win or not.
"%A$s%B$s%C$s%1$253d%11$hhn" // r11 = !(*A & *B & *C)
ZERO
"%11$s%1$255d%11hhn" // r11 = !r11
ZERO
"%11$s%D$s%D$hhn" // *D = *D | r11
```c
"%A$s%B$s%C$s%1$253d%11$hhn" // r11 = !(*A & *B & *C)
ZERO
"%11$s%1$255d%11hhn" // r11 = !r11
ZERO
"%11$s%D$s%D$hhn" // *D = *D | r11
```
That is, we set `*D` to `1` if there is a three-in-a-row. We repeat this for all
possible three-in-a-row configurations, for both players.
@@ -189,7 +216,9 @@ possible three-in-a-row configurations, for both players.
The ZERO macro ensures that the number of bytes written out is 0 mod 256 with
the following expression
"%1$hhn%1$s" (repeated 256 times)
```c
"%1$hhn%1$s" (repeated 256 times)
```
where argument 1 is a pointer to a temporary variable followed by a null byte.
@@ -206,11 +235,15 @@ bits to Xs and Os to print out. This is actually rather straightforward. Given
in 1$ the pointer to player 1's square, and 2$ the pointer to player 2's, and
in 3$ the pointer to the board string, we can compute
"%1$s" (repeated 47 times) "%2$s" (repeated 56 times) %1$32d%3$hhn"
```c
"%1$s" (repeated 47 times) "%2$s" (repeated 56 times) %1$32d%3$hhn"
```
which will, in effect, compute
*r3 = (*r1) * 47 + (*r2) * 56 + 32
```c
*r3 = (*r1) * 47 + (*r2) * 56 + 32
```
which will output `' '` if neither are true, `'X'` if r1 is, or `'O'` if r2 is.
@@ -220,7 +253,9 @@ which will output `' '` if neither are true, `'X'` if r1 is, or `'O'` if r2 is.
In order to be able to finally display the board, while still only using one
printf statement, we finish the statement with
"\n\033[2J\n%26$s"
```c
"\n\033[2J\n%26$s"
```
which is the escape sequence to clear the screen, and then prints argument 26.
Argument 26 is a pointer to a char* in memory, that initially is undefined,
@@ -229,12 +264,14 @@ tic-tac-toe board.
After the board, we need to print one of the following strings:
P1>_
P2>_
P1 WINS
P2 WINS
P1 TIES
P2 TIES
```
P1>_
P2>_
P1 WINS
P2 WINS
P1 TIES
P2 TIES
```
Depending on if it's P1 or P2's turn to move, the game is over and someone
won, or the game is over and it is a draw.
@@ -242,7 +279,9 @@ won, or the game is over and it is a draw.
This turns out not to be as hard as it might look. Using the same trick as
before, we set byte four to be
*byte4 = is_win * 'W' + is_tie * 'T'
```c
*byte4 = is_win * 'W' + is_tie * 'T'
```
The byte `'I'` and `'S'` can always be the same, and we do the same for `'E'`/`'N'`.
@@ -254,19 +293,23 @@ ends. It should just exit.
One option would be to implement the logic as
printf()
while (*ok) {
scanf();
printf();
}
```c
printf()
while (*ok) {
scanf();
printf();
}
```
but this would DOUBLE the number of calls to printf we require. So instead we
implement it like this
while (*ok) {
scanf();
printf();
}
```c
while (*ok) {
scanf();
printf();
}
```
(In reality we actually pass `scanf()` as an argument to avoid the extra
statement, but it has the same effect.)
@@ -276,10 +319,10 @@ block before the first `printf()`, but we initialize the `scanf()` format to the
string so that it returns right away without blocking. The first time the `printf()`
call runs, it writes out `"%hhd"` to create the `scanf()` format string.
-----------------------------------------------------------------------------------------------------
## Copyright:
(c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
[judges]: http://www.ioccc.org/judges.html
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
-----------------------------------------------------------------------------------------------------