fix Makefile for 2020/carlini and test a README.md style
This commit is contained in:
1 parent
4a93ba2ca6
commit
34e612b95a
2 files changed
+223
-440
No files matched your search
+104
-61
@@ -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/
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
Reference in new issue
Block a user