diff --git a/2020/carlini/README.md b/2020/carlini/README.md index 8963a32da..94bf37cdd 100644 --- a/2020/carlini/README.md +++ b/2020/carlini/README.md @@ -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 abuse 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: diff --git a/2020/carlini/try.sh b/2020/carlini/try.sh new file mode 100755 index 000000000..3fd8b4ede --- /dev/null +++ b/2020/carlini/try.sh @@ -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 diff --git a/bugs.md b/bugs.md index 4f2cc1a8b..b30bf7859 100644 --- a/bugs.md +++ b/bugs.md @@ -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 diff --git a/thanks-for-help.md b/thanks-for-help.md index 991243a56..4f878f44c 100644 --- a/thanks-for-help.md +++ b/thanks-for-help.md @@ -4291,6 +4291,14 @@ showing nothing at all. Cody also added the [try.sh](/2020/burton/try.sh) script. +## [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. + + ## [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