Merge pull request #1763 from xexyl/carlini2020
Add 2020/carlini/try.sh, update bugs.md
This commit is contained in:
4 files changed
+116
-43
No files matched your search
+66
-43
@@ -5,6 +5,17 @@ make
|
||||
```
|
||||
|
||||
|
||||
### Bugs and (Mis)features:
|
||||
|
||||
The current status of this entry is:
|
||||
|
||||
```
|
||||
STATUS: INABIAF - please **DO NOT** fix
|
||||
```
|
||||
|
||||
For more detailed information see [2020 carlini in bugs.md](/bugs.md#2020-carlini).
|
||||
|
||||
|
||||
## To use:
|
||||
|
||||
```sh
|
||||
@@ -15,16 +26,22 @@ make
|
||||
### Try:
|
||||
|
||||
```sh
|
||||
# Play with a friend, P1 and P2, squares are numbered 1..9
|
||||
./try.sh
|
||||
```
|
||||
|
||||
Or if you actually want to play:
|
||||
|
||||
```sh
|
||||
# Play with a friend, real or imagined, P1 and P2, squares are numbered 1..9
|
||||
./prog
|
||||
```
|
||||
|
||||
|
||||
## Judges' remarks:
|
||||
|
||||
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
|
||||
This program consists of a single `printf(3)` 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.
|
||||
|
||||
|
||||
@@ -51,9 +68,10 @@ Alternates between P1 and P2. Enter a digit [1-9] to move:
|
||||
|
||||
The game ends if:
|
||||
|
||||
* A player completes three in a row; that player wins
|
||||
* All squares are taken; neither player wins
|
||||
* A player makes an illegal move; their opponent wins
|
||||
* A player completes three in a row; that player wins.
|
||||
* All squares are taken; neither player wins.
|
||||
* A player makes an illegal move; their opponent wins.
|
||||
|
||||
|
||||
### LIMITATIONS
|
||||
|
||||
@@ -69,8 +87,8 @@ the program ... is hard at work processing your move! That is! Please be
|
||||
patient.
|
||||
|
||||
After macro expansion the format string is roughly 100K. This makes some
|
||||
compilers unhappy, and others slow. (For example, passing GCC the flags -Wall
|
||||
-Wextra -pedantic will result in a warning about oversized strings.)
|
||||
compilers unhappy, and others slow. (For example, passing GCC the flags `-Wall
|
||||
-Wextra -pedantic` will result in a warning about oversized strings.)
|
||||
|
||||
Under some terminals the program may exit early. The following should always work:
|
||||
|
||||
@@ -80,7 +98,7 @@ echo "1 2 3 4 5 6 7" | ./prog
|
||||
|
||||
### OBFUSCATION
|
||||
|
||||
The entirety of the program consists of a single iterated call to printf.
|
||||
The entirety of the program consists of a single iterated call to `printf(3)`.
|
||||
|
||||
```c
|
||||
int main() {
|
||||
@@ -88,25 +106,27 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
Here, `fmt` is a single string, and `arg` is a series of arguments to printf.
|
||||
Here, `fmt` is a single string, and `arg` is a series of arguments to
|
||||
`printf(3)`.
|
||||
|
||||
While its primary purpose is to serve as The One True Debugger, printf also happens
|
||||
While its primary purpose is to serve as The One True Debugger, `printf(3)` also happens
|
||||
to be Turing complete. (See "Control-Flow Bending: On the Effectiveness of Control-Flow
|
||||
Integrity" where we introduced this in an actual, published, academic paper. The
|
||||
things you can get away with sometimes.)
|
||||
|
||||
We <strike>ab</strike>use this fact to implement the logic of tic-tac-toe entirely within
|
||||
this one printf call (and a call to scanf() to read user input).
|
||||
this one `printf(3)` call (and a call to `scanf(3)` to read user input).
|
||||
|
||||
Here's (briefly) how it works.
|
||||
|
||||
|
||||
#### Preliminaries
|
||||
|
||||
This program uses three printf format specifiers.
|
||||
This program uses three `printf(3)` format specifiers.
|
||||
|
||||
- `%d` takes an integer argument and prints it
|
||||
- `%d` takes an integer argument and prints it.
|
||||
|
||||
- `%s` takes a string argument and prints it
|
||||
- `%s` takes a string argument and prints it.
|
||||
|
||||
- `%n` takes a pointer and writes (!!) the number of bytes printed so far.
|
||||
|
||||
@@ -114,13 +134,13 @@ Okay, everyone probably knows this. Let's get a bit more advanced.
|
||||
|
||||
Format specifiers can take extra "arguments".
|
||||
|
||||
- `"%hhn"`: store the number of bytes written mod 256 to the char pointer
|
||||
- `"%hhn"`: store the number of bytes written mod 256 to the `char` pointer.
|
||||
|
||||
- `"%2$d"`: print argument 2 to printf (and not the sequentially next argument)
|
||||
- `"%2$d"`: print argument 2 to `printf(3)` (and not the sequentially next argument).
|
||||
|
||||
- `"%8d"`: pad the printed integer to 8 characters
|
||||
- `"%8d"`: pad the printed integer to 8 characters.
|
||||
|
||||
- `"%3$.*4$d"`: print argument 3 to printf with as many zeros as in argument 4.
|
||||
- `"%3$.*4$d"`: print argument 3 to `printf(3)` with as many zeros as in argument 4.
|
||||
|
||||
For example, the following expression
|
||||
|
||||
@@ -135,16 +155,17 @@ x = 10;
|
||||
```
|
||||
|
||||
because it will print out `0000000005` (5 padded to size 10) and then write the
|
||||
number of bytes written to x.
|
||||
number of bytes written to `x`.
|
||||
|
||||
#### Printf Oriented Programming
|
||||
|
||||
#### `printf(3)` Oriented Programming
|
||||
|
||||
Alright, now we can get to the real fun.
|
||||
|
||||
We perform arbitrary computation with printf treating memory as a binary
|
||||
We perform arbitrary computation with `printf(3)` treating memory as a binary
|
||||
array--one bit per pair of bytes:
|
||||
|
||||
- The zero bit is represented by the sequence `00 00`
|
||||
- The zero bit is represented by the sequence `00 00`.
|
||||
|
||||
- The one-bit is represented by the sequence `xx 00` where `xx` is any non-zero byte.
|
||||
|
||||
@@ -190,13 +211,14 @@ and again, because `strlen(x)` is either `1` or `0` we have
|
||||
From here we can compute any binary circuit. Doing something efficient,
|
||||
though, still takes work.
|
||||
|
||||
|
||||
#### Tic-Tac-Toe
|
||||
|
||||
The game itself is represented as a board of 18 bits, 9 bits per player, along
|
||||
with a turn counter that alternates between player 1 and player 2.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
```c
|
||||
@@ -217,7 +239,7 @@ the following expression
|
||||
"%1$hhn%1$s" (repeated 256 times)
|
||||
```
|
||||
|
||||
where argument 1 is a pointer to a temporary variable followed by a null byte.
|
||||
where argument 1 is a pointer to a temporary variable followed by a NUL byte.
|
||||
|
||||
This works because if the current count is 0 mod 256, then `"%1$hhn"` will write
|
||||
zero to argument 1 and then "%1$s" will never emit any text. If, on the other
|
||||
@@ -229,8 +251,8 @@ Checking if there has been an invalid move is achieved similarly.
|
||||
|
||||
In order to decide what to print out, we have to cast the "in-memory" array of
|
||||
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
|
||||
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
|
||||
|
||||
```c
|
||||
"%1$s" (repeated 47 times) "%2$s" (repeated 56 times) %1$32d%3$hhn"
|
||||
@@ -242,21 +264,22 @@ which will, in effect, compute
|
||||
*r3 = (*r1) * 47 + (*r2) * 56 + 32
|
||||
```
|
||||
|
||||
which will output `' '` if neither are true, `'X'` if r1 is, or `'O'` if r2 is.
|
||||
which will output `' '` if neither are true, `'X'` if `r1` is true, or `'O'` if
|
||||
`r2` is true.
|
||||
|
||||
|
||||
#### Further Obfuscations
|
||||
|
||||
In order to be able to finally display the board, while still only using one
|
||||
printf statement, we finish the statement with
|
||||
`printf(3)` statement, we finish the statement with
|
||||
|
||||
```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,
|
||||
but within the printf statement we will construct this string to look like a
|
||||
Argument 26 is a pointer to a `char*` in memory, that initially is undefined,
|
||||
but within the `printf(3)` statement we will construct this string to look like a
|
||||
tic-tac-toe board.
|
||||
|
||||
After the board, we need to print one of the following strings:
|
||||
@@ -282,10 +305,10 @@ before, we set byte four to be
|
||||
|
||||
The byte `'I'` and `'S'` can always be the same, and we do the same for `'E'`/`'N'`.
|
||||
|
||||
We do this same on-the-fly creation of the `scanf()` format string, but for a
|
||||
different reason. We first want to run `printf()` to show the first board, and
|
||||
then alternate between runs to `scanf()` and `printf()` reading and then
|
||||
displaying moves. Importantly, we *do not* want a final scanf when the game
|
||||
We do this same on-the-fly creation of the `scanf(3)` format string, but for a
|
||||
different reason. We first want to run `printf(3)` to show the first board, and
|
||||
then alternate between runs to `scanf(3)` and `printf(3)` reading and then
|
||||
displaying moves. Importantly, we *do not* want a final `scanf(3)` when the game
|
||||
ends. It should just exit.
|
||||
|
||||
One option would be to implement the logic as
|
||||
@@ -298,7 +321,7 @@ while (*ok) {
|
||||
}
|
||||
```
|
||||
|
||||
but this would DOUBLE the number of calls to printf we require. So instead we
|
||||
but this would DOUBLE the number of calls to `printf(3)` we require. So instead we
|
||||
implement it like this
|
||||
|
||||
```c
|
||||
@@ -308,13 +331,13 @@ while (*ok) {
|
||||
}
|
||||
```
|
||||
|
||||
(In reality we actually pass `scanf()` as an argument to avoid the extra
|
||||
(In reality we actually pass `scanf(3)` as an argument to avoid the extra
|
||||
statement, but it has the same effect.)
|
||||
|
||||
Notice there is now no initial `printf()`. In order make sure the program doesn't
|
||||
block before the first `printf()`, but we initialize the `scanf()` format to the null
|
||||
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.
|
||||
Notice there is now no initial `printf(3)`. In order make sure the program doesn't
|
||||
block before the first `printf(3)`, we initialize the `scanf(3)` format to the NUL
|
||||
string so that it returns right away without blocking. The first time the
|
||||
`printf(3)` call runs, it writes out `"%hhd"` to create the `scanf(3)` format string.
|
||||
|
||||
|
||||
## Copyright and CC BY-SA 4.0 License:
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# try.sh - demonstrate IOCCC winner 2020/carlini
|
||||
#
|
||||
|
||||
# make sure CC is set so that when we do make CC="$CC" it isn't empty. Doing it
|
||||
# this way allows us to have the user specify a different compiler in an easy
|
||||
# way.
|
||||
if [[ -z "$CC" ]]; then
|
||||
CC="cc"
|
||||
fi
|
||||
|
||||
make CC="$CC" all >/dev/null || exit 1
|
||||
|
||||
# clear screen after compilation so that only the entry is shown
|
||||
clear
|
||||
|
||||
read -r -n 1 -p "Press any key to run: echo 1 2 3 4 5 6 7 | ./prog: "
|
||||
echo 1>&2
|
||||
echo 1 2 3 4 5 6 7 | ./prog
|
||||
echo 1>&2
|
||||
|
||||
echo "... now go and play for real, with a friend, real or imagined." 1>&2
|
||||
@@ -3575,6 +3575,25 @@ It will also show funny output with more than one arg. This should not be fixed
|
||||
either. But can you figure out why this happens?
|
||||
|
||||
|
||||
## 2020 carlini
|
||||
|
||||
### STATUS: INABIAF - please **DO NOT** fix
|
||||
### Source code: [2020/carlini/prog.c](2020/carlini/prog.c)
|
||||
### Information: [2020/carlini/README.md](2020/carlini/README.md)
|
||||
|
||||
The author stated that bad things happen if the entered move is outside of the
|
||||
range [1..9]. We observed a crash but we also observed a tie and other things
|
||||
besides (say with the input `-1`, `-`, `-10` and others).
|
||||
|
||||
If a player makes a move over their opponent's place they will automatically
|
||||
lose.
|
||||
|
||||
If a player tries to play a move they already played they will forfeit their
|
||||
move.
|
||||
|
||||
If you have audible bells enabled the program will beep at every turn.
|
||||
|
||||
|
||||
## 2020 ferguson1
|
||||
|
||||
### STATUS: INABIAF - please **DO NOT** fix
|
||||
|
||||
@@ -4291,6 +4291,14 @@ showing nothing at all.
|
||||
Cody also added the [try.sh](/2020/burton/try.sh) script.
|
||||
|
||||
|
||||
## <a name="2020_carlini"></a>[2020/carlini](/2020/carlini/prog.c) ([README.md](/2020/carlini/README.md))
|
||||
|
||||
[Cody](#cody) added the [try.sh](/2020/carlini/try.sh) script which although at
|
||||
first glance might not appear to have a point, it actually does, namely showing
|
||||
how you can automate play and then reminding you to actually play for real, with
|
||||
a friend, whether that's real or imagined.
|
||||
|
||||
|
||||
## <a name="2020_endoh2"></a>[2020/endoh2](/2020/endoh2/prog.c) ([README.md](/2020/endoh2/README.md))
|
||||
|
||||
[Cody](#cody) copied the files from the spoiler.zip file that was password protected with
|
||||
|
||||
Reference in new issue
Block a user