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:
|
## To use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -15,16 +26,22 @@ make
|
|||||||
### Try:
|
### Try:
|
||||||
|
|
||||||
```sh
|
```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
|
./prog
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Judges' remarks:
|
## Judges' remarks:
|
||||||
|
|
||||||
This program consists of a single print statement wrapped in a while loop. You
|
This program consists of a single `printf(3)` statement wrapped in a `while`
|
||||||
would not think that this would amount to much, but you would be very, very
|
loop. You would not think that this would amount to much, but you would be very,
|
||||||
wrong. A clue to what is happening and how this works is encoded in the
|
very wrong. A clue to what is happening and how this works is encoded in the
|
||||||
ASCII art of the program source.
|
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:
|
The game ends if:
|
||||||
|
|
||||||
* A player completes three in a row; that player wins
|
* A player completes three in a row; that player wins.
|
||||||
* All squares are taken; neither player wins
|
* All squares are taken; neither player wins.
|
||||||
* A player makes an illegal move; their opponent wins
|
* A player makes an illegal move; their opponent wins.
|
||||||
|
|
||||||
|
|
||||||
### LIMITATIONS
|
### LIMITATIONS
|
||||||
|
|
||||||
@@ -69,8 +87,8 @@ the program ... is hard at work processing your move! That is! Please be
|
|||||||
patient.
|
patient.
|
||||||
|
|
||||||
After macro expansion the format string is roughly 100K. This makes some
|
After macro expansion the format string is roughly 100K. This makes some
|
||||||
compilers unhappy, and others slow. (For example, passing GCC the flags -Wall
|
compilers unhappy, and others slow. (For example, passing GCC the flags `-Wall
|
||||||
-Wextra -pedantic will result in a warning about oversized strings.)
|
-Wextra -pedantic` will result in a warning about oversized strings.)
|
||||||
|
|
||||||
Under some terminals the program may exit early. The following should always work:
|
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
|
### 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
|
```c
|
||||||
int main() {
|
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
|
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
|
Integrity" where we introduced this in an actual, published, academic paper. The
|
||||||
things you can get away with sometimes.)
|
things you can get away with sometimes.)
|
||||||
|
|
||||||
We <strike>ab</strike>use this fact to implement the logic of tic-tac-toe entirely within
|
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.
|
Here's (briefly) how it works.
|
||||||
|
|
||||||
|
|
||||||
#### Preliminaries
|
#### 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.
|
- `%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".
|
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
|
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
|
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.
|
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:
|
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.
|
- 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,
|
From here we can compute any binary circuit. Doing something efficient,
|
||||||
though, still takes work.
|
though, still takes work.
|
||||||
|
|
||||||
|
|
||||||
#### Tic-Tac-Toe
|
#### Tic-Tac-Toe
|
||||||
|
|
||||||
The game itself is represented as a board of 18 bits, 9 bits per player, along
|
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.
|
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
|
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
|
pointers to three squares in a row to test, and `D` be where to save if there is a
|
||||||
win or not.
|
win or not.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@@ -217,7 +239,7 @@ the following expression
|
|||||||
"%1$hhn%1$s" (repeated 256 times)
|
"%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
|
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
|
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
|
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
|
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 `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 `3$` the pointer to the board string, we can compute
|
||||||
|
|
||||||
```c
|
```c
|
||||||
"%1$s" (repeated 47 times) "%2$s" (repeated 56 times) %1$32d%3$hhn"
|
"%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
|
*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
|
#### Further Obfuscations
|
||||||
|
|
||||||
In order to be able to finally display the board, while still only using one
|
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
|
```c
|
||||||
"\n\033[2J\n%26$s"
|
"\n\033[2J\n%26$s"
|
||||||
```
|
```
|
||||||
|
|
||||||
which is the escape sequence to clear the screen, and then prints argument 26.
|
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,
|
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
|
but within the `printf(3)` statement we will construct this string to look like a
|
||||||
tic-tac-toe board.
|
tic-tac-toe board.
|
||||||
|
|
||||||
After the board, we need to print one of the following strings:
|
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'`.
|
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
|
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()` to show the first board, and
|
different reason. We first want to run `printf(3)` to show the first board, and
|
||||||
then alternate between runs to `scanf()` and `printf()` reading and then
|
then alternate between runs to `scanf(3)` and `printf(3)` reading and then
|
||||||
displaying moves. Importantly, we *do not* want a final scanf when the game
|
displaying moves. Importantly, we *do not* want a final `scanf(3)` when the game
|
||||||
ends. It should just exit.
|
ends. It should just exit.
|
||||||
|
|
||||||
One option would be to implement the logic as
|
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
|
implement it like this
|
||||||
|
|
||||||
```c
|
```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.)
|
statement, but it has the same effect.)
|
||||||
|
|
||||||
Notice there is now no initial `printf()`. In order make sure the program doesn't
|
Notice there is now no initial `printf(3)`. In order make sure the program doesn't
|
||||||
block before the first `printf()`, but we initialize the `scanf()` format to the null
|
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()`
|
string so that it returns right away without blocking. The first time the
|
||||||
call runs, it writes out `"%hhd"` to create the `scanf()` format string.
|
`printf(3)` call runs, it writes out `"%hhd"` to create the `scanf(3)` format string.
|
||||||
|
|
||||||
|
|
||||||
## Copyright and CC BY-SA 4.0 License:
|
## 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?
|
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
|
## 2020 ferguson1
|
||||||
|
|
||||||
### STATUS: INABIAF - please **DO NOT** fix
|
### 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.
|
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))
|
## <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
|
[Cody](#cody) copied the files from the spoiler.zip file that was password protected with
|
||||||
|
|||||||
Reference in new issue
Block a user