{"id":13437757,"url":"https://github.com/carlini/printf-tac-toe","last_synced_at":"2025-04-08T08:14:53.782Z","repository":{"id":38250971,"uuid":"263842117","full_name":"carlini/printf-tac-toe","owner":"carlini","description":"tic-tac-toe in a single call to printf","archived":false,"fork":false,"pushed_at":"2022-06-08T08:33:53.000Z","size":20,"stargazers_count":2316,"open_issues_count":7,"forks_count":56,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-04-01T05:35:01.308Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/carlini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-14T07:10:59.000Z","updated_at":"2025-03-31T23:36:37.000Z","dependencies_parsed_at":"2022-07-12T17:14:24.490Z","dependency_job_id":null,"html_url":"https://github.com/carlini/printf-tac-toe","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlini%2Fprintf-tac-toe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlini%2Fprintf-tac-toe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlini%2Fprintf-tac-toe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlini%2Fprintf-tac-toe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/carlini","download_url":"https://codeload.github.com/carlini/printf-tac-toe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247801171,"owners_count":20998339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:00.025Z","updated_at":"2025-04-08T08:14:53.765Z","avatar_url":"https://github.com/carlini.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"# printf-tac-toe\n\nA c implementation of tic-tac-toe in a single call to printf. Written for IOCCC 2020.\n\n```c\n#include \u003cstdio.h\u003e \n\n#define N(a)       \"%\"#a\"$hhn\"\n#define O(a,b)     \"%10$\"#a\"d\"N(b)\n#define U          \"%10$.*37$d\"\n#define G(a)       \"%\"#a\"$s\"\n#define H(a,b)     G(a)G(b)\n#define T(a)       a a \n#define s(a)       T(a)T(a)\n#define A(a)       s(a)T(a)a\n#define n(a)       A(a)a\n#define D(a)       n(a)A(a)\n#define C(a)       D(a)a\n#define R          C(C(N(12)G(12)))\n#define o(a,b,c)   C(H(a,a))D(G(a))C(H(b,b)G(b))n(G(b))O(32,c)R\n#define SS         O(78,55)R \"\\n\\033[2J\\n%26$s\";\n#define E(a,b,c,d) H(a,b)G(c)O(253,11)R G(11)O(255,11)R H(11,d)N(d)O(253,35)R\n#define S(a,b)     O(254,11)H(a,b)N(68)R G(68)O(255,68)N(12)H(12,68)G(67)N(67)\n\nchar* fmt = O(10,39)N(40)N(41)N(42)N(43)N(66)N(69)N(24)O(22,65)O(5,70)O(8,44)N(\n            45)N(46)N    (47)N(48)N(    49)N( 50)N(     51)N(52)N(53    )O( 28,\n            54)O(5,        55) O(2,    56)O(3,57)O(      4,58 )O(13,    73)O(4,\n            71 )N(   72)O   (20,59    )N(60)N(61)N(       62)N (63)N    (64)R R\n            E(1,2,   3,13   )E(4,    5,6,13)E(7,8,9        ,13)E(1,4    ,7,13)E\n            (2,5,8,        13)E(    3,6,9,13)E(1,5,         9,13)E(3    ,5,7,13\n            )E(14,15,    16,23)    E(17,18,19,23)E(          20, 21,    22,23)E\n            (14,17,20,23)E(15,    18,21,23)E(16,19,    22     ,23)E(    14, 18,\n            22,23)E(16,18,20,    23)R U O(255 ,38)R    G (     38)O(    255,36)\n            R H(13,23)O(255,    11)R H(11,36) O(254    ,36)     R G(    36 ) O(\n            255,36)R S(1,14    )S(2,15)S(3, 16)S(4,    17 )S     (5,    18)S(6,\n            19)S(7,20)S(8,    21)S(9    ,22)H(13,23    )H(36,     67    )N(11)R\n            G(11)\"\"O(255,    25 )R        s(C(G(11)    ))n (G(          11) )G(\n            11)N(54)R C(    \"aa\")   s(A(   G(25)))T    (G(25))N         (69)R o\n            (14,1,26)o(    15, 2,   27)o   (16,3,28    )o( 17,4,        29)o(18\n            ,5,30)o(19    ,6,31)o(        20,7,32)o    (21,8,33)o       (22 ,9,\n            34)n(C(U)    )N( 68)R H(    36,13)G(23)    N(11)R C(D(      G(11)))\n            D(G(11))G(68)N(68)R G(68)O(49,35)R H(13,23)G(67)N(11)R C(H(11,11)G(\n            11))A(G(11))C(H(36,36)G(36))s(G(36))O(32,58)R C(D(G(36)))A(G(36))SS\n\n#define arg d+6,d+8,d+10,d+12,d+14,d+16,d+18,d+20,d+22,0,d+46,d+52,d+48,d+24,d\\\n            +26,d+28,d+30,d+32,d+34,d+36,d+38,d+40,d+50,(scanf(d+126,d+4),d+(6\\\n            -2)+18*(1-d[2]%2)+d[4]*2),d,d+66,d+68,d+70, d+78,d+80,d+82,d+90,d+\\\n            92,d+94,d+97,d+54,d[2],d+2,d+71,d+77,d+83,d+89,d+95,d+72,d+73,d+74\\\n            ,d+75,d+76,d+84,d+85,d+86,d+87,d+88,d+100,d+101,d+96,d+102,d+99,d+\\\n            67,d+69,d+79,d+81,d+91,d+93,d+98,d+103,d+58,d+60,d+98,d+126,d+127,\\\n            d+128,d+129\n\nchar d[538] = {1,0,10,0,10};\n\nint main() {\n    while(*d) printf(fmt, arg);\n}\n```\n\nIf this is the kind of thing that you enjoy, you might also like [printbf](https://github.com/HexHive/printbf).\n\n\n## USAGE\n\n    gcc -o prog prog.c\n    ./prog\n\nAlternates between P1 and P2. Enter a digit [1-9] to move:\n\n    1 | 2 | 3\n    ---------\n    4 | 5 | 6\n    ---------\n    7 | 8 | 9\n\nThe game ends if:\n- A player completes three in a row; that player wins\n- All squares are taken; neither player wins\n- A player makes an illegal move; their opponent wins\n\n\n## WHY?\n\n[IOCCC](http://ioccc.org)\n\n\n## OBFUSCATION\n\nThe entirety of the program consists of a single call to printf.\n\n    int main() {\n        while(*d) printf(fmt, arg);\n    }\n\nHere, `fmt` is a single string, and `arg` is a series of arguments to printf.\n\nWhile its primary purpose is to serve as The One True Debugger, printf also happens\nto be Turing complete. (See \"Control-Flow Bending: On the Effectiveness of Control-Flow\nIntegrity\" where we introduced this in an actual, published, academic paper. The\nthings you can get away with sometimes.)\n\nWe ab^H^Huse this fact to implement a the logic of tic-tac-toe entirely within\nthis one printf call (and a call to scanf() to read user input).\n\nHere's (briefly) how it works.\n\n### Preliminaries\n\nThis program uses three printf format specifiers.\n- `%d` takes an integer argument and prints it\n- `%s` takes a string argument and prints it\n- `%n` takes a pointer and writes (!!) the number of bytes printed so far.\n\nOkay, everyone probably knows this. Let's get a bit more advanced.\n\nFormat specifiers can take extra \"arguments\".\n- `\"%hhn\"`: store the number of bytes written mod 256 to the char pointer\n- `\"%2$d\"`: print argument 2 to printf (and not the sequentially next argument)\n- `\"%8d\"`: pad the printed integer to 8 characters\n- `\"%3$.*4$d\"`: print argument 3 to printf with as many zeros as in argument 4.\n\nFor example, the following expression\n\n    printf(\"%1$.*2$d%3$hhn\", 5, 10, \u0026x)\n\nwill have the same effect as if we had written\n\n    x = 10;\n\nbecause it will print out `0000000005` (5 padded to size 10) and then write the\nnumber of bytes written to x.\n\n### Printf Oriented Programming\n\nAlright, now we can get to the real fun.\n\nWe perform arbitrary computation with printf treating memory as a binary\narray---one bit per pair of bytes:\n- The zero bit is represented by the sequence `00 00`\n- The one-bit is represented by the sequence `xx 00` where `xx` is any non-zero byte.\n\nWe can use format strings to compute the OR/NOT of arbitrary \"bits\".\n\nWe'll start with the simplest, OR:\n\n    printf(\"%1$s%2$s%3$hhn\", a, b, c)\n\nwill compute\n\n    *c = strlen(a) + strlen(b)\n\nbut given that strlen(x) is 1 for a 1-bit and 0 for a 0-bit, we have\n\n    *c = a | b\n\nComputing the NOT of a single value is also easy:\n\n    printf(\"%1$255d%1$s%hhn\", a, b)\n\nwill compute\n\n    *b = (strlen(a)+255)%256 = strlen(a)-1\n\nand again, because `strlen(x)` is either `1` or `0` we have\n\n    *c = !b\n\nFrom here we can compute any binary circuit. Doing something efficient,\nthough, still takes work.\n\n### Tic-Tac-Toe\n\nThe game itself is represented as a board of 18 bits, 9 bits per player, along\nwith a turn counter that alternates between player 1 and player 2.\n\nTo detect who has won, we implement the following logic. Let A, B, and C be\npointers to three squares in a row to test, and D be where to save if there is a\nwin or not.\n\n    \"%A$s%B$s%C$s%1$253d%11$hhn\" // r11 = !(*A \u0026 *B \u0026 *C)\n    ZERO\n    \"%11$s%1$255d%11hhn\" // r11 = !r11\n    ZERO\n    \"%11$s%D$s%D$hhn\" // *D = *D | r11\n\nThat is, we set `*D` to `1` if there is a three-in-a-row. We repeat this for all\npossible three-in-a-row configurations, for both players.\n\nThe ZERO macro ensures that the number of bytes written out is 0 mod 256 with\nthe following expression\n\n    \"%1$hhn%1$s\" (repeated 256 times)\n\nwhere argument 1 is a pointer to a temporary variable followed by a null byte.\n\nThis works because if the current count is 0 mod 256, then \"%1$hhn\" will write\nzero to argument 1 and then \"%1$s\" will never emit any text. If, on the other\nhand, the count is not 0 mod 256, a length-1 string will be written to argument\n1, and then \"%1$s\" will increment the count by one. By repeating this 256\ntimes we're eventually going to reach 0 mod 256.\n\nChecking if there has been an invalid move is achieved similarly.\n\nIn order to decide what to print out, we have to cast the \"in-memory\" array of\nbits to Xs and Os to print out. This is actually rather straightforward. Given\nin 1$ the pointer to player 1's square, and 2$ the pointer to player 2's, and\nin 3$ the pointer to the board string, we can compute\n\n    \"%1$s\" (repeated 47 times) \"%2$s\" (repeated 56 times) %1$32d%3$hhn\"\n\nwhich will, in effect, compute\n\n    *r3 = (*r1) * 47 + (*r2) * 56 + 32\n\nwhich will output ' ' if neither are true, 'X' if r1 is, or 'O' if r2 is.\n\n\n### Further Obfuscations\n\nIn order to be able to finally display the board, while still only using one\nprintf statement, we finish the statement with\n\n    \"\\n\\033[2J\\n%26$s\"\n\nwhich is the escape sequence to clear the screen, and then prints argument 26.\nArgument 26 is a pointer to a char* in memory, that initially is undefined,\nbut within the printf statement we will construct this string to look like a\ntic-tac-toe board.\n\nAfter the board, we need to print one of the following strings:\n\n    P1\u003e_\n    P2\u003e_\n    P1 WINS\n    P2 WINS\n    P1 TIES\n    P2 TIES\n\nDepending on if it's P1 or P2's turn to move, the game is over and someone\nwon, or the game is over and it is a draw.\n\nThis turns out not to be as hard as it might look. Using the same trick as\nbefore, we set byte for to be\n\n    *byte4 = is_win * 'W' + is_tie * 'T'\n\nThe byte `'I'` and `'S'` can always be the same, and we do the same for `'E'`/`'N'`.\n\nWe do this same on-the-fly creation of the `scanf()` format string, but for a\ndifferent reason. We first want to run `printf()` to show the first board, and\nthen alternate between runs to `scanf()` and `printf()` reading and then displaying\nmoves. importantly, we *do not* want a final scanf when the game ends. It should\njust exit.\n\nOne option would be to implement the logic as\n\n    printf()\n    while (*ok) {\n        scanf();\n        printf();\n    }\n\nbut this would DOUBLE the number of calls to printf we require. So instead we\nimplement it like this\n\n    while (*ok) {\n        scanf();\n        printf();\n    }\n\n(In reality we actually pass `scanf()` as an argument to avoid the extra\nstatement, but it has the same effect.)\n\nNotice there is now no initial `printf()`. In order make sure the program doesn't\nblock before the first `printf()`, but we initialize the `scanf()` format to the null\nstring so that it returns right away without blocking. The first time the `printf()`\ncall runs, it writes out `\"%hhd\"` to create the create the `scanf()` format string.\n\n\n## LICENSE\n\nThis program is clearly some groundbreaking achievement the likes of which have\nnever been seen before. Therefore, if you would like to use this program in\nanything, it's licensed under the GPL v3.\n\nThis program is free software: you can redistribute it and/or modify  \nit under the terms of the GNU General Public License as published by  \nthe Free Software Foundation, version 3.\n\nThis program is distributed in the hope that it will be useful, but \nWITHOUT ANY WARRANTY; without even the implied warranty of \nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU \nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License \nalong with this program. If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlini%2Fprintf-tac-toe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarlini%2Fprintf-tac-toe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlini%2Fprintf-tac-toe/lists"}