Add 2020/carlini/try.sh, update bugs.md

Why a try.sh for this? It shows that you can also automate the play (it
happens to come from the author's remarks, however, so there's that
too). Then it tells you to be serious by playing with a friend, either
real or imagined, rather than doing something so silly.

Update bugs.md with a INABIAF section.

The README.md file has been format/typo checked.
This commit is contained in:
Cody Boone Ferguson committed 2023-12-20 13:42:13 -08:00
1 parent 67b09c403a
commit 3bf3ac7024
4 files changed
+116 -43

No files matched your search

+66 -43
View File
@@ -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:
+23
View File
@@ -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
+19
View File
@@ -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
+8
View File
@@ -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