diff --git a/1984/anonymous/.entry.json b/1984/anonymous/.entry.json index cc1ed2fce..206c65275 100644 --- a/1984/anonymous/.entry.json +++ b/1984/anonymous/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Anonymous_1984" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "anonymous.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1984/anonymous/index.html b/1984/anonymous/index.html index 0abf709d8..1df0dec6e 100644 --- a/1984/anonymous/index.html +++ b/1984/anonymous/index.html @@ -199,9 +199,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
Download IOCCC 1984 entry source
Jump to: top
diff --git a/1984/laman/.entry.json b/1984/laman/.entry.json index df80f1f79..9cb56e81e 100644 --- a/1984/laman/.entry.json +++ b/1984/laman/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Mike_Laman" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "laman.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1984/laman/index.html b/1984/laman/index.html index 55241e125..5f952af1d 100644 --- a/1984/laman/index.html +++ b/1984/laman/index.html @@ -165,9 +165,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 1985 entry source
Jump to: top
diff --git a/1985/lycklama/.entry.json b/1985/lycklama/.entry.json index 4a9bd1a13..178cb3494 100644 --- a/1985/lycklama/.entry.json +++ b/1985/lycklama/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Ed_Lycklama" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "lycklama.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1985/lycklama/index.html b/1985/lycklama/index.html index 209774493..2bf10ca6c 100644 --- a/1985/lycklama/index.html +++ b/1985/lycklama/index.html @@ -178,9 +178,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 1986 entry source
Jump to: top
diff --git a/1986/marshall/.entry.json b/1986/marshall/.entry.json index 11d893f5e..2ae730f99 100644 --- a/1986/marshall/.entry.json +++ b/1986/marshall/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Eric_Marshall" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "marshall.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, @@ -103,7 +103,7 @@ "OK_to_edit" : true, "display_as" : "markdown", "display_via_github" : true, - "entry_text" : "source for compilers.html" + "entry_text" : "markdown source for compilers.html" } ] } diff --git a/1986/marshall/compilers.html b/1986/marshall/compilers.html new file mode 100644 index 000000000..f0aa0ba29 --- /dev/null +++ b/1986/marshall/compilers.html @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + +
+ As briefly noted in the README.md gcc and +clang caused different problems with this entry depending on the +optimiser being enabled or not, where if the optimiser was enabled it +would work with one compiler and not the other. But if you then disabled +the optimiser the opposite problem would occur: the working compiler +would no longer work and the non-functional one would start to work! And +then there’s the problem of linux versus macOS causing problems.
+The alternate code (source code) +demonstrates the problem so you can see if your compiler has the problem +as described below.
+In some versions of gcc and clang (this was first discovered in +fedora linux 38) if the compiler was enabled one compiler would generate +code that segfaulted but the other one would not. However, when then +disabling the optimiser the compiler that had no problem suddenly did +and the one that did suddenly did not. But segfaulting was not by any +means the only problem.
+Another problem was that depending on the optimiser an infinite loop +would be entered. But then removing it would cause other problems as +well, including a segfault.
+It was not only one compiler that dumped core. Both dumped core with +other behaviour, depending on what changes in code were made and the +optimiser being on or off. Clang in macOS also dumped core!
+The alternate code (README.md) +has the original fix for clang which works with some compilers depending +on the optimiser. When we refer to the code below we refer to the +alternate code.
+Below we describe the conflicting options that resulted in the need +to modify the original code.
+With the optimiser disabled GCC compiled the code to enter an +infinite loop. Enabling the optimiser and it worked fine. But if the +optimiser is enabled clang generates code that first prints the text +twice (it should print it once) and then immediately dumps core!
+But then there’s that infinite loop. What happens if we remove +it?
+Once the loop is removed if we compile with clang and the optimiser
+is disabled all is okay. If however we compile with gcc and the
+optimiser is disabled the string is printed once and then the program
+dumps core! Why is this? It is because of the call to
+_exit() (see below).
Clang works fine with the loop removed and the optimiser disabled. If
+however we enable the optimiser clang will generate code that prints the
+string twice and dumps core just like before! What about the call to
+_exit()?
The call was:
+_exit(argv[(int)*argc-2/cc[1*(int)*argc]|-1<<4]);Changing it to not refer to these variables and just exit 1 and the +problems disappear!
+But then there was macOS which had another problem!
+If the infinite loop is not removed/commented out and the optimiser +is enabled it works fine but if it’s disabled it prints out the string +and then enters an infinite loop!
+After we fix the infinite loop if we change the _exit()
+call in macOS and disable the optimiser we might get something like:
$ ./marshall
+ choo choo
+Segmentation fault: 11Observe how it doesn’t print it twice. But of course it segfaults so +it’s not right either.
+If the optimiser is enabled, however, we will see that it works fine
+before the change to _exit().
And that is the tale of a little engine train that
+could…eventually! Or perhaps it’s the compilers, optimisers and OSes
+that eventually could?
Whichever you wish it to be, it now works with both clang and gcc! +Choo choo!
+ + + + + + + +Download IOCCC 1987 entry source
Jump to: top
diff --git a/1987/korn/.entry.json b/1987/korn/.entry.json index cc3e49c04..63d25699d 100644 --- a/1987/korn/.entry.json +++ b/1987/korn/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "David_Korn" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "korn.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1987/korn/index.html b/1987/korn/index.html index 9b596679d..722508afe 100644 --- a/1987/korn/index.html +++ b/1987/korn/index.html @@ -185,9 +185,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 1988 entry source
Jump to: top
diff --git a/1988/isaak/.entry.json b/1988/isaak/.entry.json index 52b31dc4b..dfa8cdc77 100644 --- a/1988/isaak/.entry.json +++ b/1988/isaak/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Mark_Isaak" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "isaak.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1988/isaak/index.html b/1988/isaak/index.html index 8ae87493c..281c5cb9b 100644 --- a/1988/isaak/index.html +++ b/1988/isaak/index.html @@ -190,9 +190,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 1989 entry source
Jump to: top
diff --git a/1989/jar.1/.entry.json b/1989/jar.1/.entry.json index ebd882a8f..4bc5d029d 100644 --- a/1989/jar.1/.entry.json +++ b/1989/jar.1/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Jari_Arkko" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jar.1.c", "inventory_order" : 20, @@ -73,6 +65,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1989/jar.1/index.html b/1989/jar.1/index.html index a53c3de4a..925b8c270 100644 --- a/1989/jar.1/index.html +++ b/1989/jar.1/index.html @@ -195,9 +195,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 1990 entry source
Jump to: top
diff --git a/1990/jaw/.entry.json b/1990/jaw/.entry.json index b84ed1a64..ab49be83e 100644 --- a/1990/jaw/.entry.json +++ b/1990/jaw/.entry.json @@ -11,14 +11,6 @@ { "author_handle" : "Paul_Eggert" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jaw.c", "inventory_order" : 20, @@ -75,6 +67,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1990/jaw/index.html b/1990/jaw/index.html index 11d3291c3..657e064ff 100644 --- a/1990/jaw/index.html +++ b/1990/jaw/index.html @@ -322,9 +322,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ The sorta scripts described below can sorta be found in
+this directory.
2 2+#` [ about as basic as you can get ]ld
+This script takes 2, adds 2 to it, and prints the result (with a +newline). I get 5 on the hand-held Sinclair under ZYNIX. Let me know if +your machine produces a different result so that I can send you +patches.
+0A` [ take arg 0, print it ]
+This is self-explanatory.
+[D2sD3s]:Dld [ input m n, output m n m n ]ld
+
+[2*1=d]:cld [ aux routine for :d recursion ]ld
+[1=D1s>1Ds_+=c]:dld [ input m n, as long as n <= m double it, output m n ]ld
+
+[D2s1s_+1s]:-ld [ input m n, output m-n n ]ld
+[1=D1s>1Ds_+#T=-2/D=1]:1ld [ aux routine for :@ ]ld
+[1D=d2/1=1ddd1 ]:!ld
+["0"Td]:0ld
+[DD=!1_+=0]:@ld [ input m, m assumed nonnegative, append
+ binary representation of m to top of s stack ]ld
+
+["-"T_]:+ld
+[D0 1s>=+1=@]:2ld [ input m, append - to top of s stack if m is negative,
+ then append binary representation of abs(m) to
+ top of s stack ]ld
+
+[d1A:A2=A]:A 27182 a1>=A
+[d2A:A1=A]:A 3145 a2>=A
+
+D # " in binary is "T 1=2 `ld
+1=Dd # " in binary is "T 1=2 `ld
+
+1=D 1s# " + "T 1s#T " is "T + #Td `ld
+1=D 1s# " - "T 1s#T " is "T _+ #Td `ld
+1=D 1s# " * "T 1s#T " is "T * #Td `ld
+1=D 1s# " / "T 1s#T " is "T / #Td `ld
+1=D 1s# " % "T 1s#T " is "T 1=D/*_+ #Td `ld
+
+1=D d "~ " #T " is "T D& #T$ld " (" 1=2 ")"T `ld
+1=D 1s# " & "T 1s#T " is "T &D& #T$ld " (" 1=2 ")"T `ld
+1=D 1s# " | "T 1s#T " is "T D&1sD&& #T$ld " (" 1=2 ")"T `ld
+1=D 1s# " ^ "T 1s#T " is "T 1=D&2sD&1sD&&&D& #T$ld " (" 1=2 ")"T `ld
+icalc shows off SORTA’s understanding of
+integer arithmetic. You can run it with up to two arguments; the
+arguments have default values of 27182 and 3145. Notice that
+SORTA doesn’t have a language primitive for converting a
+string to a number, yet icalc is performing arithmetic on
+its arguments. Can you see how? (Hint: Try
+./sorta "2 2+" 3 < sorta.icalc. Look at program A.)
+icalc also provides a useful binary output routine, and if
+you can’t remember offhand that
+D2sD3s&2sD&1sD&&&D& means XOR, you
+can just look it up here.
[ld#1_+1=y]:x
+[uD1+=x]:y
+0""1=y`
+This script prints out the maximum file descriptor available from
+repeated dup(2)s—usually NOFILE - 1. Can you
+see how it works?
[" "$ld]:sld [ print a space ]ld
+[1s1+DA$ld1s1_+D=sD=x]:xld [ given n (argc-n), print args ]ld
+0a1_+D=x""` [ simulation of /bin/echo ]ld
+This is a clone of /bin/echo, minus any argument
+processing and escape interpretation. (Note that, unlike some shells’
+echo, /bin/echo prints a newline even with no arguments.)
+Try ./sorta foo bar < iecho.
1#`ld2*#`ld3*#`ld4*#`ld5*#`ld6*#`ld7*#`ld8*#`ld9*#`ld10*#`ld
+This is a very straightforward factorial program: Take 1, print it, +multiply by 2, print it, multiply by 3, print it, etc.
+[1+D2s*#`ld1s]:f
+1 0
+1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f
+ifact1 does a lot of repeated work. ifact2
+takes advantage of this by keeping the current n!
+n on the stack. Each call to routine f (1=f)
+increments n by 1 and prints out the
+factorial.
[1+D2s*#`ld1s]:f [ given (n-1)! (n-1), print n!, put n! n ]
+[2s1=f2s1_+D0>=x]:x [ given n n! x, print (n+1)! through (n+x)! ]
+0 1 10 1=x
+Finally, ifact3 removes all the redundancy from
+ifact2, and exhibits proper documentation of stack
+routines. You can replace the 10 by anything in this
+version.
[ a simple example of interpreting code on the fly ]ld
+
+["] "2 2+":xT ["]T " produces "T 1=x#T " which should be 4."T`
+Notice that "2 2+" is a literal string inside the
+program.
"[] [.tr] "
+["]".[a-m][n-z][A-M][N-Z]"[" ]TTT ["]".[n-z][a-m][N-Z][A-M]"[" ]TTT
+"[tr] !q"T:xld
+["rot13: fatal: need exactly one argument"`q]:gld
+["rot13: fatal: cannot open "1ST`q]:hld
+a2_+=g
+1A 0 [ that 0 is for RDONLY ]ld
+ 0 'o D0 1s>=hld
+0Ou1=x [ in a more complex spawner, this would be done under a fork ]ld
+Despite its name, irot13 is meant to show how to exec a
+program with redirection from within sorta. You run it as
+./sorta blah < irot13; it opens blah and
+runs a (BSD-style) tr command to rotate all letters by 13
+spots. 1A 0 0 o opens argv[1]; the
+' keeps the filename around for subroutine h
+to print a nice error message if necessary. 0Ou closes
+stdout and replaces it with the new descriptor. Finally, subroutine
+x executes the tr.
["" "+sleep" "+5" "sleep" !q]:S [F0>1_+=S]:s
+"forking sleep 5..."`ld
+1=s
+"waiting for sleep to finish..."`ld
+w
+"done, sleep exit code " #T`
+This script illustrates the classic fork-exec-wait.
+[3*#" "T$ldD=f]:f
+1=f
+The sole purpose of this script is to prove that sorta
+can do infinite tail recursion (in this case, in program
+f). Do ./sorta < itailrec | more and watch
+the powers of 3 (mod your computer’s word size) flow by.
[0Ou "" "+-u" "sort" !q]:S [F0>1_+=S]:s
+[1Ou "" "+-u" "who" !q]:W [F0>1_+=W]:w
+P1=wO1=sOww
+This final example is a simple pipeline, who | sort.
Download IOCCC 1991 entry source
Jump to: top
diff --git a/1991/rince/.entry.json b/1991/rince/.entry.json index ff5742b7f..d289f309e 100644 --- a/1991/rince/.entry.json +++ b/1991/rince/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "James_Bonfield" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "rince.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1991/rince/index.html b/1991/rince/index.html index 970054cd9..c5eabb055 100644 --- a/1991/rince/index.html +++ b/1991/rince/index.html @@ -214,9 +214,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ buzzard.2.README.md this file
+buzzard.2.design.md description of
+FIRST (design documentation of THIRD)
+third implementation of THIRD in FIRST
+help.th online listing of THIRD primitives
+
Use ./first < <demo>.
demo1.1st prints Hello World! assuming +ASCII
+Use cat third <demo> | ./first.
demo1.th prints Hello World! regardless of +character set demo2.th demonstrates a simple loop +demo3.th demonstrates a simple if test demo4.th recursive factorial calculating on the way +up demo5.th recursive factorial calculating on +the way down demo6.th demonstrates switching from +compiler to execution mode
+Use cat third - | ./first.
To include the primitive on-line help, use
+cat third help.th - | ./first.
+ FORTH is a language mostly familiar to users of “small” machines. +FORTH programs are small because they are interpreted–a function call in +FORTH takes two bytes. FORTH is an extendable language– built-in +primitives are indistinguishable from user-defined words. FORTH +interpreters are small because much of the system can be coded in +FORTH–only a small number of primitives need to be implemented. Some +FORTH interpreters can also compile defined words into machine code, +resulting in a fast system.
+FIRST is an incredibly small language which is sufficient for +defining the language THIRD, which is mostly like FORTH. There are some +differences, and THIRD is probably just enough like FORTH for those +differences to be disturbing to regular FORTH users.
+The only existing FIRST interpreter is written in obfuscated C, and +rings in at under 800 bytes of source code, although through deletion of +both whitespace and obfuscation it can be brought to about 650 +bytes.
+This document FIRST defines the FIRST environment and primitives, +with relevant design decision explanations. It secondly documents the +general strategies we will use to implement THIRD. The THIRD section +demonstrates how the complete THIRD system is built up using FIRST.
+FIRST implements a virtual machine. The machine has three chunks of +memory: “main memory”, “the stack”, and “string storage”. When the +virtual machine wishes to do random memory accesses, they come out of +main memory–it cannot access the stack or string storage.
+The stack is simply a standard LIFO data structure that is used
+implicitly by most of the FIRST primitives. The stack is made up of
+ints, whatever size they are on the host machine.
String storage is used to store the names of built-in and defined +primitives. Separate storage is used for these because it allows the C +code to use C string operations, reducing C source code size.
+Main memory is a large array of ints. When we speak of
+addresses, we actually mean indices into main memory. Main memory is
+used for two things, primarily: the return stack and the dictionary.
The return stack is a LIFO data structure, independent of the +above-mentioned “stack”, which is used by FIRST to keep track of +function call return addresses.
+The dictionary is a list of words. Each word contains a header and a +data field. In the header is the address of the previous word, an index +into the string storage indicating where the name of this word is +stored, and a “code pointer”. The code pointer is simply an integer +which names which “machine-language-primitive” implements this +instruction. For example, for defined words the code pointer names the +“run some code” primitive, which pushes the current program counter onto +the return stack and sets the counter to the address of the data field +for this word.
+There are several important pointers into main memory. There is a +pointer to the most recently defined word, which is used to start +searches back through memory when compiling words. There is a pointer to +the top of the return stack. There is a pointer to the current end of +the dictionary, used while compiling.
+For the last two pointers, namely the return stack pointer and the
+dictionary pointer, there is an important distinction: the pointers
+themselves are stored in main memory (in FIRST’s main
+memory). This is critical, because it means FIRST programs can get at
+them without any further primitives needing to be defined.
There are two kinds of FIRST instructions, normal instructions and +immediate instructions. Immediate instructions do something significant +when they are used. Normal instructions compile a pointer to their +executable part onto the end of the dictionary. As we will see, this +means that by default FIRST simply compiles things.
+SYMBOL NAME FUNCTION
+
+ - binary minus pop top 2 elements of stack, subtract, push
+
+ * multiply pop top 2 elements of stack, multiply, push
+
+ / divide pop top 2 elements of stack, divide, push
+
+ <0 less than 0 pop top element of stack, push 1 if < 0 else 0
+Note that we can synthesize addition and negation from binary minus,
+but we cannot synthesize a time efficient divide or multiply from it.
+<0 is synthesize-able, but only in a non-portable
+way.
SYMBOL NAME FUNCTION
+
+ @ fetch pop top of stack, treat as address to push contents of
+
+ ! store top of stack is address, 2nd is value; store to memory
+ and pop both off the stack
+NAME FUNCTION
+
+echo output top of stack through C's putchar()
+
+key push C's getchar() onto top of stack
+
+_read read a space-delimited word, find it in the
+ dictionary, and compile a pointer to
+ that word's code pointer onto the
+ current end of the dictionary
+Although _read could be synthesized from key, we need
+_read to be able to compile words to be able to start any
+syntheses.
NAME FUNCTION
+
+exit leave the current function: pop the return stack
+ into the program counter
+SYMBOL NAME FUNCTION
+
+ : define read in the next space-delimited word, add it to
+ the end of our string storage, and generate
+ a header for the new word so that when it
+ is typed it compiles a pointer to itself
+ so that it can be executed.
+
+immediate immediate when used immediately after a name following a ':',
+ it makes the word being defined run whenever
+ it is typed.
+: cannot be synthesized, because we could not synthesize
+anything. immediate has to be an immediate operation, so it
+could not be synthesized unless by default operations were immediate;
+but that would preclude our being able to do any useful compilation.
NAME FUNCTION
+
+pick pop top of stack, use as index into stack and copy up
+ that element
+If the data stack were stored in main memory, we could synthesize
+pick; but putting the stack and stack pointer in main
+memory would significantly increase the C source code size.
There are three more primitives, but they are “internal only”– they
+have no names and no dictionary entries. The first is
+pushint. It takes the next integer out of the instruction
+stream and pushes it on the stack. This could be synthesized, but
+probably not without using integer constants. It is generated by
+_read when the input is not a known word. The second is
+compile me. When this instruction is executed, a pointer to
+the word’s data field is appended to the dictionary. The third is
+run me–the word’s data field is taken to be a stream of
+pointers to words, and is executed.
One last note about the environment: FIRST builds a very small word
+internally that it executes as its main loop. This word calls
+_read and then calls itself. Each time it calls itself, it
+uses up a word on the return stack, so it will eventually trash things.
+This is discussed some more in section 2.
Here’s a handy summary of all the FIRST words:
+WORD DESCRIPTION
+
+- * / binary integer operations on the stack
+<0 is top of stack less than 0?
+@ ! read from or write to memory
+echo key output or input one character
+_read read a word from input and compile a pointer to it
+exit stop running the current function
+: compile the header of a definition
+immediate modify the header to create an immediate word
+Here is a sample FIRST program. I’m assuming you’re using the ASCII
+character set. FIRST does not depend upon ASCII, but since FIRST has no
+syntax for character constants, one normally has to use decimal values.
+This can be avoided using getchar(3), though. Oh. One other
+odd thing. FIRST initially builds its symbol table by calling
+: several times, so it needs to get the names of the base
+symbols as its first 13 words of input. You could even name them
+differently if you wanted.
These FIRST programs have FORTH comments in them: they are contained +inside parentheses. FIRST programs cannot have FORTH comments; but I +need some device to indicate what’s going on. (THIRD programs are an +entirely different subject.)
+ ( Our first line gives the symbols for the built-ins )
+: immediate _read @ ! - * / <0 exit echo key _pick
+
+ ( now we define a simple word that will print out a couple characters )
+
+: L ( define a word named 'L' )
+ 108 echo ( output an ascii 'l' )
+ exit
+
+: hello ( define a word named 'hello')
+ 72 echo ( output an ascii 'H' )
+ 101 echo ( output an ascii 'e' )
+ 111 ( push ascii 'o' onto the stack )
+ L L ( output two ascii 'l's )
+ echo ( output the 'o' we pushed on the stack before )
+ 10 echo ( print a newline )
+ exit ( stop running this routine )
+
+: test immediate ( define a word named 'test' that runs whenever typed )
+ hello ( call hello )
+ exit
+
+test
+
+( The result of running this program should be:
+Hello
+)
+What is missing from FIRST? There are a large number of important
+primitives that aren’t implemented, but which are easy to implement.
+drop, which throws away the top of the stack, can be
+implemented as { 0 * + } – that is, multiply the top of the
+stack by 0 (which turns the top of the stack into a
+0), and then add the top two elements of the stack.
dup, which copies the top of the stack, can be easily
+implemented using temporary storage locations. Conveniently, FIRST
+leaves memory locations 3, 4, and
+5 unused. So we can implement dup by writing
+the top of stack into 3, and then reading it out twice:
+{ 3 ! 3 @ 3 @ }.
We will never use the FIRST primitive pick in building
+THIRD, just to show that it can be done; pick is only
+provided because pick itself cannot be built out of the
+rest of FIRST’s building blocks.
So, instead of worrying about stack primitives and the like, what +else is missing from FIRST? We get recursion, but no control flow–no +conditional operations. We cannot at the moment write a looping routine +which terminates.
+Another glaring dissimilarity between FIRST and FORTH is that there
+is no “command mode”–you cannot be outside of a :
+definition and issue some straight commands to be executed. Also, as we
+noted above, we cannot do comments.
FORTH also provides a system for defining new data types, using the
+words (in one version of FORTH) builds and does. We would
+like to implement these words as well.
As the highest priority thing, we will build control flow structures +first. Once we have control structures, we can write recursive routines +that terminate, and we are ready to tackle tasks like parsing, and the +building of a command mode.
+By the way, location 0 holds the dictionary pointer,
+location 1 holds the return stack pointer, and location
+2 should always be 0–it’s a fake dictionary
+entry that means pushint.
In this section we’ll eventually have real comments.
+The first thing we have to do is give the symbols for our +built-ins.
+: immediate _read @ ! - * / < exit echo key _pick
+Next we want to be mildly self commenting, so we define the word
+r to push the address of the return stack pointer
+onto the stack–NOT the value of the return stack pointer. (In fact, when
+we run r, the value of the return stack pointer is
+temporarily changed.)
: r 1 exit
+Next, we’re currently executing a short loop that contains
+_read and recursion, which is slowly blowing up the return
+stack. So let’s define a new word, from which you can never return. What
+it does is drops the top value off the return stack, calls
+_read, then calls itself. Because it kills the top of the
+return stack, it can recurse indefinitely.
: ]
+ r @ Get the value of the return stack pointer
+ 1 - Subtract one
+ r ! Store it back into the return stack pointer
+ _read Read and compile one word
+ ] Start over
+Notice that we don’t need to exit, since we never come back. Also,
+it’s possible that an immediate word may get run during
+_read, and that _read will never return!
Now let’s get compile running.
+: main immediate ]
+main
+Next off, I’m going to do this the easy but non-portable way, and put +some character constant definitions in. I wanted them at the top of the +file, but that would have burned too much of the return stack.
+: '"' 34 exit
+: ')' 41 exit
+: '\n' 10 exit
+: 'space' 32 exit
+: '0' 48 exit
+: '-' 45 exit
+
+: cr '\n' echo exit
+Next, we want to define some temporary variables for locations
+3, 4, and 5, since this’ll make
+our code look clearer.
: _x 3 @ exit
+: _x! 3 ! exit
+: _y 4 @ exit
+: _y! 4 ! exit
+
+OK. Now, we want to make THIRD look vaguely like FORTH, so we’re
+going to define ;. What ; ought to do is
+terminate a compilation, and turn control over to the command-mode
+handler. We don’t have one, so all we want ; to do for now
+is compile exit at the end of the current word. To do this
+we’ll need several other words.
Swap by writing out the top two elements into temps, and then reading +them back in the other order.
+: swap _x! _y! _x _y exit
+Take another look and make sure you see why that works, since it +LOOKS like I’m reading them back in the same order–in fact, it not only +looks like it, but I AM!
+Addition might be nice to have. To add, we need to negate the top
+element of the stack, and then subtract. To negate, we subtract from
+0.
: +
+ 0 swap -
+ -
+ exit
+
+Create a copy of the top of stack.
+: dup _x! _x _x exit
+Get a mnemonic name for our dictionary pointer–we need to compile +stuff, so it goes through this.
+: h 0 exit
+We’re going to need to advance that pointer, so let’s make a generic +pointer-advancing function. Given a pointer to a memory location, +increment the value at that memory location.
+: inc
+ dup @ Get another copy of the address, and get the value
+ so now we have value, address on top of stack.
+ 1 + Add one to the value
+ swap Swap to put the address on top of the stack
+ ! exit Write it to memory
+
+, is a standard FORTH word. It should write the top of
+stack into the dictionary, and advance the pointer
: ,
+ h @ Get the value of the dictionary pointer
+ ! Write the top of stack there
+ h inc And increment the dictionary pointer
+ exit
+' is a standard FORTH word. It should push the address
+of the word that follows it onto the stack. We could do this by making
+' immediate, but then it’d need to parse the next word.
+Instead, we compile the next word as normal. When ' is
+executed, the top of the return stack will point into the instruction
+stream immediately after the '. We push the word there, and
+advance the return stack pointer so that we don’t execute it.
: '
+ r @ Get the address of the top of return stack
+ We currently have a pointer to the top of return stack
+ @ Get the value from there
+ We currently have a pointer to the instruction stream
+ dup Get another copy of it--the bottom copy will stick
+ around until the end of this word
+ 1 + Increment the pointer, pointing to the NEXT instruction
+ r @ ! Write it back onto the top of the return stack
+ We currently have our first copy of the old pointer
+ to the instruction stream
+ @ Get the value there--the address of the "next word"
+ exit
+
+Now we’re set. ; should be an immediate word that pushes
+the address of exit onto the stack, then writes it out.
: ; immediate
+ ' exit Get the address of exit
+ , Compile it
+ exit And we should return
+Now let’s test out ; by defining a useful word:
: drop 0 * + ;
+Since we have inc, we ought to make
+dec:
: dec dup @ 1 - swap ! ;
+Our next goal, now that we have ;, is to implement
+if-then. To do this, we’ll need to play fast and loose with
+the return stack, so let’s make some words to save us some effort.
First we want a word that pops off the top of the normal stack and
+pushes it on top of the return stack. We’ll call this tor,
+for TO-Return-stack. It sounds easy, but when tor is
+running, there’s an extra value on the return stack–tor’s
+return address! So we have to pop that off first… We better just bite
+the bullet and code it out–but we can’t really break it into smaller
+words, because that’ll trash the return stack.
: tor
+ r @ @ Get the value off the top of the return stack
+ swap Bring the value to be pushed to the top of stack
+ r @ ! Write it over the current top of return stack
+ r @ 1 + r ! Increment the return stack pointer--but can't use inc
+ r @ ! Store our return address back on the return stack
+;
+Next we want the opposite routine, which pops the top of the return +stack, and puts it on the normal stack.
+: fromr
+ r @ @ Save old value
+ r @ 1 - r ! Decrement pointer
+ r @ @ Get value that we want off
+ swap Bring return address to top
+ r @ ! Store it and return
+;
+Now, if we have a routine that’s recursing, and we want to be polite
+about the return stack, right before we recurse we can run
+{ fromr drop } so the stack won’t blow up. This means,
+though, that the first time we enter this recursive routine, we blow our
+real return address–so when we’re done, we’ll return up two
+levels. To save a little, we make tail mean
+{ fromr drop }; however, it’s more complex since there’s a
+new value on top of the return stack.
: tail fromr fromr drop tor ;
+Now, we want to do if. To do this, we need to convert
+values to boolean values. The next few words set this up.
minus gives us unary negation.
: minus 0 swap - ;
+If top of stack is boolean, bnot gives us inverse
: bnot 1 swap - ;
+To compare two numbers, subtract and compare to 0.
: < - <0 ;
+logical turns the top of stack into either
+0 or 1.
: logical
+ dup Get two copies of it
+ 0 < 1 if < 0, 0 otherwise
+ swap minus Swap number back up, and take negative
+ 0 < 1 if original was > 0, 0 otherwise
+ + Add them up--has to be 0 or 1!
+;
+not returns 1 if top of stack is 0, and 0 otherwise
: not logical bnot ;
+We can test equality by subtracting and comparing to
+0.
: = - not ;
+Just to show how you compute a branch: Suppose you’ve compiled a call +to branch, and immediately after it is an integer constant with the +offset of how far to branch. To branch, we use the return stack to read +the offset, and add that on to the top of the return stack, and +return.
+: branch
+ r @ Address of top of return stack
+ @ Our return address
+ @ Value from there: the branch offset
+ r @ @ Our return address again
+ + The address we want to execute at
+ r @ ! Store it back onto the return stack
+;
+For conditional branches, we want to branch by a certain amount if
+true, otherwise we want to skip over the branch offset constant–that is,
+branch by one. Assuming that the top of the stack is the branch offset,
+and the second on the stack is 1 if we should branch, and
+0 if not, the following computes the correct branch
+offset.
: computebranch 1 - * 1 + ;
+Branch if the value on top of the stack is 0.
: notbranch
+ not
+ r @ @ @ Get the branch offset
+ computebranch Adjust as necessary
+ r @ @ + Calculate the new address
+ r @ ! Store it
+;
+here is a standard FORTH word which returns a pointer to
+the current dictionary address–that is, the value of the dictionary
+pointer.
: here h @ ;
+We’re ALL SET to compile if...else...then constructs!
+Here’s what we do. When we get if, we compile a call to
+notbranch, and then compile a dummy offset, because we
+don’t know where the then will be. On the stack we
+leave the address where we compiled the dummy offset. then
+will calculate the offset and fill it in for us.
: if immediate
+ ' notbranch , Compile notbranch
+ here Save the current dictionary address
+ 0 , Compile a dummy value
+;
+then expects the address to fixup to be on
+the stack.
: then immediate
+ dup Make another copy of the address
+ here Find the current location, where to branch to
+ swap - Calculate the difference between them
+ swap ! Bring the address to the top, and store it.
+;
+Now that we can do if...then statements, we can do some
+parsing! Let’s introduce real FORTH comments. find-) will
+scan the input until it finds a ), and exit.
: find-)
+ key Read in a character
+ ')' = Compare it to close parentheses
+ not if If it's not equal
+ tail find-) repeat (popping R stack)
+ then Otherwise branch here and exit
+;
+
+: ( immediate
+ find-)
+;
+
+( we should be able to do FORTH-style comments now )
+
+( now that we've got comments, we can comment the rest of the code
+ in a legitimate [self parsing] fashion. Note that you can't
+ nest parentheses... )
+
+: else immediate
+ ' branch , ( compile a definite branch )
+ here ( push the backpatching address )
+ 0 , ( compile a dummy offset for branch )
+ swap ( bring old backpatch address to top )
+ dup here swap - ( calculate the offset from old address )
+ swap ! ( put the address on top and store it )
+;
+
+: over _x! _y! _y _x _y ;
+
+: add
+ _x! ( save the pointer in a temp variable )
+ _x @ ( get the value pointed to )
+ + ( add the incremement from on top of the stack )
+ _x ! ( and save it )
+;
+
+: allot h add ;
+
+: maybebranch
+ logical ( force the TOS to be 0 or 1 )
+ r @ @ @ ( load the branch offset )
+ computebranch ( calculate the condition offset [either TOS or 1])
+ r @ @ + ( add it to the return address )
+ r @ ! ( store it to our return address and return )
+;
+
+: mod _x! _y! ( get x then y off of stack )
+ _y _y _x / _x * ( y - y / x * x )
+ -
+;
+
+: printnum
+ dup
+ 10 mod '0' +
+ swap 10 / dup
+ if
+ printnum
+ echo
+ else
+ drop
+ echo
+ then
+;
+
+: .
+ dup 0 <
+ if
+ '-' echo minus
+ then
+ printnum
+ 'space' echo
+;
+
+: debugprint dup . cr ;
+
+( the following routine takes a pointer to a string, and prints it,
+ except for the trailing quote. returns a pointer to the next word
+ after the trailing quote )
+
+: _print
+ dup 1 +
+ swap @
+ dup '"' =
+ if
+ drop exit
+ then
+ echo
+ tail _print
+;
+
+: print _print ;
+
+ ( print the next thing from the instruction stream )
+: immprint
+ r @ @
+ print
+ r @ !
+;
+
+: find-"
+ key dup ,
+ '"' =
+ if
+ exit
+ then
+ tail find-"
+;
+
+: " immediate
+ key drop
+ ' immprint ,
+ find-"
+;
+
+: do immediate
+ ' swap , ( compile 'swap' to swap the limit and start )
+ ' tor , ( compile to push the limit onto the return stack )
+ ' tor , ( compile to push the start on the return stack )
+ here ( save this address so we can branch back to it )
+;
+
+: i r @ 1 - @ ;
+: j r @ 3 - @ ;
+
+: > swap < ;
+: <= 1 + < ;
+: >= swap <= ;
+
+: inci
+ r @ 1 - ( get the pointer to i )
+ inc ( add one to it )
+ r @ 1 - @ ( find the value again )
+ r @ 2 - @ ( find the limit value )
+ <=
+ if
+ r @ @ @ r @ @ + r @ ! exit ( branch )
+ then
+ fromr 1 +
+ fromr drop
+ fromr drop
+ tor
+;
+
+: loop immediate ' inci @ here - , ;
+
+: loopexit
+
+ fromr drop ( pop off our return address )
+ fromr drop ( pop off i )
+ fromr drop ( pop off the limit of i )
+; ( and return to the caller's caller routine )
+
+: execute
+ 8 !
+ ' exit 9 !
+ 8 tor
+;
+
+: :: ; ( :: is going to be a word that does ':' at runtime )
+
+: fix-:: immediate 3 ' :: ! ;
+fix-::
+
+ ( Override old definition of ':' with a new one that invokes ] )
+: : immediate :: ] ;
+
+: command
+ here 5 ! ( store dict pointer in temp variable )
+ _read ( compile a word )
+ ( if we get control back: )
+ here 5 @
+ = if
+ tail command ( we didn't compile anything )
+ then
+ here 1 - h ! ( decrement the dictionary pointer )
+ here 5 @ ( get the original value )
+ = if
+ here @ ( get the word that was compiled )
+ execute ( and run it )
+ else
+ here @ ( else it was an integer constant, so push it )
+ here 1 - h ! ( and decrement the dictionary pointer again )
+ then
+ tail command
+;
+
+: make-immediate ( make a word just compiled immediate )
+ here 1 - ( back up a word in the dictionary )
+ dup dup ( save the pointer to here )
+ h ! ( store as the current dictionary pointer )
+ @ ( get the run-time code pointer )
+ swap ( get the dict pointer again )
+ 1 - ( point to the compile-time code pointer )
+ ! ( write run-time code pointer on compile-time pointer )
+;
+
+: <build immediate
+ make-immediate ( make the word compiled so far immediate )
+ ' :: , ( compile '::', so we read next word )
+ 2 , ( compile 'pushint' )
+ here 0 , ( write out a 0 but save address for does> )
+ ' , , ( compile a push that address onto dictionary )
+;
+
+: does> immediate
+ ' command , ( jump back into command mode at runtime )
+ here swap ! ( backpatch the build> to point to here )
+ 2 , ( compile run-code primitive so we look like a word )
+ ' fromr , ( compile fromr, which leaves var address on stack )
+;
+
+
+: _dump ( dump out the definition of a word, sort of )
+ dup " (" . " , "
+ dup @ ( save the pointer and get the contents )
+ dup ' exit
+ = if
+ " ;)" cr exit
+ then
+ . " ), "
+ 1 +
+ tail _dump
+;
+
+: dump _dump ;
+
+: # . cr ;
+
+: var <build , does> ;
+: constant <build , does> @ ;
+: array <build allot does> + ;
+
+: [ immediate command ;
+: _welcome " Welcome to THIRD.
+Ok.
+" ;
+
+: ; immediate ' exit , command exit
+
+[
+
+_welcome
+
+
+
+
+
+
+
+ Download IOCCC 1992 entry source
Jump to: top
diff --git a/1992/kivinen/.entry.json b/1992/kivinen/.entry.json index b19bf19a1..2b7eb8b15 100644 --- a/1992/kivinen/.entry.json +++ b/1992/kivinen/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Tero_Kivinen" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "kivinen.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1992/kivinen/index.html b/1992/kivinen/index.html index 02c3b72e0..9a5104472 100644 --- a/1992/kivinen/index.html +++ b/1992/kivinen/index.html @@ -239,9 +239,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ The Towers of Hanoi is a puzzle consisting of three posts and several +rings of various sizes. The initial configuration of the game is that +all the rings are stacked in decreasing order of size on one post. The +object of the game is to move all the rings from one post to another +subject to two rules: only one ring can be moved at a time, and at no +time can one ring be placed on top of a smaller ring. The number of +steps necessary to solve this puzzle is exponential in the number +rings.
+ + + + + + + +
+ The Patience puzzle, probably of Chinese origin, consists of an iron +U-shaped piece and several rings that link around it. The physical shape +of the puzzle is very difficult to describe verbally, but here is an +attempt.
+This description of the puzzle is presented as a set of instructions +for constructing the puzzle. Start with a flat piece of iron about 15 +centimeters long by about two centimeters wide. Drill six holes in the +piece of iron. Through each hole, place a rod. The rod should have a +diameter slightly less than that of the hole so that it can move freely. +One end of the rod is like the head of a nail and is larger than the +hole to prevent it from coming free. The other end is looped around a +metal ring with a diameter of about five centimeters. This completely +prevents the iron rod from separating from the base. Each rod, in +addition to going through the iron base, goes through the ring at the +end of the rod to its right. The only rod free of this restriction is +the rightmost rod. What has been described so far is one of the two +components of the puzzle.
+The other component is another iron rod shaped like a U.
+The U is as long from the tips to the base as the original
+piece of iron with the holes in it. The ends of the U are
+looped around another ring of about five centimeters in diameter. In the
+puzzle’s initial configuration, the U is placed through the
+rings so that it is held in place by the iron rods. Imagine starting
+with the U piece before the iron loop is placed at the end.
+Slide the legs of the U through the loops so that the iron
+rods securing the loops to the metal strip go up the center of the
+U. Placing the final ring at the ends of the U
+prevents it from coming out. The object of the puzzle is to free the
+U-shaped piece from the other piece.
The following illustration (sideways) shows a three-ring case of this
+puzzle. If you look carefully, you can see that the first ring could be
+lifted over the edge of the U and slid down through the
+center of the U, thereby freeing the U from
+the first iron rod. The second ring can also be freed in the initial
+configuration since it is already over the rod for the first ring. The
+third ring cannot be moved, however, since it is blocked by the iron rod
+to which the first ring is attached. If the first first is removed, then
+the third ring is no longer blocked by the first ring’s iron rod and can
+be removed freely. The second ring, however cannot be moved if the first
+ring is gone because it will then be blocked by the first ring in its
+new position outside of the U.
Here is an illustration of the three-ring case. The bottom of the +puzzle is on the left-hand side of the page.
+
+
+
+ //==\\
+ || ||
+ || ||
+ *****||** ||
+ *** || ***||
+ ** || *||
+ |\ ** || ||**
+ | \ *** || || ***
+ | \ *** | || ===
+ | \ *** || // * \\
+ | \ *** || || ** ||
+ | | | ********** ||
+ | | || || || ||
+ | | || || || ||
+ | | *****||** || \\ ||
+ | | |\ | *** || ***|| ||
+ |==| |=========-**-===-||-============//
+ |==| |=========-**-===-||-===========//
+ | | \| | *** || || ***
+ | | *** || || ===
+ | | ***| || // * \\
+ | | *** || || ** ||
+ | | ||********** ||
+ | | || || || ||
+ | | *****||** || \\ ||
+ | | |\ | *** || ***|| ||
+ |==| |=========-**-===-||-============//
+ |==| |=========-**-===-||-===========//
+ | | \| | *** || || ***
+ | | *** || || ===
+ | | ***| || // * \\
+ | | *** || || ** ||
+ | | ||********** ||
+ | | || || || ||
+ | | || || || ||
+ | | | || || \\ ||
+ | | |\ | || || ||
+ |==| |================-||-============//
+ |==| |================-||-===========//
+ | | \| | || ||
+ | | || ||
+ | | || ||
+ | | || ||
+ \ | || ||
+ \ | \\ ||
+ \ | \\ ||
+ \ | \\ ||
+ \ | \\||
+ \| \||
+This puzzle takes other forms as well. One such puzzle is called +“Spin-Out”. This puzzle consists of one plastic piece that encases +another piece that can slide in and out of it. The movement of the +smaller piece is restricted by discs that, when turned to one angle, +lock the smaller piece inside the larger one and, when turned the other +way, allow it to move. This puzzle is as difficult to describe as the +first and, since I don’t have one in front of me, I will not attempt to +describe it. The object of this puzzle is to free the smaller piece from +the larger one. The other puzzle that is like this one consists of a +cylinder with rods protruding from the top. The rods can be slid from +the center to the edge of the cylinder constrained by a series of discs +in the center. The discs have cuts in them that allow some rods to move +and force others in one place based on the relative rotations of the +discs. The object here is to move all the rods from the inside to the +outside.
+What makes these puzzles similar is that the physical shapes of the
+puzzles put the same restrictions on the movement of the pieces.
+Specifically, there is always one piece that is free to move. The only
+other piece that is free to move is the second one that is still
+attached to the base it is trying to be freed from. In the case of the
+original puzzle, consider the rings numbered from 1 to 6 with ring 1
+being the one at the closed end of the U. Ring 1 can always
+be removed from or added to the U. In the initial state,
+when all rings are around the U, ring 2 is the other one
+that can be moved because it is second. If ring 1 is removed, then only
+two operations are possible: the restoration of ring 1 and the removal
+of ring 3. This is because ring 1 is always movable, and ring 3 is
+currently the second ring still attached to the U. This
+constraint leads to a recursive solution to the puzzle. Like the Towers of Hanoi, the number of steps to solve this
+puzzle is exponential in the number of rings.
Download IOCCC 1993 entry source
Jump to: top
diff --git a/1993/jonth/.entry.json b/1993/jonth/.entry.json index a69155f29..9352c1e95 100644 --- a/1993/jonth/.entry.json +++ b/1993/jonth/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Jon_Thingvold" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jonth.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1993/jonth/index.html b/1993/jonth/index.html index 413d1dd59..60ac85a09 100644 --- a/1993/jonth/index.html +++ b/1993/jonth/index.html @@ -165,9 +165,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ Due to the long time this has been slowly evolving, it’s hard to
+describe quite whether it’s been implemented. I think
+perhaps evolve is a better term. The basic concept behind the program is
+to describe a game as a simple, slightly cheaty, cellular automata
+system, with some of the transitions bound to key presses.
For instance, assume you wish to make a boulder o roll
+off a hill / A standard automata would have 2 rules
+something like this:
+ o.. ...
+ \.. --> 'o' .o. --> '.'
+ ... .\.
+However, the system I devised, in order to keep things small and +neat(!), was to define a 3x3 to 3x3 map (instead of the 3x3 to 1x1 shown +above). Hence:
+ .o. ...
+ .\. --> .\o
+ ... ...
+This does of course have the odd problem with clashes. What if we +want to roll two boulders down simultaneously towards the same square. I +perform no buffering, and so this situation is just a ‘fight it out’ and +let which ever transition has priority win. Such a case may produce the +following change of maps (depending on which ‘roll’ transition is used +first).
+ o.o ..o o..
+ \./ changes to \o/ or to \o/
+ ... ... ...
+I claim that it is possible to perform all of the 3x3 to 1x1 +transitions in a 3x3 to 3x3 based system. One other feature added in is +a workaround for the ‘no buffering’ problem. If we define a space ship +(as in the Space +Invaders example) to fly right, and we are ‘walking through’ our map +from top-left to bottom-right, then it is easy to foresee a situation +where our space ship would zip right as far as possible in only the one +cycle of the automata. However, a similar leftwards-bound space ship +would only move the required one square. For this reason I have two +lists of transitions and apply them independently of each other; first +top-left to bottom right, and the second in the reverse direction. This +means that I can make any ‘object’ move by any amount in a single +transition.
+Now comes the key important addition to make it ‘game’ worthy - the
+addition of external control by the user. This is done by requesting
+that some transitions are only valid when a certain key has been
+pressed. So for example, if we (an @ symbol) wish to move
+right when the l key is hit we have a transition like:
* * * * * *
+ * @ . * . @
+ * * * * * *
+Here I also use wildcards. On the match side (left) of the
+transition, an asterisk will match against any character. On the update
+side (right), an asterisk means ‘keep whatever was here before’. So the
+above example simply means to swap the @ and .
+around when l is pressed. For the purposes of space saving,
+this transition is shrunk to a single line.
a b c 1 2 3
+ d e f 4 5 6
+ g h i 7 8 9
+is written as: eabcdfghi512346789K.
If there are no keys needed for this transition, the character
+*1 should be used instead. Now the format of the game
+description file is now the following (angle brackets denote an ‘entity’
+to be defined):
<top-bot transition>*\n<bot-top transition>*\n<X size>[ \t]+<Y size>\n
+ <map>
+The <X size> should take into account the newline
+at the end of each line of map, and hence should also be one larger than
+expected.
Download IOCCC 1994 entry source
Jump to: top
diff --git a/1994/ldb/.entry.json b/1994/ldb/.entry.json index 39b7c391e..0686ccfda 100644 --- a/1994/ldb/.entry.json +++ b/1994/ldb/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Laurion_Burchall" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "ldb.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1994/ldb/index.html b/1994/ldb/index.html index 868b042c1..51ee6c35e 100644 --- a/1994/ldb/index.html +++ b/1994/ldb/index.html @@ -163,9 +163,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ For the first program (shapiro.c):
+This is a very simple program:
+ 1. #include <stdio.h>
+ 2. main()
+ 3. {
+ 4. int K,L,M=0;
+ 5. FILE *J;
+ 6. J=fopen(__FILE__,"r");
+ 7. for(;
+ 8. (L=getc(J))>=0;
+ 9. L>='J'&&L<='J'+15 &&
+10. ((M=!M)?K=L:putchar(K-'J'|(L-'J'<<4)))
+11. );
+12. exit(0);}
+Lines 1-5 are basic stuff. Line 6 opens the source code for input.
+Line 7 is the loop. Line 8 reads in each character in the file (placed
+in L) while not >= 0. Line 9 is an if
+expression which filters out all characters not between J
+and J + 15. Line 10 uses M to execute two
+different paths; the first just stores the character in K
+and the second path combines the last two characters read in as high and
+low nibbles of a byte and outputs them. In effect this is a very poor
+ASCII to binary decoder. The last line is necessary so that make will
+not stop when it executes this program.
The second program is encoded into this format and then included +(mostly) as a comment.
+Notice that it is impossible to write this program while leaving 16 +contiguous ASCII characters free for the encoding. Because of this the +second program is mostly included as a comment. However, some of the +characters of the encoded program are also source code for the +decoder.
+ 1 #include <stdio.h>
+ 2 #include <time.h>
+ 3 #define o(j,k,m,n,l) {fprintf(j,a[k],m,n,a[l]);fflush(j);}
+ 4 char *a[]={"%c+------------+\n","%c+%3d %-8s+\n","seconds","minutes",
+ 5 "hours","%s\n","\0337\033[%d;65;H%s\0338"};
+ 6 int b[]={0,0,17,14,11,0};
+ 7 main(v,c)
+ 8 int v;
+ 9 char ** c;
+10 {
+11 char d[40],*e,f=6;
+12 time_t g;
+13 FILE *h,*i;
+14 while (--f && !pipe(d) && !fork());
+15 h=fdopen(6-f<<1,"a");
+16 i=fdopen((6-f<<1)+1,"r");
+17 for(;;){
+18 if(f){
+19 fgets(d,40,i);
+20 d[strlen(d)-1]='\0';
+21 if(*d=='0'+f){ sprintf(*c,"%s\n",d+1); o(stdout,6,6-f,d+1,0) }
+22 if(*d>'0'+f) o(h,5,d,0,0)
+23 }else{
+24 time(&g);
+25 e=asctime(localtime(&g));
+26 for(v=1;v<6;v++) o(h,v!=1&&v!=6-1,v+'0',atoi(e+b[v]),v)
+27 sleep(1);
+28 }}}
+
+Lines 1 and 2 have to be there. Think of o in line 3 as
+send message with j,k and m as
+where, format and what. Lines 4 and 5 are all the text strings. Line 6
+is the offsets into the time string for the hours, minutes and seconds.
+Lines 7-13 are basic stuff. Line 14 is the crux of the biscuit. 14 forks
+off f (6) processes each of which is connected to the ones
+above and below it with a Unix pipe. It was too difficult to recover the
+actual FD numbers for the pipe ends in line 14 so I regenerate them and
+fdopen() them (for use with the standard C library
+functions) on lines 15 and 16. Now we have six almost identical
+processes each of which has two files open, h and
+i (up and down). The only difference in each process is the
+value of f (0 <= f < 6). The five
+processes where f !=0 are the slave processes and
+f == 0 is the master. Line 17 says we are going to do this
+forever. Lines 18 and 23 differentiate between the master and the
+slaves.
Lines 19 to 22 are the slave code. Here we read in a line of text
+from i (down, line 19) and remove the newline char (line
+20). On line 21 if this message is for us (if the first character is our
+number, f, in ASCII) we do two things:
*c which is really
+argv[0]. If you are on the correct flavor of Unix this will
+change the output of the ps(1) command to the string we
+just put there.stdout with format number 6
+("\0337\033[%d;65;H%s\0338") If you can read VTxxx (and I
+read books printed as ANSI/VTxxx codes for fun :-) ) you know this is
+the sequence for:remember where we are,
+go somewhere else,
+print the message,
+and return to where we started.
+Line 22 says if this message is not for us (the number is bigger than
+ours) send it to h (up).
Lines 24 to 27 are the master code. Here we get the time and convert
+it to an ASCII format (lines 24 and 25). Then on line 26 we build and
+send each of the messages for the slaves. The message uses a different
+control string for the different slaves. For the first and last slave
+the message text is just "%c+------------+\n" (slave
+number, text, newline.) For the other slaves it is
+"%c+%3d %-8s+\n" (slave number, number, units, newline).
+The slave number is v+'0'. The number (if used) is
+atoi(e+b[v]) and the units are just the string in the slave
+number position.
Download IOCCC 1995 entry source
Jump to: top
diff --git a/1995/leo/.entry.json b/1995/leo/.entry.json index 0827486fb..71d4ecfae 100644 --- a/1995/leo/.entry.json +++ b/1995/leo/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Leonid_A_Broukhis" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "leo.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, @@ -103,7 +103,7 @@ "OK_to_edit" : true, "display_as" : "markdown", "display_via_github" : true, - "entry_text" : "spoiler document" + "entry_text" : "markdown source for spoiler1.html" } ] } diff --git a/1995/leo/index.html b/1995/leo/index.html index 41e4cb224..a28b66b77 100644 --- a/1995/leo/index.html +++ b/1995/leo/index.html @@ -315,9 +315,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ Download IOCCC 1996 entry source
Jump to: top
diff --git a/1996/jonth/.entry.json b/1996/jonth/.entry.json index 71998659a..b3fe13456 100644 --- a/1996/jonth/.entry.json +++ b/1996/jonth/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Jon_Thingvold" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jonth.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1996/jonth/index.html b/1996/jonth/index.html index f5ab446fa..0f0f98c63 100644 --- a/1996/jonth/index.html +++ b/1996/jonth/index.html @@ -181,9 +181,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ Morse codes for letters and digits use sequences of 1 to 5
+(inclusive) Morse symbols (denoted as . or -),
+e.g.
E .
+Z --..
+3 ...--
+(BTW: digits always have 5 symbols, letters 1 to 4. I didn’t use this +fact.)
+Of course we can encode these symbols with bits in a byte. I chose
+- to be a bit set to 0, . to be a bit set to
+1. Characters in C are at least 8 bits, but they are either signed or
+unsigned. To be portable, I’ve used only values from 0 to 127
+(inclusive) (both in the encoding of Morse codes, as well as in
+subexpressions in the program). Hence we can use type char
+(using unsigned char for an extra bit would have made the
+source code excessively large ;-)
The least significant bit contains the first symbol of a Morse code, +so we have
+E > 0......1
+Z > 0...1100
+3 > 0..00111
+The dots still have to be determined. But we also need to encode the +length of the sequence. Since there are only 4 different kind of +lengths, two bits would suffice, e.g.
+E > 000....1
+Z > 010.1100
+3 > 01100111
+That means bits 6 and 5 set to 00 is length 1, set to 01 is length 2, +etc. But this would mean that we would have to extract the length into a +counter before shifting out each symbol, such as
+length = (encoding & 96) >> 5;
+do {
+ putchar(encoding & 1 ? '-' : '.');
+ encoding >>= 1;
+} while (length--);We can also use the remaining bits to include the +counter
+length 1: 00000.1x E > 00000.11
+length 2: 0000.1xx
+length 3: 000.1xxx
+length 4: 00.1xxxx Z > 00.11100
+length 5: 0.1xxxxx 3 > 0.100111
+with 1 bit still to be chosen freely (let’s assume it is 0). Now we +can do
+do {
+ putchar(encoding & 1 ? '-' : '.');
+ encoding >>= 1;
+while (encoding > 1);As you see, the encoded Morse sequence can be the counter itself. But +this is not very pleasant, as some Morse sequences will end up having an +ASCII value < 32, i.e. they will be control codes. This would make +the initializing (encoding) string awkward. Hence I came up with the +following
+length 1: 0010010x E > 00100101 (= '%')
+length 2: 001010xx
+length 3: 00110xxx
+length 4: 0100xxxx Z > 01001100 (= 'L')
+length 5: 011xxxxx 3 > 01100111 (= 'g')
+Notice these encodings in the initialization string. Only the coding
+for 5 needs an escape
+(5 = ..... > 01111111 = '\177'). The way the length is
+encoded still allows the encoding to be its own counter:
do {
+ putchar(encoding & 1 ? '-' : '.');
+ encoding = (encoding + 32) >> 1;
+} while (encoding > 35);The last shift will always result in 00100010, hence the
+test for >35. Note that the calculation for the next
+value of encoding is done at int precision, so
+that the actual value of the char remains
+<= 127.
The character '-' (ASCII 45) and '.' (ASCII
+46) are conveniently in sequence, so the putchar can simply be
putchar(45 + encoding % 2);I used the modulo operator this time, because + takes
+precedence over &. The assignment can be incorporated
+into the while control test, and a do-while
+loop with a body of a single expression statement can be written as a
+while loop with a comma operator in the test:
while(putchar(45 + encoding % 2), encoding = encoding + 32 >> 1);Voila.
+Now, the decoding of Morse codes is just the reverse. We shift in
+0s and 1s as we scan the line. But: we have to
+scan from back to front (the left most Morse symbol is in the least
+significant bit, and thus has to shifted in last). The test for the
+scanning while loop must stop when there are no more . or
+- on the left. Hence we need a terminating character in
+front of the line. The input line is read one past the encoding string
+(into the same character array) the terminating character for the
+reverse scan is therefore provided by the implicit '\0' of
+the array initializer ! The test also stops when at most 5 symbols have
+been shifted in.
The encoded character is then looked up in the encoding string with
+memchr(3) (why memchr(3) is explained later).
+Either a ? (when there are more than 5 consecutive Morse
+symbols, or if the sequence is unknown) or the decoded character is
+printed. Or a ' ' if there was an extra space in the source
+line. Note the use of “or” in these last sentences, as they explain the
+use of the conditional operator.
The strspn(3) calls need either " .-" or
+".-" as argument. Of course they are overlapped. I put
+these strings between the encodings for digits and letters. This is OK,
+since neither of these characters, or the '\0', is a valid
+encoding ! We must, however, use memchr(3) instead of
+strchr(3) in the decoding lookup because of this
+'\0'. Since it is far more obscure this way, I hope you’ll
+forgive me.
The outline of the program is roughly as follows
+while (more lines) {
+ if (line is in morse) {
+ while (more characters) {
+ skip next sequence
+ if (' ')
+ putchar(' ');
+ else
+ decode_sequence; /* backwards */
+ }
+ } else {
+ while (more characters) {
+ if (isalnum)
+ encode_character;
+ putchar(' ');
+ }
+ }
+Because of the two while(more characters) loops
+are the same, I’ve exchanged the if(line is in morse) and
+the two while loops (so there is single
+while loop). However, we must now store the
+if(line is in morse) test for efficiency (it would be
+stupid to test the entire line for every character). This is done in
+l[0] ! (the first encoding is thus in l[1]).
+The memchr(3) decoding lookup is not disturbed, because the
+test is always 0 for Morse lines, and '\0' is
+not a possible encoding. l[0] is also used as the temporary
+shift character while encoding a line of text. This does not disturb the
+if (line is in morse) test, as the temporary
+l[0] will remain != 0.
I’ve put the ifs in the above layout into a single
+switch. Hope you don’t mind ;-)
Summary of the use of l[999]:
+l[0] result of "is this line in morse" test
+l[0] temporary shift during encoding
+l[0] - l[33] Morse codings (letters and digits, plus garbage)
+l[11] - l[14] strspn argument
+l[12] - l[14] strspn argument
+l[34] terminator for backward decoding
+l[35] - l[998] input line
+Selected notes: - We don’t need to declare isalnum(3)
+(or include ctype.h) as its implicit declaration is correct (int
+argument, int result). This is not so for strlen(3),
+strspn(3), and memchr(3) as they use
+size_t. - During development, my gcc 2.7.2.3 had an
+internal compiler error (signal 6) on code that was correct ! - lclint
+2.4b thinks that “the observer is modified” a couple of times, whereas
+it is not. Well, it is, but there is a sequence point in between. -
+lclint 2.4b parses line 13 as while loop is infinite,
+whereas it is not.
.. - …. .. -. -.- - …. .. … … .–. — .. .-.. . .-. .. … – — .-. . — -… +..-. ..- … -.-. .- - . -.. - …. .- -. - …. . .–. .-. — –. .-. .- –
+. -..- . .-. -.-. .. … . -.-. …. .- -. –. . - …. . .–. .-. — –. .-. +.- – … — .. - -.. — . … -. — - … …. — ..- -
+ + + + + + + +Download IOCCC 1998 entry source
Jump to: top
diff --git a/1998/schnitzi/.entry.json b/1998/schnitzi/.entry.json index 014d44a85..c15217708 100644 --- a/1998/schnitzi/.entry.json +++ b/1998/schnitzi/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Mark_Schnitzius" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "schnitzi.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/1998/schnitzi/index.html b/1998/schnitzi/index.html index 5e84b289f..0a18de20b 100644 --- a/1998/schnitzi/index.html +++ b/1998/schnitzi/index.html @@ -226,9 +226,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ This document describes how this was fixed to work with macOS as well +as some of the magic in the entry. Even if you don’t have a Mac it might +be worth your time reading especially as some of it details some +important fixes and changes in the original entry as well.
+As an aside: if you have a Mac with the Apple chip, some of the
+header files will report an unsupported architecture and unsupported
+compiler (error, warning). This will not make the program abort,
+however, but even with an Intel CPU though, the original entry will not
+work in macOS as it stands because /usr/include is
+hard-coded in the code and macOS does not have it.
Nevertheless, although some of it would not work in other systems
+where /usr/include exists, the #defines would
+still be found okay, at least until a file was not found (this
+limitation was removed in both versions in 2023). see
However, as noted, macOS does not have
+/usr/include so this would never work for macOS without
+some changes, described next. For macOS you will need the command line
+tools installed which can be done like:
sudo xcode-select --installAn important point is that the original version hard-coded
+gcc but just like the entry, the alternate version has also
+removed this limitation despite gcc existing (though it’s
+clang) in macOS.
Now as far as how this works if you look at the code of schweikh1.c, on line 55 you’ll see a funny +command (this goes for the alternate version as well but a bit +different).
+Now the key is two magic numbers, one (two times) on a different line +shortly below the string and the other a couple lines further below.
+In the original the numbers are, respectively, 44 and
+46, but with the gcc limitation removed the numbers now are
+43 and 45. But how does this work? Well the
+command is:
"0gcc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
+which has been changed to be:
+"0cc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
+Shortly below that you will see, as noted above, the numbers:
+if ((H = fopen (__FILE__+43, 43+__FILE__)))
+while ((fgets (L, (int)sizeof L, H)) != 0) {
+ I[strcspn (I, 45+__FILE__)] = O = 0;The first number in the above C means the length starting from 0 up
+through the >. The second number is the same starting
+point but up through the \0 which is why it’s
++2. But what happens if only the first number is updated
+(from the original)? Most of the output will be just
+#define by itself; in the cases where there was text after
+that it was macros that certainly were not defined. There might have
+been other errors as well.
Changing this for macOS allows for opening the right files; the
+problem with macOS is /usr/include does not exist: instead
+it’s
+/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
+which is why the different command line and the different numbers. The
+change of the numbers can be found in the alternate code and the
+explanation is the same.
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see: https://creativecommons.org/licenses/by-sa/4.0/
- + @@ -414,9 +414,9 @@ role="doc-backlink">↩︎Download IOCCC 2000 entry source
Jump to: top
diff --git a/2000/jarijyrki/.entry.json b/2000/jarijyrki/.entry.json index bea4c1a95..fbf4021b1 100644 --- a/2000/jarijyrki/.entry.json +++ b/2000/jarijyrki/.entry.json @@ -10,14 +10,6 @@ { "author_handle" : "Jyrki_Holopainen" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jarijyrki.c", "inventory_order" : 20, @@ -50,6 +42,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2000/jarijyrki/index.html b/2000/jarijyrki/index.html index 0c7dd9610..7110a0e19 100644 --- a/2000/jarijyrki/index.html +++ b/2000/jarijyrki/index.html @@ -428,9 +428,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see: https://creativecommons.org/licenses/by-sa/4.0/
- + @@ -428,9 +428,9 @@ class="footnote-back" role="doc-backlink">↩︎Download IOCCC 2001 entry source
Jump to: top
diff --git a/2001/jason/.entry.json b/2001/jason/.entry.json index fa7f0d5bc..8d22071aa 100644 --- a/2001/jason/.entry.json +++ b/2001/jason/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Jason_Orendorff" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jason.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2001/jason/index.html b/2001/jason/index.html index 1463cf679..6775f38be 100644 --- a/2001/jason/index.html +++ b/2001/jason/index.html @@ -226,9 +226,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ First, build the kernel and filesystem (just make).
+In short, you need to build this on an x86 Linux machine. If you wish to
+try to compile on an OS other than Linux you may need modify the Makefile to compile sh using a
+cross-compiler that produces ELF binaries - i.e. a cross-compiler
+targeting x86 Linux. In short, you need to build this on an x86 Linux
+machine.
Next, find a suitable machine to run the OS on. It should be +perfectly safe, and since it does not directly attempt to access any +hard disk drives (only a RAM disk loaded by +the bootloader) it should do no damage to your existing setup, BUT I +ACCEPT NO LIABILITY FOR ANY DAMAGE DONE BY THIS PROGRAM. Run it at your +own risk. The OS has been tested on half a dozen machines or so, and +worked on all but one. The failure came in the form of the machine +rebooting itself midway through loading the OS, with no nasty +side-effects.
+Hardware requirements - i386 or better processor, but the GUI is +surprisingly power-hungry, so I would recommend a fast P3 or P4 +processor (has been tested on P3/P4/Athlon machines). Minimum RAM is +something like 32MB. PS/2 mouse & keyboard required - no USB I’m +afraid, and the keyboard map is for a UK keyboard. Uses VESA VBE 3.0, so +should work on any modern graphics card (has been tested on NVIDIA, +Matrox, and SiS cards).
Now you need to setup a bootloader to load the OS. If you have a
+system using a lilo bootloader you can simply add the OS to your boot
+menu. Just add appropriate image= and initrd=
+lines to your /etc/lilo.conf, pointing at
+kernel and fs.tar respectively (as built by
+make earlier), then run lilo. If you do not wish modify
+your lilo setup, one easy option is to get yourself a GRUB boot disk
+image, and follow the instructions for GRUB below - Debian users can
+simply runapt-get install grub-disk to get themselves a
+GRUB boot disk.
If you run GRUB, then at the boot menu simply hit c to
+get a console, then type kernel=(hd0,0)/<PATH>/kernel
+(substituting appropriate hard drive/partition numbers & path, or
+using (fd0) to load a kernel on the floppy), then
+initrd=(hd0,0)/<PATH>/fs.tar, and finally
+boot.
Assuming the OS has booted, what can you do now? Well, start by
+trying sh, to open another shell. Then, try
+vi gavin.c to open up the OS source in a text file viewer
+(up/down or PgUp/PgDn to scroll). Note that the provided applications,
+sh and vi both have windows of the same size,
+and both open in the top left corner of the screen, so you may need drag
+the windows around a bit to tell them apart.
To shut down - just hit the power button. :-)
Download IOCCC 2004 entry source
Jump to: top
diff --git a/2004/jdalbec/.entry.json b/2004/jdalbec/.entry.json index 96b2e7ce7..4410bd446 100644 --- a/2004/jdalbec/.entry.json +++ b/2004/jdalbec/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "John_Dalbec" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jdalbec.c", "inventory_order" : 20, @@ -73,6 +65,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2004/jdalbec/index.html b/2004/jdalbec/index.html index 4096e3645..870eadd86 100644 --- a/2004/jdalbec/index.html +++ b/2004/jdalbec/index.html @@ -319,9 +319,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 2005 entry source
Jump to: top
diff --git a/2005/jetro/.entry.json b/2005/jetro/.entry.json index 731e9de0f..2c60952d7 100644 --- a/2005/jetro/.entry.json +++ b/2005/jetro/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Jetro_Lauha" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "jetro.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2005/jetro/index.html b/2005/jetro/index.html index 3069c3d70..46963785c 100644 --- a/2005/jetro/index.html +++ b/2005/jetro/index.html @@ -163,9 +163,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see: https://creativecommons.org/licenses/by-sa/4.0/
- + @@ -261,9 +261,9 @@ role="doc-backlink">↩︎Download IOCCC 2006 entry source
Jump to: top
diff --git a/2006/meyer/.entry.json b/2006/meyer/.entry.json index 0c2c3bd40..04f31dd0f 100644 --- a/2006/meyer/.entry.json +++ b/2006/meyer/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Raphael_Meyer" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "meyer.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2006/meyer/index.html b/2006/meyer/index.html index 3d19be96a..0dd863721 100644 --- a/2006/meyer/index.html +++ b/2006/meyer/index.html @@ -231,9 +231,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 2011 entry source
Jump to: top
diff --git a/2011/konno/.entry.json b/2011/konno/.entry.json index 7e9e40934..0c9436c4c 100644 --- a/2011/konno/.entry.json +++ b/2011/konno/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Taketo_Konno" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "konno.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2011/konno/index.html b/2011/konno/index.html index dcf5920a6..4acd03fc2 100644 --- a/2011/konno/index.html +++ b/2011/konno/index.html @@ -166,9 +166,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ ./deckmyn "`cat deckmyn.c`" "`cat musicfile`" > output.pbmThen display output.pbm.
This minimalistic music notator expects two command line arguments.
+The first is the font description embedded in the source code. Notice
+that you don’t pass the file name, but the actual content as one long
+character string! If you want to define your personal music font, you
+can of course do so. The file layout must be exactly the same as deckmyn.c though, only the locations of space
+(" ") may differ.
The second argument is the actual music. Again it is one long string, +not a file name. But it is possible to save your music in a file, of +course.
+Output should be redirected to a file. Afterwards, this file can be +visualised with various viewers.
+Every element in the music is given by a string of exactly 3 +characters. Any violation of this will result in the rest of the music +being interpreted in some randomly wrong way. Notice that in a file, the +newline is also a character! Therefore, when preparing music in a file, +care should be taken that the last musical element of a line is only 2 +characters!
+Basic notes: A single note is input by a letter (in the range
+[A-Ga-g]) followed by a duration (1, 2, 4 or 8) and
+possibly a dot (.). The notes cover 2 octaves, from
+C (lowest) to b (highest). The possible
+durations are 1 (whole note), 2 (half note), 4 (quaver), 8
+(semi-quaver). So a dotted quaver A followed by a semi
+quaver G is input as "A4.G8 ".
Rests: A rest is given by the letter r followed by a
+duration (1, 2, 4 or 8).
Bar lines: There are three possible bar line types: single
+(": "), double (":: ") and final
+(":; ").
Accidentals: Chromatic changes (sharp, flat, natural) are entered
+separately from the note that follows them! They are a three-character
+sequence by themselves. The first character is the chromatic change
+[+-=]. The second is the pitch at which it should be drawn.
+Thus "+G G4 " will draw a G sharp quaver. Notice that these
+signs do not necessarily have to be followed by a note.
+"+f +c +g B2 " is perfectly legal.
Space (formatting): To manually change the spacing of the music,
+you can add space between different musical elements. This is given by
+an s followed by the amount of space. "s3 " is
+the space taken by one note. This length is given in 1 character.
+However, to get a value larger than 9, you can just continue along the
+ASCII table. So "sz " gives you quite a lot of space on a
+staff. "s0 " is not only meaningless, it can cause nasty
+results.
New staff: A token "x " signifies to start a new
+staff of music. A music staff may never be empty! Even if you just put
+some blank space ("s1 "), you should always have an entry
+for every staff. "x x " may give nasty results.
All staves in the output are extended to the length of the longest +staff. To have nicely formatted music, though, you will probably have to +add spaces manually.
Time signatures: A time signature is given by
+[m][234][234]. For example, "m24" will give
+you a two fourth signature. Notice that there are only three available
+numbers. Especially “6” and “8” are dearly missed.
Clef: The clef (G or F) can only be
+changed at the beginning of the music input. If the music starts with
+"KF ", the music will be written in a bass clef.
+"K" followed by any other letter will just default to a
+standard G clef.
Blank music paper (with G clef):
+./deckmyn "`cat deckmyn.c`" "sz sa x s1 x s1 x s1 x s1 x s1 "> blank.pbmWith an F clef:
+./deckmyn "`cat deckmyn.c`" "KF sz sa x s1 x s1 x s1 x s1 x s1 "> blank.pbm
+Notice how only the first staff (or any staff) needs to have the full +length.
+Some files with well-known melodies are included as examples.
+ + + + + + + +
+ Qiming Hou
+hqm03ster@gmail.com
+http://www.houqiming.net
make hou
+./hou syntax-file file-to-process
+The results are best viewed in a 80x25 console with ANSI colors.
+less hou.c
+
+./hou ansi.txt hou.c | less -r
+
+./hou chk.txt hou.c | wc
+
+./hou markdown.txt remarks.markdown > remarks.htm
+your-browser remarks.htm
+copy header.htm hou.htm /y
+hou html.txt hou.c >> hou.htm
+start hou.htm
+What a versatile entry! It can be used to check the size of IOCCC +entries and to publish them as HTML.
+For extra credit: what is the meaning of the number 2321237?
+The reviewer may have already noticed that the 2nd page of this +program is a dense blob, which is discouraged by the contest rules. We +wish to point out that a majority of state-of-art programming editors +support syntax highlighting, which should be enabled when reading this +entry. Anticipating that the reviewer’s preferred color setting may +produce a suboptimal visual effect, a few syntax files are provided to +highlight the source code under an author-provided setting, using the +submitted program itself. Syntax highlighting would also visually +improve the 3rd page.
+The code may throw a few warnings due to formatting constraints. The +author did try to remove warnings under gcc and Visual Studio, +though.
+The author does not own the original anime series where the embedded +ASCII arts come from. For comparison, original non-ASCII art depictions +of the relevant characters and objects can be found by googling the +embedded text messages.
+The syntax file consists of a number of rewriting rules. Each rule +consists of a regular expression, a space, a format string, and a +newline. All text matching the regular expression would be replaced by +the format string. In case of a conflict, rules appearing earlier would +take precedence. One can refer to the original text in the format string +using “%s”. Other “%” characters in the format string must be escaped +with another “%”. The space character can be used in the regular +expression by escaping it with [] or ““.
+The following regular expression operators are supported:
+() [] * + ? | ""
+For example, one can use the following expression to match a certain +declaration statement in hou.c:
+"char"[ *]*[a-zA-Z_][0-9a-zA-Z_]*[ ]*((=[0-9a-zA-Z_ ]+)|(\[[0-9a-zA-Z_ ]*\]))?[ ]*(,[ *]*[a-zA-Z_][0-9a-zA-Z_]*[ ]*((=[0-9a-zA-Z_ ]+)|(\[[0-9a-zA-Z_ ]*\]))?[ ]*)*;
+The regex engine is also algorithmically efficient. To illustrate the +point, ansi.txt contains a pathological expression [2] that guarantees a +hang for the competing Perl engine while matching itself. Try to compare +these two engines:
+./hou ansi.txt ansi.txt
+perl patho.pl < ansi.txt
+Finally, there are a few limitations…
+The first layer of obfuscation comes from the challenge of embedding +a large ASCII art in a dense blob. The entire art is composed using only +keywords, strings and character constants. That results in a number of +otherwise useless defines and quite a number of unconventionally written +constants. While the preprocessor may remove the former, the later would +remain regardless of beautification and preprocessing.
+The second layer of obfuscation comes from the need to squeeze a +reasonably powerful regex engine into the remaining area that isn’t +occupied by excessive keywords and useless constants. To achieve this, +the regex compiler is written using the same threaded virtual machine +[1] that parses the regex. Due to compiler limitations, the generated VM +code also ends up obscured by a large amount of spaghetti branches and +virtual thread creations. Finally, the VM in [1] is extended to track +multiple expression matches for the actual formatting.
+As an extra tweak, the text message actually does something useful. +Remove the “Make a contract with me” catch line and the program would +cease to function.
+[1] Russ Cox, +Regular Expression Matching: the Virtual Machine Approach
+ +Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see: https://creativecommons.org/licenses/by-sa/4.0/
- + @@ -252,9 +252,9 @@ href="#fnref3" class="footnote-back" role="doc-backlink">↩︎Download IOCCC 2012 entry source
Jump to: top
diff --git a/2012/kang/.entry.json b/2012/kang/.entry.json index 9d4b59bbd..5c32c72cc 100644 --- a/2012/kang/.entry.json +++ b/2012/kang/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Seonghoon_Kang" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "kang.c", "inventory_order" : 20, @@ -105,6 +97,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2012/kang/index.html b/2012/kang/index.html index ad65b4c64..37c2a4ece 100644 --- a/2012/kang/index.html +++ b/2012/kang/index.html @@ -324,9 +324,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ The program implements a so called Krivine +machine, enhanced with a basic input/output system +and a reference counting garbage +collector. The Krivine machine is a stepwise transformer of a global +state consisting of the current term, the current environment (a list of +closures), and a stack of continuations (see the 3rd reference for +details).
+The term space holds lambda calculus +terms in the following format. 0 denotes some form of I/O; the only four +occurrences are at:
+ C=13: recognizes end of a byte
+ C=20: recognize output of 0 bit
+ C=21: recognize output of 1 bit
+ C=end: input list, expanded lazily
+1 denotes a variable, with the next entry its de Bruijn
+index.
2 denotes application with the next entry the size of
+the term being applied.
3 denotes lambda abstraction.
A closure consists of the following items:
+Here’s what each variable does:
+L Lambda term space
+m mode: 0 for bits and 7 for bytes
+b bits remaining unread in I, -1 on EOF
+D Desirable temporary
+c continuation context
+a active environment pointer
+C Current term index
+U Ultimate term pointer
+u utility entry
+B Born to be free-list of closures
+I Input character
+O Output character
+e entry of current term
+Here’s what each of the 6 auxiliary functions do:
+s variable lookup in active environment with de Bruijn index u
+S gets one bit of input, setting b to -1 on EOF or to remaining number of bits in current byte
+k copy fragment from string literal holding lambda terms xor 46 into current end of term space
+x expand input, adding 11 (resp. 99) entries to term space for bit (resp. byte) mode
+p parses BLC-encoded lambda term using S(), stores results in term space and returns length
+d decrease reference counter, add record to free list on reaching zero
+
+
+
+
+
+
+
+
+ The file must be encoded in UTF-8 as the BOF marker indicates.
+You can directly feed it to the renderer. If you’re wondering about +what the following code block does, that’s why:)
+@|?|?z#
+@|||?z&
+>?Sedvf
+??Tdeu#
+>>Tdeui
+??Tdmmf
+??[[euf
+?}?Oq}e
+?}?Oqqf
+v}?|q}e
+v}?|qqf
+?Sj|?{f
+?Tjk?{e
+FF?Oqq$
+RR?Oqq$
+^^?Oqq$
+jj?Oqq$
+vv?|qq$
+IOBMqqe
+U]BMqqe
+aiBMqqe
+muBMqqe
+EEEJpp%
+QQEJpp%
+]]EJpp%
+iiEJpp%
+uuEJpp%
+ZnY|{|e
+[mZ|\{\{#
+[mZ|||f
+kkdr\{\{\%
+lkdr\{\{>
+A scene file begins with arbitrary binary data, followed by several +shader functions.
+Shader functions are declared using a one-character name followed by
+a ] character. There are some special function names, which
+will be explained below.
The language used to write shader functions is very simple. +Basically, you write a sequence of assignment statements like this:
+A=A+D
+B=B+E
+Namely, you write a one-character variable name, an equal sign
+(=), followed by an expression (made of operators and
+one-character variable names, of course).
The shader language supports seven operators, listed highest priority +first:
+*/+-<#:
+*/+-< each mean what they mean in C, but the priority
+part is a bit different. Basically, * has higher priority
+than /, so A/4*4 actually computes
+A/(4*4). And you can’t write (A/4)*4 –
+brackets are not supported. As an advice, keep your expressions simple
+:) If you really needs to write something like C=(A/4)*4,
+write instead:
B=A/4+
+C=B*4
+The # operator acts somewhat like the C ?:.
+It returns one of its two operands based on, what else, the value of
+variable #. When # is true, #
+returns its second operand.
The : operator calls built-in functions. The first
+operand is the function id and the second is the parameter. Here is a
+list of functions:
0:x = the x-th byte of the scene file
+1:x = sin(x*PI)
+2:x = atan2(x,#)/PI
+3:x = sqrt(max(x,0))
+4:x = floor(x)
+5:x = the next number in the (allegedly random) number sequence, can't be called in volume shaders
+6:x = value of the variable (char)(120+x)
+7:x = write #'s value to the variable (char)(120+x)
+Contrary to popular belief, the language itself doesn’t support
+numerical constants. Any character sitting between operators is a
+variable. When you see 1/2, it’s in fact dividing the variable 1’s value
+by the variable 2’s value. For everyone’s convenience, though, in the
+beginning every variable is initialized to its ASCII code minus 48. As
+long as you don’t write something like 3=1+1, you can use
+single digit numbers as, well, single digit numbers. If you need
+something like 1920 or 0.73, you need to
+compute it yourself. And yes, all variables store floating point
+values.
If a character appears as the first character of any
+argv, it goes to zero. This includes the scene filename. If
+you name your scene 1.scene, well, good luck with
+1=0!
You can terminate a statement using any character that isn’t an
+operator, be it \n or ;. But you must put in
+one and only one character. Put in two (e.g. the Windows
+\r\n), and it terminates your function. Also, the first
+statement must begin immediately after ].
When a function terminates, the interpreter calls the next function
+specified by variable R. If you want to call function
+n, just write somewhere:
R=n
+providing you have left n at its initial value. If you
+want to actually return to the renderer, put in R=0.
As you may have noticed, you can put in all kinds of stuff between +functions, like this comment.
+Basically, you define a geometry using volume shaders, define a +camera using a camera shader, and define light sources and surface +reflectance properties using material shaders.
+| Continue to the comments before function +A | +
//As an (optional) assignment, figure out what function 'n' does. And why there is a plus sign at every EOL except the last.
+n]#=1+
+x=X+1/2+
+d=0:U;d=d-n;U=U+1;A=d<x;#=#*A+
+d=0:U;d=d-n;U=U+1;A=x<d+1;#=#*A+
+x=Y+1/2+
+d=0:U;d=d-n;U=U+1;A=d<x;#=#*A+
+d=0:U;d=d-n;U=U+1;A=x<d+1;#=#*A+
+x=Z+1/2+
+d=0:U;d=d-n;U=U+1;A=d<x;#=#*A+
+d=0:U;d=d-n;U=U+1;A=x<d+1;#=#*A+
+d=0:U;d=d-n;U=U+1+
+U=U+2+
+C=C#d+3*9+
+#=d+
+R=0#n
+The function A initializes global settings:
G -- Image width, in pixels
+H -- Image height, in pixels
+I -- Perturbation probability
+J -- Camera ray stratification probability
+If you have used a MLT renderer before, you probably know what
+I and J does. If you don’t, well, use the same
+value as the default scene. Everything converges just fine under
+suboptimal settings, just a little slower. Generally, use a higher value
+for I if your scene is primarily illuminated by some
+indirect source.
A also serves as the geometry shader for volume 0. The
+scene geometry is made of up to 32 volumes, each with 64x64x64 cells.
+The volume 0 is the ‘top level’ volume where the camera goes. Each cell
+of a volume can be empty, filled with a material, or another volume. The
+roundish characters in old_default.scene and the carpet on the
+floor in this file are examples where a cell is defined to another
+volume. Materials will be explained later.
The volume shader for the i-th volume is named as the (i+1)-th
+capital letter. Each volume shader is invoked once for each cell. The
+cell coordinates (0..63) are given as inputs in variable
+x,y,z, and you’re supposed to
+return the cell content in variable C. To fill it with
+material i, return 64+i in C. To
+fill it with volume i, well, return i. To
+leave it empty, return C=0.
Before starting to build a scene, it’s better to have a draft sketch
+handy first. Something like
.
+Sometimes it’s hard to imagine where something should go without the
+sketch. And you don’t have to stick to your sketch :)
What motivated my default scene: it was the full moon’s day (a +national holiday) in China and my parents were planning to renovate +their apartment. I rendered the image to tell my mom how ridiculous her +starry-sky wall paper would look like if she used it on every single +wall. She ends up using it on just one wall, the one facing my bed, to +thank me for the effort.
+| Continue to the comments before function +a | +
//As a bonus, find out what the first line does
+A]#=#-#;"=7:0-p+^;#="+
+R=n+
+G=p*:+
+#=0<B+
+G=G*2#G+
+H=G*3/4+
+I=4/5+
+J=1/2+
+C=p+1+
+U=w+w+w
+The function a is the camera shader, which, well,
+generates the camera ray. It’s supposed to return the ray in variables
+UVWXYZ and ABCDEF (that may sound like a lot
+of variables, but it really isn’t that much).
Here comes some subtlety of PSSMLT (Primary Sample Space
+Metropolis Light Transport, or Kelemen-style MLT). Basically, a
+PSSMLT renderer gives you an (allegedly random) number
+sequence, and you compute a color out of it. This is usually done by
+generating and tracing rays out of those random numbers. The renderer
+would assemble the colors into an image.
Remember the function 5? Here it comes. The first two
+“random” numbers have special meaning and must specify a point on the
+image plane [0,1)x[0,1). You’re supposed to transform the
+image plane to the 3D space, choose a point for the camera, and shoot a
+ray from the camera point to the image plane point, see . The camera point goes to
+
(U,V,W), and the ray direction vector (image plane point -
+camera point) is (X,Y,Z). For the coordinate space, volume
+0 is scaled up 64x here to (0,0,0)-(4096,4096,4096).
For a basic camera shader example, check otherroom.scene.
+If any ray goes out of volume 0, the renderer crashes. Make sure it +doesn’t happen. Wall up everything.
+Variables ABCDEF control the color returned from the
+ray. They will be explained in the material shaders part. For now, just
+put in:
A=0;B=0;C=0
+D=1;E=1;F=1
+| Continue to the comments before function +1 | +
a]R=0+
+X=5:5+
+Y=5:5+
+X=X-1/2+
+Y=Y-1/2+
+x=5:5+
+y=5:5+
+x=x-1/2+
+y=y-1/2+
+x=x*8*7;y=y*8*7+
+X=X*G+
+Y=Y*H+
+Z=H*2/3+
+d=4*;+
+d=d*p/Z+
+X=x+X*d+
+Y=y+Y*d+
+Z=Z*d+
+U=p*p/2-p*2+x+
+V=p*p/2+p*1-y+
+W=p*8+
+A=0;B=0;C=0+
+D=1;E=1;F=1
+The numbered functions define materials, i.e., surface reflectance
+properties. As the name suggests, the shader simulates ray-surface
+interaction. It’s done by updating UVWXYZABCDEF, of
+course.
ABCDEF define colors. (A,B,C) is the RGB
+irradiance ultimately returned to the renderer, and (D,E,F)
+maintain the cumulative RGB reflectance of the current ray path. This
+may sound confusing, but basically, if the surface is a light source,
+you accumulate the current path reflectance (D,E,F),
+multiplied by the light strength, to (A,B,C). See shader 6
+for an example.
If you want a colored surface, use its color to modulate
+(D,E,F), i.e. D=D*red;E=E*green;F=F*blue, easy
+as that. To get some texturing, compute the color procedurally or look
+up a binary table.
There’s something subtle about colors. To go along with the trend,
+the renderer follows the practice of linear rendering with a gamma of
+2. Stripping the fancy jargon, that basically means after
+picking a color in your favorite image editing program, you need to
+convert the RGB values from 0..255 to 0..1, and then put here its
+square. Only after squaring you get your real, lively color in
+the output image, as opposed to something dull and gray. You need to do
+the same if you use a RGB texture. And yes, that is the what the second
+sqrt(3) in a.c is for.
Going beyond colors, to define a shiny or dull surface one needs more
+maths, and optics. The jargon for this is BRDF
+(Bidirectional Reflectance Distribution Function) importance sampling.
+Let’s continue to function o.
| Continue to the comments before function +o | +
//This function computes the starred wallpaper
+//Original color: 0.36 0.714285 0.8
+1]R=o+
+d=p*6+
+x=U+W+
+x=4:x/d+
+z=4:V/d+
+#=3*x-4*z+
+y=4:#/5+
+#=#-y*5+
+y=1#0+
+x=x*d+d/2-U+W+
+z=z*d+d/2-V+
+r=3:x*x+z*z+
+#=z+
+u=2:x+
+d=1:u*5+
+d=d*d*d+3/2+
+d=d*p+
+#=r<d+
+#=#*y+
+d=2/5#I+
+D=D*d+
+E=E*4/7+
+d=I#2/5+
+F=F*d
+Function o defines a diffuse (that is, dull) surface. To
+define reflectance properties like this, one needs to sample an outgoing
+ray direction, based on the incoming direction and the surface normal.
+It’s called importance sampling, because here the probability of each
+outgoing direction has to be proportional to the reflected light
+strength (i.e., the BRDF) on that direction. For a diffuse
+surface, sample the upper hemisphere with a probability proportional to
+the cosine of the outgoing angle, see
.
The incoming direction is in (X,Y,Z) and the hit point
+is (U,V,W). The outgoing direction is returned in, where
+else, (X,Y,Z).
The surface normal is provided in variables S and
+T. Since the geometry is defined as a bunch of cube-shaped
+cells, there are only 6 possible normals:
+(±1,0,0) (0,±1,0) (0,0,±1). Here S is the sign
+and T is the dimension. If you get S=-1 and
+T=1, the normal is (0,-1,0).
If you don’t want to redo all the math yourself… this file provides a +few simple shader functions:
+o -- Diffuse surface. Do the color thing your self and call R=o
+5 -- Perfect mirror.
+f -- Surface with a Fresnel term. Basically, it resembles a polished surface made from a dielectric material.
+For something more complex like a fluffy carpet, well, you could use
+a volume to directly model the mesoscopic
+detail. Shader D in this file produces a bunch of dense,
+tiny spikes to simulate a fluffy appearance.
| Continue to the EOF | +
//diffuse ray generation
+o]R=0+
+u=5:5+
+v=5:5+
+v=v*2+
+r=3:u+
+z=1:v+
+y=1:1/2+v+
+z=z*r+
+y=y*r+
+x=3:1-u+
+x=0-x*S+
+X=6:T+
+w=x;x=z;z=y;y=w+
+Y=6:T+
+w=x;x=z;z=y;y=w+
+Z=6:T
+
+//reflector
+5]R=0+
+x=1;y=1;z=1+
+#=0-1+
+#=7:T+
+X=x*X;Y=y*Y;Z=z*Z
+
+//approximate Fresnel, works better than the real thing when you don't want to tweak the IOR
+f]x=X;y=Y;z=Z+
+r=3:x*x+z*z+y*y+
+d=6:T+
+d=1-d*S/r+
+d=d*d*d+
+#=5:5+
+#=d<#+
+R=5#o+
+d=d#1-d+
+u=1#u+
+v=1#v+
+w=1#w+
+D=D*d*u+
+E=E*d*v+
+F=F*d*w
+
+//the handles
+B]R=0+
+#=X<8*3+
+C=3#0
+
+C]R=0+
+#=X<8*4+
+C=0#4
+
+//red-Fresnel
+2]R=f+
+u=1/2;v=1/9;w=v
+
+//white-Fresnel and the full moon
+3]R=f+
+u=7*9/p;v=u;w=v+
+x=3*:+8+
+z=U-x*p+p/2+
+x=V-x*p-p/2+
+r=3:x*x+z*z+
+#=4<r/p+
+D=D*I#D+
+E=E*4/7#E+
+F=F*2/5#F+
+d=1/5#0+
+A=A+D*d+
+B=B+E*d+
+C=C+F*d
+
+//handle
+4]R=5+
+D=D*I;E=E*4/7;F=F*2/5
+
+//light from the window
+6]R=0+
+#=P+
+d=2#8*2+
+A=A+D*d+
+B=B+E*d+
+C=C+F*d*I+
+P=9
+
+//carpet
+7]R=o+
+D=D/7+
+E=E/3+
+F=F/7
+
+D]R=0+
+#=8<Y+
+C=0#3+
+#=4:3*X-4*Z+
+y=4:#/7+
+#=#-y*7+
+C=7#C
+The last line, well, is the output filename. Just remember to throw
+in two ]s and don’t put a newline after it.
]]example.ppm
+
+
+
+
+
+
+
+ Download IOCCC 2013 entry source
Jump to: top
diff --git a/2013/mills/.entry.json b/2013/mills/.entry.json index 6bec1ca53..956ad38d4 100644 --- a/2013/mills/.entry.json +++ b/2013/mills/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Christopher_Mills" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "mills.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2013/mills/index.html b/2013/mills/index.html index 1c7ea6e89..34963cdd0 100644 --- a/2013/mills/index.html +++ b/2013/mills/index.html @@ -244,9 +244,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ As you may know, QR code (specification: ISO/IEC 18004) is quite +complex. It requires a bit stream, Reed-Solomon codes, BCH codes, data +relocation, big (and irregular!) numerical tables, human-oriented +pattern arrangement (difficult to implement by a small program), +etc.
+On the other hand, a quine has a big restriction: it needs to have +two copies of the program!
+Do you think that it is possible to write QR code encoder in C within +1kB?
+It is known that we can mitigate the quine restriction by
+eval. For example, this is a quine written in Ruby:
eval s="print 'eval s='; p s"Note that print appears just once in the program.
To emulate this approach in C, I adopted the following structure.
+char s[]="<main code>int main() { <VM code> }";
+int main() { <VM code>; }
+<VM code> : An own VM implementation (written in
+C) that executes the main code.<main code> : An object code of quine and QR code
+encoder (written in the assembly language of the VM).<main code> (about 1100 bytes) appears once and
+<VM code> (about 450 bytes) appears twice. So the
+whole code is about 1100 + 450 * 2 = 2000 bytes. Hooray!
The VM is a kind of stack machine which makes the object code
+compact. The instruction set is carefully selected to minimize the sizes
+of both <main code> and
+<VM code>.
The instructions have variable lengths, but many of them are
+represented as one printable character. (See machine.rb and assemble.rb in detail.) The mapping between
+instructions and characters is also carefully designed to minimize
+<VM code>, which makes the resulted program
+non-human-readable.
It is too tedious to write the machine language directly. So I wrote +the source code in Ruby syntax and created a Ruby-to-VM compiler that +involves alpha renaming and automatic register allocation only for this +purpose.
+The compiler tool chain is the following.
+src.rb interp.c
+ | |
+ | [parse.rb] |
+ V |
+ast.txt |
+ | |
+ | [serialize.rb] |
+ V |
+seq.txt |
+ | |
+ | [alpha.rb] |
+ V |
+alpha.txt |
+ | |
+ | [register.rb] |
+ V |
+reg.txt |
+ | |
+ | [assemble.rb] |
+ V |
+raw.txt |
+ | |
+ +---------+---------+
+ |
+ | [link.rb]
+ V
+ main.c
+ |
+ | [gen-prog.c]
+ V
+ prog.c
+main.c by
+linking the instructions and interpreter source.main.c.You can run the process by rake command.
+ruby-minisat gem is needed.
$ gem install ruby-minisat
+$ rakeI checked that the following QR code readers can read the output of +my program.
+The author also tested an Android app called Barcode Scanner which
+used to be found at
+https://play.google.com/store/apps/details?id=com.google.zxing.client.android
+but it appears to no longer exist.
You can use tool.rb to convert the output to a +png file.
+./main Hello | ruby tool.rb > hello.png
+
+$ zbarimg -q hello.png
+QR-Code:Hello
+
+$ ruby tool.rb prog.c
+$ ruby tool.rb prog.c | ruby tool.rbDownload IOCCC 2014 entry source
Jump to: top
diff --git a/2014/maffiodo1/.entry.json b/2014/maffiodo1/.entry.json index 0516ff5b8..75d02c583 100644 --- a/2014/maffiodo1/.entry.json +++ b/2014/maffiodo1/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Sandro_Maffiodo" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "prog.c", "inventory_order" : 20, @@ -113,6 +105,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2014/maffiodo1/index.html b/2014/maffiodo1/index.html index e7a6d4397..dec4722ba 100644 --- a/2014/maffiodo1/index.html +++ b/2014/maffiodo1/index.html @@ -398,9 +398,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ I wanted to enter the IOCCC….
+I think I can write something clever. Possibly even obscure. The +competition is fierce, but not a closed door.
+I needed an interesting program, that had a reasonable chance of
+being shrunk within 4096 bytes, and lent itself to obfuscation. I had
+been revisiting utilities I’ve written over the years. One useful
+program was calc, a simple recursive descent signed integer
+expression parser I developed from first principles as a demonstration
+of how computers work for my son. That program did the basic four
+operations, but as a programmer, I needed more, so those were added over
+time.
calc is a useful, functioning utility. The operators
+before the obfuscation dance began (this is called foreshadowing):
() forced precedence
+- ~ ? unary minus, compl, byteswap
+* / % & << >> mul, div, mod, bitand, shift: left, right
++ - | ^ add, sub, bitor, xor
+NUM 0-9
+This seemed like a reasonable candidate to experiment with +obfuscation; perhaps it might yield good results.
+The algorithm was and is rather straightforward:
+expr() {
+ n = term()
+ t = token()
+ if (t == ADD) return n += expr()
+ if (t == SUB) return n -= expr()
+ if (t == BOR) return n |= expr()
+ if (t == XOR) return n ^= expr()
+ ungettoken(t)
+ return n
+}
+term() {
+ n = factor()
+ t = token()
+ if (t == MUL) return n *= term()
+ if (t == DIV) return n /= term()
+ if (t == AND) return n &= term()
+ if (t == RSH) return n >> term()
+ if (t == LSH) return n << term()
+ ungettoken(t)
+ return n
+}
+factor() {
+ t = token()
+ if (t == LPAREN) {
+ n = expr()
+ if token() == RPAREN
+ return n
+ error()
+ }
+ if (t == SUB) return -factor()
+ if (t == CMP) return ~factor()
+ if (t == QUE) return byteswap(factor())
+ if (t == DIG) return strtol()
+ error()
+}
+Within the above algorithm, there are many details unmentioned, all +ripe for obfuscation:
+atoi(3) input valuessprintf(3) resultsstdin)Uncertain as to how this would progress, I started
+obfuscating/condensing everything except the conversion operations,
+leaving those to sprintf(3) and strtol(3). The
+first version of the obfuscated code used an homage to 1984 anon for
+I/O, and it featured a #define for elses and
+the types, to help minimize the repetition. And of course, most
+functions were renamed with one or two letters, and most of the
+variables became single letter globals. Tokenization was rather easy
+with a declared array, just initialized with unique values for each.
This was when the obfuscation took a turn for the better (worse): it +was easy to hide the operations via a simple transformation. The code +now dealt with small integer values instead of direct ASCII +representation of the operations. And the strings were collapsed into a +single combined error message and operator tokenization table. This was +relatively obscure.
+Next, sprintf(3) was replaced with a custom formatter.
+This was rather simple, but at least the formatting was not obvious.
+Formatting and printing were split early. strtol(3) was
+replaced with custom logic, limited to 3 practical bases: octal,
+decimal, and hex. The last result was remembered in a special variable,
+dot.
By this time, I had a version which I considered obscure, and shared
+this early version with a couple of colleagues for comments. I suggested
+I could improve the obfuscation of a term() and
+factor(), and would like to have assignable memories.
This code was pretty dense, but inspired by previous IOCCC entries, I
+thought I might get rid of all the elses, which showed the
+structure of the parser rather clearly. Investing in the ternary
+operator, the code got quite a bit more difficult to follow, used only
+for loops, and I had to start taking notes to follow it.
+This was a good sign! ;-)
I started testing the code. I wrote a test suite to ensure the
+changes I made did not corrupt the processing (oh so easy to do with
+this obfuscated mess). Why does -1>>32 yield
+-1? Oh, yeah: signed arithmetic. Enter Java’s
+unsigned shift: >>>. Better. But with
+more features, more to get wrong, and harder to know which expression is
+causing the error. The easy solution: if the name begins with
+e, echo the input. Now I can see the failing expression.
+Wait, what is this test expression supposed to be testing?
Wouldn’t it be nice to have comments?
+Adding new functionality to an obscure program is … going about it +backwards. Get the functionality correct and complete, THEN obscure. +Sounds obvious, but that did not happen. I got comments working, then +desired to have multiple statements on a single line (read: semicolon +expression terminators). This involved quite a bit of rework of the +input code, but now allowed very convenient user interaction.
+Satisfied with the functionality, but unsatisfied with the obscurity, +I pondered if I could get rid of all the constants? This had not been +done before (to my knowledge), and I was able to work out the how-tos +and the compromises. Now I can no longer declare my arrays, I have to +allocate them. Obscurity now increased!
+But wouldn’t it be nice to have assignable memories?
+Around this time, the formatted code no longer fit with the 2053 +limit (due to a lack of comprehension of the significance of the +space-after-brace-and-semi rule, and a layout program that did not know +how to do this). Frustrated, I wrote another program to reformat this +code into rule2.c, which was a single C token per +line. This solved the formatting problem, but intriguingly introduced +another. Now I was faced with the 4096 rule as well as the 2053 rule. +But this was a challenge!
+I wrote a de-obfuscator, and worked on both the unobfuscated version +and the master code. I worked on the template formatter to make this as +automated as possible, and worked to achieve both a perfectly presented +picture AND the single-line exactly-at-4096 rule2.c.
+Finally, “perfection”, with assignable memories! Where perfection is +defined as a nicely formatted picture at less than 2053, and exactly +4096 token-per-line rule2.c.
+Cross platform, it works on Mac OSX, fails on Linux. It works if
+#include <stdlib.h>, but it dumps core without it
+(the only change!)?. 64-bits without proper declaration of
+ints in syscalls yields unpredictable results. 32-bit
+compilation showed problems with byte-swapping in using addition versus
+bitwise-or. In 32-bit land, x<<32 == x, so
+x<<32 + x>>32 does not yield the desired
+result, but x<<32 | x>>32 does (with
+appropriate care and feeding). And the different compilers on different
+hosts complained loudly and differently, although all produced correct
+bits in spite of the noise.
Further, the program did not do UNIX pipelines well, due to the
+unbuffered homage code (byte-at-a-time I/O), of which
+write(2) was also the source of the portability problem.
+Replace write(2) with
+getchar(3)/putchar(3), and it piped well but
+lost some obscurity and no longer paid (direct) homage to history.
Another problem: only one expression at a time was allowed on the
+command line, no semis, no comments, and it turns out $2,
+$3, … were not handled correctly. Given the new I/O code,
+it was now possible to copy the command line arguments into a buffer,
+and write a small routine to retrieve bytes from this buffer like
+getchar(3). Voila! Better obfuscation with much better
+functionality!
But wouldn’t it be nice to show what the current variables hold?
+Bytes are now hard to find to keep exactly at 4096, and adding +functionality to obscured code is not easy. Restructuring the logic to +gain the bytes necessary, code to display the variables was added.
+Test. Test. Test. Oh, noes! More bugs! Typing “1 + 2 5”
+yields 3, but no syntax error. “g = 1” yields
+a syntax error, and “0xg” does not! “)” causes
+a segfault! Why didn’t I test these before I started obfuscating?! By
+this point, everything I did to the code perturbed the 4096 bytes, all
+variables were already a single character, and it was really difficult
+to follow the logic in a debugger (single-stepping an expression did the
+entire line of code, even if it involved multiple statements – and it
+did, everywhere, even in the unobfuscated version; think: a complex
+chain of ternary expressions is a single statement). This involved
+careful examination of all the expressions within the logic, trying to
+find a different way of getting the same result, but typically within
+fewer bytes than before to allow for the added logic to fix the
+bugs.
The bugs were fixed, the code re-formatted, and rule2.c was again 4096 bytes.
+But wouldn’t it be nice for unary plus to work?
+Add unary plus, re-balance. Little things now take a fair bit of +work. Adding anything requires taking away in others, but since all the +logic is functional, this means rewriting the logic to be more concise. +After several rounds of this, bytes are scarce to come by.
+But wouldn’t it be nice for assignments to be silent?
+Made them silent (easier said than done!). Why does
+“1 + 2 5” print a syntax error, but
+“a = 1 + 2 5” does not? Rewrite(!) the assignment logic –
+and this requires more bytes!
Except there were not enough bytes to do this, unless
+#include <unistd.h> is removed. But then
+sbrk() must be declared, eating into the savings. If only
+there were 512 bytes of memory already allocated that could be portably
+touched for free….
Light bulb! Memory allocation was excised, and the code became more +obscure.
+It was working again! It formats beautifully, it compiled cleanly
+(with apropos -Wno-guards), it remains obscure after
+cpp(1) and clang-format(1), it measures within
+the limits and exactly at 4096 (this latter was really getting
+challenging to maintain). No more bugs known, it is feature complete,
+documentation written.
But wouldn’t it be nice to have a logical not?
+Sigh. This was the last operation added. Another round of code churn, +expressions are difficult to rewrite because they are both obscure and +relatively tight. But changing around the stable initialization code – +by now the largest piece of logic – yielded the necessary bytes, and +improved obscurity by double duty encoding of the operations +initialization.
+The utility is now truly functional, and feature complete, and +obscure. Without src.doc.txt (basic +documentation of the code structure), and some commented expanded logic +stashed away, and the familiarity I have with the code having written +and revised it repeatedly, I would not even want to attempt to decipher +the logic. Many times in the debugging, I was thwarted by my own +obfuscations, and went down blind paths in trying to fix a bug or add +functionality. Now I think I have a good entry.
+But wouldn’t it be nice to import pre-set definitions from a +file?
+ + + + + + + +
+ calc is a recursive-descent parser. It allocates from
+the stack deep enough that all expressions are exhausted before the
+recursion stack depth reaches the allocations. The memory map and source
+is documented in src.doc.txt. Tokens are
+encoded using values 0-20, where 0 is an invalid token, 1 is minus, 2 is
+space, 3 is plus, etc. The necessary constants are derived at the start
+of the program, using the guarantee that globals are initialized to
+zero; so a correct NULL pointer is obtained from an uninitialized global
+pointer variable, and the integers from uninitialized global
+int variables. If x is a global
+int, then !x is 1, and the
+derivation proceeds from there, using some identities that are
+confusingly similar and interestingly combined (increasing
+obfuscation):
obc clr
+--- ---
+-~x x+1
+~-x x-1
+y-~x y+x+1
+The derived constants are a compromise between simple expressions, +maximum utility, and minimum number of variables used.
+Command line arguments are copied into the upper half of the input
+buffer, separated by a space. This allows the code to have a single
+input parser by using a pointer-to-func to either
+getchar(3) if live, or a getchar(3)
+simulator.
A small amount of obfuscation comes from void functions
+declared int, but missing return. This is
+necessary to allow these functions in ternary ?:
+expressions and paired with && and ||;
+but the requisite returns would add symbol count at no benefit to
+obscurity or functionality. Function decomposition was mostly chosen to
+avoid curly braces: if a for-loop required multiple
+statements, the body became a new function. Last, but not least: sharing
+state via global variables is an ancient and honored form of
+obfuscation, and remarkably effective.
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see: https://creativecommons.org/licenses/by-sa/4.0/
- + @@ -349,9 +349,9 @@ class="footnote-back" role="doc-backlink">↩︎Download IOCCC 2015 entry source
Jump to: top
diff --git a/2015/mills1/.entry.json b/2015/mills1/.entry.json index 836e078d2..263502721 100644 --- a/2015/mills1/.entry.json +++ b/2015/mills1/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Christopher_Mills" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "prog.c", "inventory_order" : 20, @@ -57,6 +49,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2015/mills1/index.html b/2015/mills1/index.html index ba13050c0..66d87ef48 100644 --- a/2015/mills1/index.html +++ b/2015/mills1/index.html @@ -267,9 +267,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ The values below are the field-by-field difference
+iocccsize - ioc. (>0 means
+iocccsize counted more than ioc,
+<0 ioc counted more)
cl: lines, cw: words, cc: chars, ci: rule2, ts: saved, kw: keywords, ks: kw_saved
+
+cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1986/pawka.c
+cl -45 cw -55 cc -617 ci -463 ts -85 kw -26 ks -69 src/obc/1990/theorem/theorem.c
+ioc is correct; cl is correct even in compat (91) due to
+comments eating newlines iocccsize fails at
+z;/*/, eating everything until the following
+/**/main( looks to be due to mis-identification of a
+comment closing block as opening block
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1991/westley/westley.c
+cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1992/gmariano/gmariano.c
+cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1992/gmariano/gmariano.orig.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1994/shapiro/shapiro.c
+cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1996/gandalf/gandalf.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1996/schweikh1/schweikh1.c
+cl 0 cw -2 cc 0 ci -2 ts 0 kw 1 ks 2 src/obc/1998/df/df.c
+iocccsize counts as keyword __##int##__,
+and saving two characters but int is not a plausible keyword
cl 0 cw -1 cc 3 ci 2 ts 1 kw 0 ks 0 src/obc/2001/herrmann1/herrmann1.c
+the text:
+ d M Y(O)/* state to */
+ d
+counts as cw 3 in iocccsize, but this is clearly 4 and the cc,ci,ts +is due entirely to:
+ /*/*/*/*/*/ /*/*/
+ E E /*/*/*/*/*/,/) F
+which reduces to:
+ * E E * ,/) F
+but iocccsize counts this as * E E *,/) F, whereas ANSI
+C says comments are replaced with a single space equivalent
+(non-portable token-paste with comments)
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/arachnid/arachnid.c
+cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/arachnid/arachnid.orig.c
+cl 0 cw 0 cc 0 ci -4 ts 0 kw 2 ks 4 src/obc/2004/hoyle/hoyle.c
+cl 0 cw 0 cc 0 ci -4 ts 0 kw 2 ks 4 src/obc/2004/hoyle/hoyle.orig.c
+iocccsize counts as two keywords the token-pasted
+int##if(e.g int1) thus the kw 2, and another
+bug in iocccsize counts #if as a keyword to 1
+thus saving 2 bytes on int, and 2 more on #if, when neither
+is a plausible keyword
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/omoikane/omoikane.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2005/chia/chia.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2005/mikeash/mikeash.c
+cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo.c
+cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo2.c
+cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo3.c
+these three discrepancies are due to backslash-newline of
+longjm\p – at exactly the 521 byte line length boundary
+(which is arbitrary) ioc is correct, because it has no line
+length limit
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2011/akari/akari.c
+cl 0 cw -7 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2011/hamaji/hamaji.c
+cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1.c
+cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1_color.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1_deobfuscate.c
+cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/omoikane/nyaruko.c
+cl 0 cw 0 cc 0 ci -3 ts 0 kw 1 ks 3 src/obc/2013/cable2/cable2.c
+this is due to token-pasting of _##char;
+iocccsize counts this as a keyword but it is a
+pre-processor token-pasted string of the arg to Y() and
+char rendering this not a keyword, even in disguise
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2013/endoh4/endoh4.c
+cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2013/misaka/misaka.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/endoh2/prog.c
+cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/maffiodo1/prog.c
+cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/maffiodo1/prog.orig.c
+cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/sinon/prog.c
+
+
+
+
+
+
+
+
+ What kind of crazy idea is this? Install an IOCCC entry? I realise it +seems odd (at best) so why did I make a rpm spec file and why did I hint +at building RPMs for others? I can’t say precisely; certainly +I’m under no impression that anyone else will want to use it +but the entry was fun enough for me that I actually will (a +number of my programs over the years are programs I actually install and +I already have done so with this entry).
+I already have my own RPM repository and initially after having won I +set it up so that I could host the weasel rpm (binary and source) there. +At the same time though I feel the entry belongs on the IOCCC website; +that is to say the only place the raw files should be downloadable from +is the IOCCC website itself. Simon Cooper (the judge who at the time +dealt with setting up the entries on the IOCCC website) asked me if +there is any real attachment in having the RPM built as part of the +entry and the answer is ‘no’. He did suggest too that if I were to have +the RPM hosted on the IOCCC website not only would it place a burden on +me to keep it up to date (not that it ever needed to be updated) but it +would place a burden on the judges. This is no longer true with GitHub +but anyway since this was for personal reasons I decided that:
+I would not host the actual source code or scripts on my website; +they will remain on the IOCCC website. This will most likely never +change; instead I would link to the entry directory so that they remain +where they belong: the IOCCC website itself. I have set up an errata page +however should there be anything requiring a fix.
I am keeping the weasel.spec file in the entry as it was part of
+my submission but I do not explain here how to set up the build
+environment or how to build a RPM: if you know how to use
+rpmbuild then you should be able to work
+it out. Again that’s a crazy idea but again it was there and it was part
+of the judges’ comments (which I liked a lot) which is yet another
+reason I kept it.
Because I strongly suspect (in other words since I know) I am the +only one who has any desire for actually installing an IOCCC entry I did +not do anything further. Nevertheless more information on my entry can +be found at https://ioccc.xexyl.net/2018/weasel.
Download IOCCC 2018 entry source
Jump to: top
diff --git a/2018/mills/.entry.json b/2018/mills/.entry.json index 5a888aa7d..79c4077b8 100644 --- a/2018/mills/.entry.json +++ b/2018/mills/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Christopher_Mills" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "prog.c", "inventory_order" : 20, @@ -49,6 +41,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2018/mills/index.html b/2018/mills/index.html index e079410cb..2b7546f22 100644 --- a/2018/mills/index.html +++ b/2018/mills/index.html @@ -862,9 +862,9 @@ https://creativecommons.org/licenses/by-sa/4.0/Download IOCCC 2019 entry source
Jump to: top
diff --git a/2019/karns/.entry.json b/2019/karns/.entry.json index 3038e2cf5..04fcf9466 100644 --- a/2019/karns/.entry.json +++ b/2019/karns/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Joshua_Karns" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "prog.c", "inventory_order" : 20, @@ -65,6 +57,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2019/karns/index.html b/2019/karns/index.html index dca79737a..a425f8efd 100644 --- a/2019/karns/index.html +++ b/2019/karns/index.html @@ -211,9 +211,9 @@ https://creativecommons.org/licenses/by-sa/4.0/
+ The program contains dedicated font data for all uppercase alphabet +letters.
+The source code of the font is glyphs.txt,
+which is a sequence of chunks that is a ASCII code (65-90) followed by
+points data: x,y,ctrl.
If ctrl is 1, the point is used as control point for
+quadratic bezier curve.
code.c is the main logic for the program, which +has some placeholders for font data and something.
+It decompresses the font data and renders a given text by projection.
+It exploits C99’s double complex type to
+represent 2D points, but carefully coded so that it does not require any
+math.h functions; you don’t have to pass -lm
+to cc.
tmpl.txt is a template for the program. gen.rb composes the all items and generates prog.c.
+ + + + + + + +
+ In order to successfully compile this you will need a C compiler that
+supports C99+ and ncurses; note that for some platforms (e.g. Red Hat
+based Linux) you will need two different packages: the library itself
+but also the dev package that has the header files etc. If you use the
+Makefile obviously you need make too.
In gameplay.md I show how to easily +customise the colours, set up different gameplay modes and various other +things. The troubleshooting.md and terminals.md as well as bugs.md +include a variety of information should you run into trouble.
+–
+I have tested this under macOS Catalina, macOS Ventura (and probably +between the two), Fedora (29 and also 31 - no that’s not a typo, I +upgraded from 29 to 31 as I had delayed upgrading to 30 due to a number +of issues and being busy) and CentOS 7.7.1908. It should work under any +modern linux system and maybe other Unices as well as macOS (as +noted).
+It obviously requires ncurses but other than that nothing extra +should be needed. Note that the testing of Fedora 29 was earlier in the +development however (but then 29 is also EOL).
+Furthermore I do not believe - but I cannot confirm either - that +endianness matters; I only have Intel CPUs and arm64 (MacBook Pro +M1).
+I imagine that it works for 32-bit systems but I do not know for +sure.
+You should be able to get the compiler with:
+sudo xcode-select --installYou don’t need Xcode itself and it appears that the command line +tools (as above should install) already has ncurses so there shouldn’t +be anything else that needs to be done. But I’m not actually sure of +this - I’m seeing conflicting information and it’s been too long; anyway +there’s always MacPorts and also +Homebrew which should be sufficient; I do NOT recommend +Homebrew however: it takes ownership of /usr/local: as in the user you +run it as is the new owner so that you don’t need sudo because … Well +never mind.
+You’ll need the packages gcc, ncurses and
+ncurses-devel (plus make if you want to use
+the Makefile) to compile and link this entry. With Fedora more recent
+versions there’s the dnf package manager but I cannot help
+with any of the GUI installers since I don’t use them. Something
+like:
sudo dnf install ncurses-devel ncurses make gccor
+# dnf install ncurses-devel ncurses make gccJust like Fedora you will need gcc,
+ncurses, ncurses-devel (and if you want to use
+the Makefile make). CentOS uses yum and CentOS
+8 uses dnf. So:
sudo yum install ncurses-devel ncurses make gccor
+# yum install ncurses-devel ncurses make gccor
+sudo dnf install ncurses-devel ncurses make gccor
+# dnf install ncurses-devel ncurses make gccYou can also use clang to compile (which is what gcc actually is +under macOS).
+As long as you have the dependencies above the only thing that comes +to mind is integer width problems.
+If size_t does not equate to unsigned long: In this case
+if say it’s an unsigned long long then theoretically you should be able
+to change the strtoul() to be strtoull()
+(after updating the typedef for unsigned long to be unsigned long long
+maybe?). It appears to work from a quick test at least.
If it’s an unsigned int I believe you will have to opt for an
+unsigned long (so strtoul() cast to an unsigned int).
Anything else I do not know other than just changing the typedef and +updating to the proper function (and/or cast to the right type).
+ + + + + + + +
+ In this document I provide information to the herpetologist hackers +to help them modify certain things and add features for their studying +of snakes. Or if you prefer you can be the snake studying the +herpetologist. Whichever character you prefer to play doesn’t really +matter.
+WARNING: There are some things cited in this document that are no +longer correct, might be misleading or otherwise not clear. This is both +intentional and unintentional. This is especially true for claims that +something means something specific. Put another way just because the +table says something doesn’t mean it’s the whole truth: no misleading +comments but misleading document that talks about some parts of the +code!
+Please do not assume that just because something is in this file +means that it’s complete or will work without any changes (see above). +As I have changed the game over development stages some features have +been dramatically changed and some of these ideas could be incompatible +now or otherwise impractical. Also you might find in fact that some of +these leave more questions than answers (e.g. some require making +decisions for specific problems one might encounter)!
+With that said here’s the C sibilancy (the herpetologist programmer’s +and snake’s syllabary) for all snake and herpetologist programmers +alike.
+Modifications
+Modifying the colours
Background colour
Modifying the characters for the wall, bug and snake
Modifying input keys
Modifying the status/score line
Make it so the snake starts at a random location
Additions
+Health (and/or Lives)
Saving the game
Different types of walls
Blockades
Diagonal movement
Special abilities
Obstacles
More than one bug
Power-ups
Snakeskin shedding (becoming an obstacle)
Snake moves faster over time
Max times the bug evades before game over
Here I include information on modifying the game in various ways +depending on the preference of the herpetologist (or if you prefer you +can be the snake studying the herpetologist).
+As I already said I provided a script that prompts you for colours +and then compiles the game with those colours (default if invalid +input).
+The parts of the game that are coloured are the WALL(s), the snake +HEAD and snake BODY. In the published alternate version (prog.alt.c) the +bug is also colourable. This means that there are a total of four +configurable colour pairs. It only supports the eight standard colours. +Each colour is a pair: foreground and background.
+To change the colours use the snake-colours.sh or play.sh scripts. +Hit enter for defaults if you don’t want to change the colour.
+I prefer black backgrounds but this is the basic idea behind changing +it. You initialise a pair (make it 5 so as to not interfere with the +script customisability) and then you use the bkgd() function with the +colour pair. Something like this:
+init_pair(5, COLOR_BLACK, COLOR_WHITE);
+bkgd(COLOR_PAIR(5));I played with this after winning and I came across some problems with +colours but that changes the background of the program anyway.
+The standard colour identifiers are:
+COLOR_BLACK
+COLOR_RED
+COLOR_GREEN
+COLOR_YELLOW
+COLOR_BLUE
+COLOR_MAGENTA
+COLOR_CYAN
+COLOR_WHITESee Linux Journal for more information article https://www.linuxjournal.com/content/programming-color-ncurses +for more details.
+For the wall, bug and snake body characters you can replace the +following in the source code (include the single +quotes!) respectively:
+'#'
+
+'*'
+
+'o'
+Let’s say you don’t want to use arrow keys or you don’t have arrow
+keys. Instead you might want the keys l, r,
+u and d for the directions. Find the case
+statements:
case KEY_LEFT:
+case KEY_RIGHT:
+case KEY_UP:
+case KEY_DOWN:Replace KEY_LEFT with:
'l'and do the same thing for the other directions too. If you want more +than one key e.g. the arrow keys and the letters just add another case +statement e.g. for left it might be:
+case KEY_LEFT: case 'l':Yes the single quotes matter. You can do similar for the other input +keys. Please note that modifier keys like ctrl aren’t detectable or at +least I’m unaware of how to do so.
+Below I give a table of some variables/expressions along with the +data types and their respective format specifiers of the data. +Originally it was a lot more detailed but as I started obfuscating +things variables started to disappear and I decided to remove those +variables from the table outright. Again also remember that it’s +outdated.
+Please note that this set of information is the same that will be +seen at the end of the game; I leave that as an exercise to the reader +to implement two different sets. Please also be aware that if you don’t +want any information you must set it to the below at the very least; +defining it without a value is not enough (it amounts to the parameters +to a printf call):
+#define J " " );If you don’t care about brief displays of ‘o’ at 0,0 you can just +have:
+#define J "");That’s the bare minimum. Notice also that it’s been a long while +since I’ve tested this part but I think this is right.
+Anyway as I said the score line has basic statistics that’s +relatively easy to change if you want more or less or different +information e.g. the location of the bug.
+Initially there was no display of where the (head of the) snake was +at nor the maximum size (that is the size calculated by ncurses) - +though the astute hacker would notice that the maximum dimensions size +is not what the status line says: this has to do with the walls and the +status line - coordinates.
+At that point the only time the top stats were updated is when the +snake got a bug. This however was something I didn’t quite like: I like +having the coordinates of where the snake is plus the other things that +are there (a number of which were added over time). Obviously this would +take many bug sized bytes (bites) even once let alone twice! Thus I made +it into a define:
+#define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n), Y, y[-2], X,*y,A,g, c);With the table below you can show a variety of other +variables/statistics.
+There’s another reason for changing this line though: depending on
+the terminal width the line could cover the top wall; this can even
+happen only part of the game because the line length is not constant:
+most of the variables change throughout the game play and as these are
+displayed the length of the line changes. Use make test to
+get an estimate (it’s not perfect).
This is not the fault of my entry but rather how text wraps. But this +is another reason it’s easy to change: if the terminal size is too short +then you can update it; but if you want additional or different +information you can also update it. Remember that if you modify the +score data the termcaps algorithm might not remain as correct!
+But the game needs to have the walls drawn and the score line is also +important; well it’s important in that the row number of the top wall is +in fact used and so if this line wasn’t there it would require changes +to make it work right.
+For an example this would only include the size of the snake and the +number of bugs eaten (I think I have this correct; I’ve made many +changes to the point of not wanting to mess with anything that’s +unnecessary):
+#define J Z(SIZE: %zu BUGS: %zu\n), A, c);More generally the following expressions and data types (with format +specifiers) come to mind as information that might be of interest.
+ DESCRIPTION EXPRESSION TYPE FORMAT SPECIFIER
+
+ Snake size A size_t %zu
+
+ Y coordinate Y int %d
+ of snake head
+
+ X coordinate X int %d
+ of snake head
+
+ Max snake size g size_t %zu
+
+ Bugs eaten c size_t %zu
+
+ Bug line (Y) *U int %d
+
+ Bug column (X) *V int %d
+
+ Snake size A size_t %zu
+
+ Growth size (per N size_t %zu
+ bug)
+Quite simply change:
+X = H / 2; Y = *x / 2;To be something like:
+X = R(1,H-1);
+Y = R(2,*x-1);Or whatever you might want it to be… (question to answer: will this +work at all? You decide!)
+Here are some things you could add (including a bit of code).
+This was something I thought of a number of times but I opted for +other features instead. Here are some ideas on how one might choose to +implement something like this. Or maybe it’s more like what sorts of +things do you have to consider and answer?
+If the snake runs back into itself - thus dying - the snake would +actually be slithering inside itself (a rather creepy thought - though +they sort of do when they shed their skin). How then would the lives be +of any value? If that would constitute as running into itself wouldn’t +it very quickly lose all lives?
+I can think of a number of ways to answer this question: by defining +specific rules (as in what would be allowed and what wouldn’t be) and +also by when to decrement the lives.
If the snake hits a wall and it’s not allowed to where would the +snake be placed? This is similar to (1) but maybe not as many things to +consider.
Should the snake lose its size? WHAT does having another life +even MEAN?
Do you want (you do not necessarily need) a new variable that +says how many lives you want? Either way probably be good that it can be +specified by the player.
The simplest rules I can think of off the top of my head is that the +snake loses its size but it keeps the bugs count. So if the lives are +three and the snake has three lives, has eaten 15 bugs and then runs +into itself it will start out again at the original size (remember +there’s a fair bit more than just changing the size of the snake to do +in order for it to be of use!) but it then the next bug it eats will be +the 16th bug.
+Of course you could also say that the first time the snake eats a bug +(on the next life) it grows to the size it once was. This would actually +be one way to deal with some issues that I sort of alluded to above. Of +course it also has some other complications.
+You could do similar for health only maybe the size of the snake +decreases or you use the variable you would have used for lives to be +health instead.
+Vital issue to resolve: where is the snake? And if the snake size is +decreased will you be careful enough that you don’t run into the snake +where it actually isn’t?
+A few things would have to be done to do this. Maybe more than a few: +some decisions and some code.
+First obviously is you would need a hook to save the game: that is +what if you don’t want to save? You could save every time the snake +moves so that you keep the highest size (though this could theoretically +slow the game down more as the snake gets bigger and bigger). Or you +could say that hitting the key ‘s’ saves the game by calling a save +function.
+Second is you need a save function. You would need to save the +dimensions of the game, all the coordinates the snake is currently at +(from head to tail), where the bug is, decide whether or not to save the +direction (if it’s 0 the snake isn’t moving which would mean the player +could decide when to resume) and the number of bugs eaten so far. +Alternatively you could have the bug simply be placed somewhere where +the snake isn’t after replacing (can you replace something with itself?) +the snake.
+Question: do you have to save the WALLS and other environmental +variables? You wouldn’t have to but you could: if you didn’t it would +mean that the player could change the growth size on another run for +example, or they could disable or enable a feature.
+Of course you would also need a restore routine. This would mean you +would want to parse the command line so as to know which file should be +read in (easiest way would be if the first arg is some value then it +tries to load in whatever you decided to name the save file). How would +this work?
+You would have to make sure that the dimensions of the terminal are +valid for what was saved and you would have to compensate that maybe the +dimensions have to be slightly adjusted for the screen (I have given +more than enough information on this elsewhere as to why this is). +Obviously you have to initialise ncurses, allocate the arrays, draw the +walls, snake, bug etc. You wouldn’t initialise ncurses or its features +more than once (i.e. keep track of whether or not you have initialised +these things due to a file being read or starting anew). Clearly you +would have to come up with a file format and have error detection too. I +can think of an easier way to make sure you don’t initialise the ncurses +twice however. It would do the same things only with a bit less code and +maybe simpler logic.
+It might seem like a difficult task but it’s actually not that +difficult. It didn’t take much time for me to do something like this: as +far as saving and loading the values and even setting up ncurses: I just +didn’t go beyond that.
+Tip: DON’T TRY
+scr_dump()/scr_restore()/scr_init()/scr_set().
+After a quick test in the way I have things set up it seems to cause
+some problems (also scr_dump() quits the program).
You would have to instead identify that restoring is requested, then +read in the file (including the coordinates) and then populate the snake +arrays, drawing it on screen.
+The game has two modes with walls: if you run into it the game is +over (you die) OR you go through it and come out on the opposite side. +There certainly are other possibilities though. Here I give some ideas +that could be implemented (and one I have some code for).
+I did this once before as an experiment so it can work fine: let’s +say instead of the snake either dying or going through and out on the +other side what if it’s just that the snake is momentarily dazed and +unable to move again until it comes round sort of like what happens with +some people who pay more attention to their ruddy phone instead of where +they’re walking (even when crossing a busy street!).
+If you look at the walls code you will see a ternary operator for
+both X and Y coordinates. This of course is
+very different now but the idea remains the same:
if (p && !(*O>=O[1]||H>=M(1)))
+{
+ if (X<1||X>=H)
+ X = X<1?H-1:1;
+ if (Y<2||Y>=*O)
+ Y = Y<2?*O-1:2;
+}You can ignore the first if if you like; I’m not going to explain +that part. The inner if statements however are testing if the new snake +coordinates are beyond the walls (off screen). Depending on where the +snake is it will set the coordinates to the correct new location.
+If however you wanted the walls to act as a barrier you could do +something like this instead (omitting the inner if statements here):
+X=X<1?1:*H-1;
+
+Y=Y<2?2:*O-1;Again though that’s just the idea; the code is dramatically +different. The above incidentally is not even complete.
+You could also have it so depending on the WALLS value have it go +through walls, end game or act as a blockade. If you really wanted you +could have some walls you can go through and some you can’t (maybe +different colours would be needed?).
+Oh and yes you might find it can look a bit funny this way. For +instance if you’re slithering left along top wall and you hit up the +snake will still be horizontal (after the thing I warned about is +resolved of course! - And that’s a very important one unless you want +negative coordinates - and you don’t!) since it’ll have stopped. If you +were to then hit the direction you had been going you will see that it +continues going that way in a straight horizontal line. In other words +the snake will just appear to be not moving as if the game had been +paused.
+I said this is impractical so how could it be done? Well you have to
+decide which keys would take you up and right, up and left, down and
+right or down and left. The trouble is ncurses can’t detect both right
+and up at the same time. Besides that though it would only be a matter
+of updating both Y and X coordinates rather
+than just one or the other. That’s easy enough to do. The only
+impractical part of this is deciding the keys and unfortunately rule 2b
+(and probably 2b) would have been broken and so have no chance of
+winning (now that I won it’s too much effort and I’ve forgotten most of
+the stuff too to say nothing of getting the format right).
Anyway it’s just a matter of updating both X and
+Y. But what about going through walls mode? Does it matter?
+I’ll let you figure it out!
Looking back at the missing features, this is the feature I wanted +more than any other.
+Could the snake jump over itself if it wanted to? How would this be +implemented? The head would simply be moved one beyond but then you +would have to keep track of where the part that was jumped over is and +what part of the snake has to do the same thing. I can think of a much +easier way though. This very paragraph actually includes it! Then it +would only be a matter of deciding what key does what.
+Similar to walls only that it’s inside the field. You would have to +consider the behaviour of running into the obstacles: should the snake +die? Should it lose health/lives (if implemented)? Can it go through +them? And also: the bug placement would have to be updated too. You +would also need to figure out what character to use for the obstacles: I +personally would just reuse the wall character. Also to make it simpler +if the walls are passable then so too would be the obstacles; else you +would die (or if you had health/lives then those would apply).
+The main thing to consider would be the test if the snake has eaten a +bug would have to check an array (and: how big? Where does the size come +from?) as well as the placement routine (though I guess you could have a +value for each array meaning that if you run into a bug spot that has +more than one bug you would eat that many bugs).
+You would need to decide what power-ups exist, what they do, how many +can there be (and how many can the snake have at one time), what makes +them show up plus you would have to add code to handle the +power-ups.
+I.e. at some point the snake would split back to some (arbitrary) +size but then the snakeskin left would act as the snake in the sense +that if the snake runs into it it’s the same as running into the snake. +More realistically the snakeskin would be the same size the snake was +and the snake would grow as it slithered out. But that would fill the +screen very quickly of course. Maybe the snakeskin has some other +purpose?
+In the game +[Surround](https://en.wikipedia.org/wiki/Surround_(video_game) you have +no control over the speed. I believe there are five different speed +levels but the player has no control over it except the drawing mode. It +would be possible to make it so the snake moves faster over time. How +often and by what amount would probably vary but importantly it would +need to not go so fast there’s no controlling - or go below 0 which puts +it in blocking mode. Probably it would be that it gets faster up to a +certain per cent of the original speed so that it doesn’t get too fast. +But even that would have to be customisable.
+Basically so that the player is on a time schedule of sorts. The same +could be done for maximum number of movements before game over. These +would both be rather easy to implement but personally I would only +consider the max number of evasions (I just don’t have many more bytes +left!) - unless maybe that there is no default limit on max moves.
+ + + + + + + +
+ Current bugs and things that look like bugs but are not, as of 12 +September 2020.
+ +getch() also accepts input from some input +hardware other than keyboards
With negative growth the snake drops +from the middle of the body
Vertical snake has more space between +snake char than horizontal
Fun fact: There’s something of a built-in test unit for some +features; much like some of the other features this was not +intentionally designed but is simply a fun consequence of the features I +did implement.
+Let’s say you want to verify that the evade mode is at the right +count. How can you verify it without modifying the code? Try:
+SIZE=0 SHED=1 SHEDS=1 EVADE=10 ./progSince you start out at size 0 and you shed every movement then when +you’re size 10 (as long as you don’t eat a bug of course!) the bug +should evade.
+If you set it to SIZE=10 there would be a difference of
+10. Now every move you will see the size the snake is. But then…Why is
+it that it doesn’t seem to move until the snake is of size 20? Because
+you start out at size 10 of course (but in fact if you observe the snake
+won’t appear to be of size 20 yet).
There probably are combinations that can test these types of things I +just don’t at this time remember any I might have done.
+One way to show this is in full is to set the initial size and growth +size to a high value; then try each one individually. For example:
+SIZE=100 ./progIn this case after you make the first move the snake will keep +growing and so the snake tail stays in the same spot until after it’s +the ‘full size’. When it has reached the full size then the entire body +of the snake will start to move too (i.e. if the tail isn’t moving it is +growing or else paused).
+In several tests I set the initial size to a high value, the max to a +higher value still and enabled cannibalism and much of the screen was +full of the snake before it started to move. The cannibalism.log.md has some examples of +both cannibalism and a long snake that didn’t move for most of the +game.
+If however you let the snake get to the full size then eat a bug +there can be a moment of lingering where it appears the snake moves for +a few places before it then stops moving and just grows until it’s the +full size.
+It can also appear to be not moving when the snake is extraordinarily +long and more so when the snake has just eaten: which of course is when +it would grow (just like when shedding). Even so sometimes it takes a +moment for the snake tail to catch up with the body (so to speak) so +that it could for a few moments be difficult to know if where you’re +going is going to be occupied or not.
+Basically if the snake head is moving you can consider the snake to +be moving or growing and that’s the important point (if you have the +default status line you can also see the coordinates being updated to +show this).
+On my MacBook Pro the trackpad has actually triggered directions; if
+I were to swipe up and then down the snake would run into itself. I
+believe this has to do with the function call keypad(v, 1);
+which enables arrow keys.
What this means is that for both you will only have the snake head. +At this point you can safely go back on yourself even without +cannibalism enabled. This is intentional; a snake size of 0 is pointless +but why should it not be playable anyway?
+If SIZE < 0 you’ll likely win straight away since
+it’s unsigned and it’ll wrap back to the maximum so you will win
+automatically (this is the same as the second mode the judges suggested
+with SIZE=-1 ./prog).
As for the growth sizes (including shedding) if it’s < 0: the +snake will still lose its length and display will still be odd: because +the snake size is decreased any element beyond the reduced size isn’t +touched so the snake characters will remain on the screen; only the +moving snake is what matters. But it’s likely the game will be won +fairly quickly because once it’s -1 in size it’ll be at the maximum +size.
+For example if I say SIZE=3 GROW=-2 at two bugs the game
+is won.
As for growing: anything > 0 it will grow that many; 0 it will not +grow (which I show as a gameplay mode); and < 0 it will ‘shrink’ as I +explained above. You could say that it’s defying physics by growing and +shrinking simultaneously but either way negative size will force a +win.
+Here’s a fun output:
+< <<<+
+That’s from:
+SHED=1 SHEDS=-1 ./progAnd right after that you would see:
+YOU WIN!
+X:72/156 Y:20/41 S:997/997 B:0
+Try this though:
+SHEDS=-1 SHED=25 ./progAnd you’ll see that you start to be the normal size but then you +leave your head several places until you end up getting to the max size +(by way of it being unsigned). This isn’t a bug even if it might seem +like one. In fact this is the basis of the grow/shrink mode of play that +I talk about in the gameplay.html (gameplay.md on GitHub) file.
+Here’s another thing to be aware of. Let’s say you do:
+SIZE=1 GROW=-1 ./progHow many bugs will it take before you win? In fact it’s two: you +would see the head twice: the second time once you ‘shrink’ down to size +0; it would then be that if you get another bug you will be at the ‘max +size’:
+YOU WIN!
+X:81/156 Y:35/41 S:997/997 B:2I.e. what happens if you’re growing - for example after just eating a +bug - and you’re racing towards your tail? If you’re behind it should be +fine since you’re going the same speed as the snake is, right? Not +quite. Because you’re growing you might end up running into your tail +(depending on how much you’ve left to grow and how far away from your +tail)! This happened to me once and it surprised me and I figured I +better note it.
+Remember: the tail at that point isn’t moving yet; the snake +is growing!
+For instance it might look like this (the first line is as it
+happens, the second pointing to where it happens and the third being
+what’s left after the rest of the snake has gone through). Doing
+SHED=100 SHEDS=-1:
ooooooooo>ooo>
+ ^
+ >
+The ^ points to the part that’s split from the snake and
+which will be there on its own after the snake passes through it. Here’s
+an example with it being SHEDS=-3:
<ooo<ooooo
+ ^^^
+ <oo
+If you go through the part that has split (which for some might be +difficult to see whilst the snake has not fully left its skin) you won’t +run into yourself; if you run into another part though you do. This +might be a bit confusing I know but what kind of animal shrinks in size +by leaving its head behind only to sprout up a new head? Actually…on +second thought, I won’t go there! :)
+Anyway it’s an odd idea for the snake to shrink by leaving part of +its skin: that’s how they grow in the wild so it’s a misnomer. The +shrinking was only ‘discovered’ because it’s an unsigned number but +happily it allows for different gameplay modes.
+I show a different example in the crazy.log.md file but what defines a coordinate +which the snake occupies? If the snake breaks apart then the only part +of the screen the snake is occupying is the place which is actually +moving: if the snake decreases in size by 3 then the three respective +characters you can go through safely. But this also means that a bug can +go there! So you might see something like:
+<*oooooooo
+This is because the snake is no longer there: in fact in this run the +snake was quite a bit lower on the screen and 25 in length.
+Ilya Kurdyukov shared concern for the loop in the bug placement +routine: is it safe? What he refers to is that it’s a potential infinite +loop. But is it? I understand the concern and it’s something I played +with a lot to be sure that it doesn’t happen. This is how it works.
+To explain it I will go back to the make test program
+termcaps (which is imperfect for the recommended terminal
+size but the max snake size is exactly how it’s done in Snake and works
+well). The minimum terminal size I require in the game is 10 x 10. This
+includes the walls and score line.
Now the default max snake size is 997 but when the terminal is too +small it adjusts the max snake size. For 10 lines and 10 columns you +would see:
+$ make test
+terminal supports cursor movement
+terminal supports making cursor invisible
+terminal supports bold
+terminal supports colours
+
+terminal rows 10 (7 playable)
+terminal cols 10 (8 playable)
+
+snake size: 49 (max size: 49)
+ bugs: 9 (max size: 9)
+
+at least 28 columns recommended for snake size 49 (is 10)
+at least 28 columns recommended for snake size 49 (is 10)
+
+1 problem detected (0 fatal).
+make: *** [test] Error 1First: what’s the problem? It’s just a warning that for the snake +size of 49 the recommended size of columns is 28. However this is just +for the score text; with only 10 columns it will overflow and wrap to +the next line(s). But if you observe that 49 is the max snake size. How +is this derived and why?
+With 10 rows and columns it’s calculated as 7 * 7
+(i.e. it’s (cols-3)*(rows-3). However the number of
+playable locations is in this case 56
+(playable_rows * playable_columns). I have played with this
+many times and I do not believe there should be a problem. If there is
+it’s from a very poor implementation of rand() and I discuss this in troubleshooting.html (troubleshooting.md on GitHub).
There I also give some test outputs without the cap in place to give +an idea of when it became a problem (or would have). In short this is +known but I do not think it will be a problem and I have given +resolutions. In any event without cannibal mode it’s even less likely +that you’ll ever run into this problem.
+The obvious question is why didn’t I put in a break? Because of the +IOCCC size rule 2 and what would it do anyway? I guess it would win but +to my mind that is a bug and I prefer putting in restrictions to try and +make it very unlikely.
+Ilya Kurdyukov also pointed out that depending on orientation of the +snake there are different amount of spaces between the snake char ‘o’. +This is what he had to say on it:
+++Yes, it is the problem, space in character terminal is not isotropic +from a viewer (gamer) perspective so one step up look like two steps +right.
+MATE Terminal 1.20.0, and font “Ubuntu Mono Regular 13”
+But, as I said, there is no terminal problem itself, it’s because +character tiles are not square (except for Japanese/Chinese, Japanese +even have so called full-width English characters in their encoding, +presented in UTF too).
+https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms
+So the typical fonts are Half-width or close to it.
++
However the function mvaddch() takes a
+chtype which amounts to a C char with additional
+information and so the full width Ois too big for the
+function call. He reminded me of the old DOS box chars but even if this
+was easily duplicable (and it might be and I don’t remember) the logic
+would have to be changed for drawing the snake and in such a way that
+would change the source code layout too much - to say nothing else of
+the fact I have also forgotten some of what I did obfuscation wise (and
+the spoilers aren’t enough here!).
That being said the font and font size does seem to have somewhat of +an effect on it since his screenshots have wider gaps than mine.
+Once again thank you mate for bringing this to my attention and also +playing with it and providing the resources! Much appreciated! (And +although you suggested it is bugging me I once again here say it’s not +at all bugging me but it is very much appreciated and it means a great +deal to me. Cheers.)
+| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ To give an idea what cannibalism can look like (it’s I believe a +unique feature to any other version of Snake), this file shows just how +extreme cannibalism can get with a long snake. I made a few changes, +described below, to make it easier to distinguish what is what.
+In the below game dumps I have replaced the bug with
+B, the head with
+H and where it is known, the tail with
+T, in order to help distinguish where they
+are. Since the head is replaced with H I
+have noted the direction it was moving above each dump as well as at the
+right of the line; each line with a bug, the head or tail is marked with
+upper case text at the right to make it easier to find.
The first run I include several dumps but there isn’t much +information about most of them.
+X:45/89 Y:22/25 S:1675/2001 B:35
+##########################################################################################
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+# o o o o o oooo o #
+# oooooooooo o o o o ooo ooooooooo oooo #
+# o ooooooooooooooooooooooooo o o o o o #
+# o ooooooooooo ooooooooooooooooooooooo oooooooooooooooo o o o #
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o o oo#
+# o o ooooooooooooooooooooooooooooooooooooooo o o oBo o oooo o # <- *BUG*
+# o o o ooooooooooooooooooooooooooooooooooooooo o o o o o o #
+# o o o o o ooo o o oo oooooooooooooooooooooooo o #
+# o ooooooooooooooooo oooooooo o o oooooooooooo ooooooooooooooooooooooooo o #
+# o o o o oooooo o o o o o oooooooooooooooooooooooo o o o o o #
+#oooooooooooooooooo o o o o oo o o o o o oo oo o oo o o oooooooooooooooooooo#
+# o oooooooooooooooooooooooooooooooooooooooooooooooooooo oo o o oo o o o #
+# o o ooooooooooooooooooooooooooooooooooooooooooooooooo o oo o o o #
+#oooooooooo oooo o o o o o o o o oo o oooooooooooooooooo ooooooooooooooooo#
+# oooooooooooooooo o o o ooooooooo o oo o o o o o o ooooooooooooo #
+# ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o #
+#oooooo o oooooo o o o o o o o ooooooooooooooooooooooooo o ooooooo#
+# o o o o o o oooo ooooooooooooooooo o o o o o #
+# o o oo o o o oooooH o oooooooo ooooo o # <- *HEAD, RIGHT*
+#ooooooooo ooooooooooooooooooooo ooooooooooo ooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+##########################################################################################
+X:79/89 Y:9/25 S:1680/2001 B:36
+##########################################################################################
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+# o o o o o oooo o #
+# oooooooooo o o o o ooo ooooooooo oooo #
+# o ooooooooooooooooooooooooo o o o o o #
+# o ooooooooooo ooooooooooooooooooooooo oooooooooooooooo o o o #
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o o oo#
+# o o oooooooooooooooooooooooo oo o o oooooooooooooooooooH o # <- *HEAD, RIGHT*
+# o o o oooooooooooooooooooooooooooooooooooooooooo o o o o o #
+# o o o o o o o o o oo o oooooooooooooooooooooooo o #
+# o ooooooooooooooooo o o o o oooooooooooo ooooooooooooooooooooooooo o #
+# o o o o oooooo o o o o oooooooooooooooooooooooo o o o o o #
+#oooooooooooooooooo o o o oo o o o o o ooo oo o oo o o oooooooooooooooooooo#
+# o oooooooooooooooooooooooooooooooooooooooooooooooooooo oo Bo o oo o o o # <- *BUG*
+# o o ooooooooooooooooooooooooooooooooooooooooooooooooo o oo o o o #
+#oooooooooo oooo o o o o o o o ooo o oooooooooooooooooo ooooooooooooooooo#
+# oooooooooooooooo o o ooooooooo o ooo o o o o o o ooooooooooooo #
+# ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o #
+#oooooo o oooooo o o o o o o o ooooooooooooooooooooooooo o ooooooo#
+# o o o o o oooo ooooooooooooooooo o o o o o #
+# o o o o o oooooo o oooooooo ooooo o #
+#ooooooooo ooooooooooooooooooooo ooooooooooo ooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+##########################################################################################
+X:53/89 Y:18/25 S:1690/2001 B:38
+##########################################################################################
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+# o o o o o oooo o #
+# oooooooooo o o o o ooo ooooooooo oooo #
+# o ooooooooooooooooooooooooo o o o o o #
+# o ooooooooooo ooooooooooooooooo o oooooooooooooooo o o o #
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o o oo#
+# o B o oooooooooooooooooooooooo o o o oooooooooooooooooooo o # <- *BUG*
+# o o o oooooooooooooooooooooooooooooooooooooooooo o o o o o o #
+# o o o o o o o o o o o oooooooooooooooooooooooo o o #
+# o ooooooooooooooooo o o o o oooooooooo o ooooooooooooooooooooooooo o #
+# o o o o oooooo o o o o oooooooooooooooooooooooo o o o o o o #
+#oooooooooooooooooo o o o oo o o o o o o o o o oo oooooooooooooooooooooooooo#
+# o oooooooooooooooooooooooooooooooooooooooooooooooooooo oo oo o oo o o o #
+# o o ooooooooooooooooooooooooooooooooooooooooooooooooo o oo o o o #
+#oooooooooo oooo o o o o o o o o o o ooooooooooooooooo ooooooooooooooooo#
+# oooooooooooooooo o o ooooooooo o o o o Hooooooooooo o ooooooooooooo # <- *HEAD, LEFT*
+# ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o o #
+#oooooo o oooooo o o o o o o o ooooooooooooooooooooooooo o ooooooo#
+# o o o o o oooo ooooooooooooooooo o o o o o #
+# o o o o o oooooo o oooooooo ooooo o #
+#ooooooooo ooooooooooooooooooooo ooooooooooo ooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+##########################################################################################
+X:61/89 Y:6/25 S:1815/2001 B:63
+##########################################################################################
+# oooooooooooooooooooooooooooooooooT # <- *TAIL*
+# o #
+# ooooo o ooooooooooo #
+# oooooooooo o o B ooooooooooooooooooooo # <- *BUG*
+#oooooo o oooooooooooooooooooooooooooooooo Hooooooooooooooooooooooooooo# <- *HEAD, LEFT
+# o ooooooooooooooooooooooo o oooo o o o o ooo #
+# o o oooo oooooooooooooooooooooooooooooooo o ooooooooooooooooooo ooo #
+# o ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooo #
+# o o o o oooooooooooooooooooooooooooooooooooooooooooooo o o o o ooo #
+# o oooooooooooooooooooooooooooooooooooooooooooo o oooooooooooooooooooooooooooooooo #
+#ooooooooooooooooooooooo o oooooooooo o ooooooooooooooooooooooooooooooo#
+# o o o o oooooo o ooooooooooooooooooooooooooooooooooooooooooo #
+#oooooooooooooooooo o o o oo o o o ooooo o o oooooooooooooooooooooooooo#
+# o oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o oo oo #
+# o o oooooooooooooooooooooooooooooooooooooooooooooooooo oo o ooooooooooo #
+#oooooooooo oooo oooooooooooooo o o ooooooooooooooooo ooooooooooooooooo#
+# oooooooooooooooo oo ooooooooo o oooooooooooooo o ooooooooooooooooo #
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o oooooooooooooo#
+#oooooo ooooooooooooooooooooooooooooooooooooooooo o ooo ooooooo#
+# oo o oooo oooo o o oooooo o #
+# ooooooooooooooooo ooooooooooooooooooo oooooooooooooooooooo o #
+#ooooooooo ooooooooooooooooooooooooooooooooooooooooooooo ooooooo#
+# #
+##########################################################################################
+X:47/89 Y:5/25 S:1815/2001 B:63
+##########################################################################################
+# ooooooooooooooooT # <- *TAIL*
+# o #
+# ooooo o ooooooooooo oooo # <- *HEAD, LEFT*
+# oooooooooo o o BHooo o ooooooooooooooooooooo # <- *BUG*
+#oooooo o oooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooo#
+# o ooooooooooooooooooooooo o oooo o o o o ooo #
+# o o oooo oooooooooooooooooooooooooooooooo o ooooooooooooooooooo ooo #
+# o ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooo #
+# o o o o oooooooooooooooooooooooooooooooooooooooooooooo o o o o ooo #
+# o oooooooooooooooooooooooooooooooooooooooooooo o oooooooooooooooooooooooooooooooo #
+#ooooooooooooooooooooooo o oooooooooo o ooooooooooooooooooooooooooooooo#
+# o o o o oooooo o ooooooooooooooooooooooooooooooooooooooooooo #
+#oooooooooooooooooo o o o oo o o o ooooo o o oooooooooooooooooooooooooo#
+# o oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o oo oo #
+# o o oooooooooooooooooooooooooooooooooooooooooooooooooo oo o ooooooooooo #
+#oooooooooo oooo oooooooooooooo o o ooooooooooooooooo ooooooooooooooooo#
+# oooooooooooooooo oo ooooooooo o oooooooooooooo o ooooooooooooooooo #
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o oooooooooooooo#
+#oooooo ooooooooooooooooooooooooooooooooooooooooo o ooo ooooooo#
+# oo o oooo oooo o o oooooo o #
+# ooooooooooooooooo ooooooooooooooooooo oooooooooooooooooooo o #
+#ooooooooo ooooooooooooooooooooooooooooooooooooooooooooo ooooooo#
+# #
+##########################################################################################
+Another example invocation where the snake head is in the snake and +where the snake hadn’t yet moved (that is it was still growing):
+CANNIBAL=1 SIZE=1500 GROW=2 MAXSIZE=1600 ./progIt looked like:
+X:55/89 Y:11/25 S:1564/1600 B:32
+##########################################################################################
+# oooo ooooooooooooooooooooooooo o #
+# o ooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooo #
+#ooo o o o o o oooooo#
+# o o o oooooooooooooooooooooooooo o #
+#oooooooooooooooooooooooooo o ooo o oo oooooooooooooooooooooo#
+#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo#
+# o o ooooooooooooo oooooooooooooooooooooo ooo oo o o o oo#
+#ooo o o oooooo o oooo ooo oo o o oooo#
+# o o o oooo o o oo ooo oooooooooooooooooo oo#
+# o o o ooooooooooooo oooooooooooooooo oooooHoooooooooooooooooooooooooooooo # <- *HEAD, LEFT*
+# o o o o ooooooooo oooooooooooooooooooooooooooo o o o o #
+# o ooooooo o o o oooooooooooooo o o o o o o ooooo #
+# o o o o o o o o o o o o o #
+# o o o o o o oB ooooooooooooooooooo o o ooo # <- *BUG*
+# o o o o o oooooooooooooooooooooooooooooooooooooooooooo o o #
+# oooooooooooooo o o oo o o oooooooooooooooooooooooooooooooooooooo #
+# o oo o o oo oooooooooooooo o o o oooooooooooo #
+#oooooo oo oooooooooooooo o o o o o o ooo oooooo#
+# ooooo o o o o oooooooooooooooooooooooooooooooo o #
+# o oooooooooooooo o o o o ooooooooo o o #
+# o o oooooo o oooooooooooo o o #
+# oooooooooooooooooooooooooooooooooooooooooooooooooooo ooo o #
+# ooooooooooo ooooooooooooo o oooooooooo #
+##########################################################################################
+…and no Preciouss: the itsself was +not a typo but intentional sibilance!
+ + + + + + + +
+ Here’s a delicious double-layered chocolate fudge cake that is +something of my mum’s speciality. It’s complicated and I should warn +that nobody has seemed to get it down as well as she has but it’s well +worth the effort if you love chocolate and have the time and +patience.
+My late stepmum who only liked white ‘chocolate’ loved this cake! The
+picture below is after it’s been iced. The image ioccc-cake.jpg was a feeble attempt at drawing
+IOCCC into the cake before icing, a different time. The
+picture below was taken for my birthday (14 February) in 2022 as it’s
+the best one I have:
+
+There are two sets of ingredients and instructions; the cake and the +icing. In addition there are some VERY IMPORTANT NOTES +to pay attention to; some will be repeated because they’re +VITALLY IMPORTANT for the quality of the cake. That +cannot be overstated.
+Below I have included the ingredients separately but I include them +within the instructions as well and in bold. Also in bold are some of +the special notes.
+Finally note that depending on where you are in the world this might +not work in the warmer months of the year and for the icing in +particular.
+This was originally taken from an American magazine for women so the +measurements are in the US standards. A nice resource (that has much +more than just cooking) is http://www.onlineconversion.com/cooking.htm. This will +particularly matter for cups, teaspoons, tablespoons and ounces.
+Baker’s chocolate is a brand name and the product in question is +unsweetened chocolate bars used for baking. According to a chocolates FAQ +it might be called different things:
+++Bitter chocolate contains no sugar and is often called ‘unsweetened’ +or ‘baking’ chocolate. In some countries it’s called 100% cacao since +it’s composed only of ground up cocoa beans, and nothing else.
+
The FAQ suggests that you might have to look for a baking supply shop +but I cannot offer any specific advice other than it’s unsweetened +chocolate bars for baking. The FAQ talks about what looks like +substitutes but I have no idea what that would turn out as; I somehow +suspect it wouldn’t be good.
+Either way PLEASE STILL PAY ATTENTION TO ALL THE NOTES on the +chocolate IN THE RECIPE and RECIPE NOTES BECAUSE THEY’RE EXTREMELY +IMPORTANT!
+PAY CLOSE ATTENTION TO THESE:
+USE MARGARINE! DO NOT USE +BUTTER! Butter does not work well. If you think it will come +out better by using butter you’re very mistaken; it will come out badly! +People have come back saying it didn’t come out well and they are asked +if they used butter or margarine. They had used butter because they +‘know better’ but they were very wrong and it is why the cake was +ruined.
Over the years they changed the size of the unsweetened chocolate +squares; they used to be one ounce each; now they are 1/4 ounce each +square so be sure to remember that THE AMOUNT IS BY OUNCE +NOT SQUARES AS LISTED. See the measurements link if +there’s any doubt.
Pay careful attention to the COOK FOR ONLY THE INDICATED +TIME part. Note that if your oven cooks a bit hotter you might +have to adjust the timing a bit. This is likely the case if the cake +comes out a little bit dry.
REMOVE THE CAKE FROM THE PANS 2 MINUTES AFTER +REMOVING FROM OVEN and then allow to FULLY COOL +ON RACKS! The important point is to MAKE SURE +THAT THEY DON’T OVERCOOK.
When the recipe was first written they sold powdered sugar in a +box; now it’s confectionery sugar in a bag. My mum experimented with the +amount and found that 4 cups seems about right.
USE WHOLE MILK! Don’t try skim milk or +whatever per cent of fat milk you have.
Use ONLY PURE VANILLA EXTRACT! +DO NOT USE IMITATION VANILLA! Not following +this rule will lead to a worse outcome.
Lightly grease TWO 9 x 1.5 INCH ROUND PANS; +lightly dust with flour.
Preheat oven to 350 F / 176.67 C.
Melt 3 OZ UNSWEETENED CHOCOLATE in double boiler +or small bowl over hot (BUT NOT BOILING) +water; let cool whilst proceeding.
Sift 2 and 1/4 CUPS FLOUR, 2 TSP BAKING +SODA, and 1/2 TSP SALT
Beat 1 STICK MARGARINE (NOT BUTTER!) in +large bowl until soft. Again MARGARINE, NOT +BUTTER!
Add 2 and 1/4 CUPS PACKED BROWN SUGAR and +3 EGGS. Beat until light and fluffy.
Beat in 1 1/2 TSP PURE VANILLA EXTRACT (NOT +IMITATION VANILLA!) and COOLED MELTED +CHOCOLATE.
Stir in dry ingredients alternately with 1 CUP SOUR +CREAM; always beat until smooth.
Stir in 1 CUP BOILING WATER.
Pour into prepared pans.
Bake for ONLY 35 MINUTES (NO +LONGER!) at 350 F / 176.67 C.
When you take the cake out put the cake pans on a cooling rack +for **_ONLY TWO MINUTES AND THEN IMMEDIATELY TURN CAKES OUT ON TO +RACK. Let COOL COMPLETELY!**
You will also need a bigger pan or bowl that can have ice water that +the icing bowl can sit in.
+PAY CLOSE ATTENTION TO THESE:
+MAKE SURE that the melted chocolate and +MARGARINE (NOT BUTTER!) do not get +solid.
For step #4 put 8 to 10 ICE CUBES in a pan or +big bowl (that’s big enough that another bowl can fit in it). +ADD AN INCH OF WATER and SET THE BOWL WITH THE +ICING IN THE PAN OR BOWL of ice water. DO NOT LET THE +ICING GET WET!
DO NOT LET ICING GET TOO THICK.
USE WHOLE MILK! Don’t try skim milk or +whatever per cent of fat milk you have.
Use ONLY PURE VANILLA EXTRACT! DO NOT +USE IMITATION VANILLA! Not following this rule will lead to a +worse outcome. As I recall the icing was quite a bit worse (but still +chocolaty at least) the times we had imitation vanilla (simply because +we couldn’t get pure vanilla at the time we needed it). Just don’t do +it.
Combine 4 OZ UNSWEETENED CHOCOLATE and +1/2 CUP MARGARINE (NOT BUTTER!) in pan. Melt +and remove from heat. DO NOT LET IT GET SOLID!
Combine 3 1/4 CUPS POWDERED SUGAR, 1/2 +CUPS WHOLE MILK and 2 TSP PURE +VANILLA EXTRACT (NOT IMITATION VANILLA!)** into a bowl. +STIR UNTIL SMOOTH.
Add chocolate mixture.
Set bowl in pan/bowl of ice and water. Beat with spoon/mixer +until it’s thick BUT not too thick. Make SURE ICING +DOES NOT GET WET! The idea is to MAKE THE BOWL +IT’S IN COLD BUT TO NOT LET THE ICING GET +WET!
| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ The following are a few screen dumps of:
+SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./progAs you will see it took 17 bugs to get down to below 0. Notice how I +had to get down to 0 more than once: because I moved enough times before +I got the last bug that I grew by 5 in size. I then however moved a bit +more and ate a bug which brought me down to -5 therefore winning at the +max size of 250. This then showed:
+YOU WIN!
+X:62/89 Y:5/25 S:250/250 B:17
+Observe all the arrows on the screen; that’s where a bug was that I +ate and then I split off losing 10 in size. The first dump I’m already +at 15/250:
+X:17/89 Y:15/25 S:15/250 B:13
+##########################################################################################
+# <ooooooooo o <ooooooooo* #
+# o o> #
+# o ooo oooo #
+# o o ooooooooo> o v #
+# o o oo o #
+# o o o o #
+# o o o o #
+# <oo o o o #
+# o #
+# o o #
+# o o #
+# o o o #
+# <ooo o oooooooo> #
+# <oooooooooooooo o o #
+# o o #
+# o o #
+# < ooo o o #
+# o o #
+# o v #
+# oooooo> #
+# o #
+# o o> #
+# oooooo #
+##########################################################################################
+But here now I’m at 0; the head is marked H and it’s
+moving left.
X:19/89 Y:12/25 S:0/250 B:16
+##########################################################################################
+# <ooooooooo o <oooooooo <ooooooo #
+# o o> oo #
+# o ooo oooo #
+# o o oooooo*oo> v #
+# o o oo o oo> #
+# o o o o oo #
+# o o o oooo #
+# <oo o o o #
+# o #
+# o o #
+# H o o # <- *HEAD, LEFT*
+# o o o #
+# <ooo o oo ooooo> #
+# o o #
+# o o #
+# o o #
+# < ooo o #
+# o o #
+# <oooooooo o v #
+# oooooo> #
+# o #
+# o o> #
+# oooooo #
+##########################################################################################
+I ended up moving enough times where my size went back up to 5.
+Below the snake is moving right and its head is indicated by a
+H with the line marked at the right; the bug is marked as
+B and is also marked at the right. Notice how it’s in the
+body of the snake (but see below)!
X:55/89 Y:4/25 S:5/250 B:16
+##########################################################################################
+# <ooooooooo o <oooooooo <ooooooo #
+# o o> oo #
+# ooooH oooo # <- *HEAD, RIGHT*
+# o o ooooooBoo> v # <- *BUG*
+# o o oo o oo> #
+# o o o o oo #
+# o o o oooo #
+# <oo o o o #
+# o #
+# o o #
+# o o #
+# o o o #
+# <ooo o oo ooooo> #
+# o o #
+# o o #
+# o o #
+# < ooo o #
+# o o #
+# <oooooooo o v #
+# oooooo> #
+# o #
+# o o> #
+# oooooo #
+##########################################################################################
+But did you see anything strange? The bug is on the fourth line below +the upper wall. However it appears to be inside the body of the snake! +But actually that’s just where the snake split off at some point: the +snake no longer occupies that place and indeed I could safely go through +that part of the screen.
+ + + + + + + +
+ Playing the game is very simple but because there are many different +modes I have included much more than just the input keys and a few +printouts of the game. For normal play you probably don’t even need this +file at all unless it be for the input keys.
+ +Most people probably won’t have any issue at all but for those who +have any problems with the terminal see the terminals.html (terminals.md on GitHub) file for more +information on the terminal capabilities testing utility I include.
+For troubleshooting other problems see troubleshooting.html (troubleshooting.md on GitHub) and bugs.html (bugs.md on +GitHub).
+Left, right, up and down arrow keys: move that +direction.
q (and Q): quit the game.
Space: pause/resume the game (direction keys will also resume).
+Note that no other keys will resume the game; however if you press a key
+that getch() picks up on and the game is not paused then it
+will be as if you pressed the current direction key (assuming you
+already moved the snake).
Here I will show the initial screen as well as what a few things look +like. If you don’t feel you need this but you want to configure colours +jump to colour selection. You can also jump to gameplay modes or game +variables.
+The bug colour by default is white; the snake head colour is red and +the snake body colour is green. The walls are cyan.
+When you load the game you will see a screen that looks something +like the below. Note that the top line is the score/status line: the top +wall is at line 1 and the left wall is column 0. Below I use the +coordinates in the X/Y order (ncurses is the opposite: Y/X). I will +parenthesise the numbers below to refer to the number in the +diagram:
+X:24/49 Y:8/16 S:5/658 B:0
+##################################################
+# #
+# #
+# #
+# #
+# #
+# #
+# + #
+# #
+# * #
+# #
+# #
+# #
+# #
+# #
+##################################################
+
+The snake (+) is at (24),(8). The bug char is
+* and the wall char is #. When the snake moves
+the head will be an arrow facing the direction it is moving; the body is
+‘o’.
The top wall is always line (1), the left wall is always column +(0), the bottom wall is line (16) and the right wall is column (49). If +walls are impassable and you run into any of these the game is over (you +can slither at the edge of the wall); else you’ll come out the opposite +side.
The snake size is (5): but until you start to move it’s +technically 1 (even if you set SIZE=0 this applies). +This is so that you can decide which direction from the centre of the +screen. By default the head colour is red; the body is green. The snake +will grow by one until it reaches the right size.
The maximum snake size is (997): this is the default; it can be +changed but it will always have an upper limit depending on the terminal +size.
The number of bugs eaten so far is 0.
X:39/49 Y:8/16 S:5/658 B:0
+##################################################
+# #
+# #
+# #
+# #
+# #
+# #
+# oooo> #
+# #
+# * #
+# #
+# #
+# #
+# #
+# #
+##################################################
+I let the snake grow to its full size (with the head at (39),(8)) and +I’m two places above the bug so let’s go down:
+X:39/49 Y:9/16 S:5/658 B:0
+##################################################
+# #
+# #
+# #
+# #
+# #
+# #
+# oooo #
+# v #
+# * #
+# #
+# #
+# #
+# #
+# #
+##################################################
+Now you see that the snake is going down and in one move the bug will +be eaten. Let’s do that:
+X:36/49 Y:10/16 S:10/658 B:1
+##################################################
+# #
+# #
+# #
+# * #
+# #
+# #
+# #
+# #
+# ^ o #
+# oooo #
+# #
+# #
+# #
+# #
+##################################################
+Observe that the bug was eaten and a new bug spawned elsewhere. I +went left three and then up one and could slither on to the bug.
+By default walls are passable (WALLS=1). Let’s get +to full size and go through the top wall:
+X:36/49 Y:15/16 S:10/658 B:1
+##################################################
+# o #
+# o #
+# o #
+# * o #
+# o #
+# o #
+# o #
+# o #
+# o #
+# #
+# #
+# #
+# #
+# ^ #
+##################################################
+There you can see I have grown to the full size and went through the +top wall; the head is now at the opposite wall. Continuing:
+X:36/49 Y:11/16 S:10/658 B:1
+##################################################
+# o #
+# o #
+# o #
+# * o #
+# o #
+# #
+# #
+# #
+# #
+# ^ #
+# o #
+# o #
+# o #
+# o #
+##################################################
+I might then have turned to the left and then shortly go up to get +the bug. This goes on until you lose, quit or win the game.
+When you specify WALLS=0 the walls become an +obstacle that end the game. It might look like (I set the starting size +to 150 and had only just started):
+X:20/49 Y:2/16 S:150/658 B:0
+##################################################
+# ^ #
+# o #
+# o * #
+# o #
+# o #
+# o #
+# ooooo #
+# #
+# #
+# #
+# #
+# #
+# #
+# #
+##################################################
+If I continue going up I will see:
+OUCH!
+X:20/49 Y:1/16 S:150/658 B:0
+By default cannibalism is disabled (CANNIBAL=0) so +if you run into yourself it might look something like:
+X:45/49 Y:10/16 S:10/658 B:1
+##################################################
+# #
+# #
+# #
+# * #
+# #
+# #
+# #
+# ooooo #
+# ^ o #
+# ooo #
+# #
+# #
+# #
+# #
+##################################################
+If I were to do any movement but turn to the left I would run into +myself. I’ll go to the right two spaces:
+OW!
+X:47/49 Y:10/16 S:10/658 B:1
+But CANNIBAL=1 can look very funny both in movement +and still especially when you go through yourself several times when the +snake is rather long. For example going through walls and the snake a +few times:
+X:4/49 Y:12/16 S:165/658 B:3
+##################################################
+#oo o ooooo o o o#
+# oooo o ooo o#
+# ooooooooooooooooooo#
+# o #
+# oooooooooo o #
+# o o o #
+# o oooo o o #
+# o o o o #
+# ooooooooooooooo #
+# o o* #
+# oo> o o #
+# o oooooooo o #
+#oooooooooooooo o oooo oooooooo#
+# o o o o o #
+##################################################
+I’ll go back through myself now:
+X:6/49 Y:14/16 S:165/658 B:3
+##################################################
+#oo o ooooo o o o#
+# oooo o ooo o#
+# ooooooooooooooooooo#
+# o #
+# oooooooooo o #
+# o o o #
+# o oooo o o #
+# o o o o #
+# ooooooooooooooo #
+# o o* #
+# ooo o o #
+# oo oooooooo o #
+#ooooo>oooooooo o oooo oooooooo#
+# o o o o o #
+##################################################
+If you note that the head is at (6),(14) I will go one more to the +right and you will see that the arrow has moved over:
+X:7/49 Y:14/16 S:165/658 B:3
+##################################################
+#oo o ooooo o o o#
+# oooo o ooo o#
+# ooooooooooooooooooo#
+# o #
+# oooooooooo o #
+# o o o #
+# o oooo o o #
+# o o o o #
+# ooooooooooooooo #
+# o o* #
+# ooo o o #
+# oo oooooooo o #
+#oooooo>ooooooo o oooo oooooooo#
+# o o o o o #
+##################################################
+Try this: get a reasonable length and then go back on yourself (if +you’re going left go right etc.) and then after some length (but before +you get beyond the snake) turn (so that you go perpendicular) and then +watch as the snake starts unwinding at one place and then suddenly +unwinds another direction.
+An example of extreme cannibalism is in the cannibalism.log.html (cannibalism.log.md on GitHub) file. It +explains information about the file including how to find the bug, head +and tail of the snake, at least where it was documented. The first run +has several dumps. The final example has the snake inside itself.
+This feature will make you grow by the value of
+SHEDS for every
+SHED movements (the counter isn’t
+incremented when you eat a bug). Basically growing without eating a bug
+(stretching would be better name here).
If you ever get to the MAXSIZE the game
+is won. You can force a win by setting the max size to a smaller value
+than the size you specify (or the default) or setting the size to higher
+than the default (or < 0 which will end up being > the max size
+though I suppose there might be some value that makes it less than again
+- I’m not going to try and figure that out however). Anyway:
SIZE=-1 ./prog
+YOU WIN!
+X:24/49 Y:8/16 S:658/658 B:0Observe that the size isn’t the default 997: because the screen size +is too small for that value (see terminals.md).
+The reason I chose 997 is that it leaves a bit of a challenge for +some (maybe most) people but it’s still winnable. Previously even I +couldn’t have won unless maybe a much bigger screen, if both cheat modes +were disabled. I probably could win without any cheat mode enabled on my +24” monitor but I probably would need at least one cheat mode on my 16” +laptop (I don’t need both enabled). I don’t think I need either for +997.
+The beauty though is the game can be winnable by anyone since you can +define the max size to win. Games are meant to be fun: who enjoys games +that can never be won? Very few probably do and those who do enjoy them +it probably are games that are relaxing and stimulating (which +admittedly this game can be - but still I want it to be winnable +too).
+Another reason I chose 997: it’s prime and the judges love prime +numbers. Actually, this is the main reason for the number: I had changed +it down to 1000 but then it occurred to me that there are prime numbers +close to 1000 so why not choose one instead (that was less than 1000)? +And so I did. Another fun fact: at the default starting size and growth +size of 5 the number of bugs needed before the game is won is 199 - also +a prime!
+If you wish to quit you can hit ‘q’ (case-insensitive). For +example:
+X:24/49 Y:8/16 S:5/658 B:0
+Here I list a number of different modes of play. In the bugs.html (bugs.md on GitHub) +file I even show how there’s a built-in test unit for some modes! There +are certainly other modes that could be imagined: the modes below +happened over time and some were simply an accidental discovery or +something I thought of by a chance; others were thought out from the +beginning but were improved over time.
+The play.sh script sets up some of these +automatically; I describe it after the modes below.
+If you have photosensitive epilepsy please be
+careful especially with low values for both
+WAIT and
+EVADE. It appears that the number
+of hertz (flashes) per second triggering seizures is wide but
+depending on the variables it easily can be in the range.
There are different patterns and lights flashing at different speeds +so please exercise caution. This could trigger seizures and it +might also be too much stimulation for some people.
+Please be careful of these if these types of things have ever +affected you or if you have any form of epilepsy. Thank +you.
+If you want to draw the snake wherever you like on the screen you can +do this by modifying the wait time (for input) to -1 (or any value < +0): then the snake does not move until you press a movement key. For +example:
+WAIT=-1 ./progWhen you eat a bug the snake will grow and you can draw more. If you +want to make it so you can go through yourself:
+WAIT=-1 CANNIBAL=1 ./progIf you don’t want to start out so small (i.e. you want to be able to +draw more quickly) you can just set the size to a bigger number (and you +could do the same with growth and moult/shed):
+SIZE=50 CANNIBAL=1 WAIT=-1 ./progDo note that although you’re a bigger size you still grow one +movement at a time.
+Okay but then if you get too many bugs the drawing will be over. If +this proves to be a problem simply do:
+SIZE=100 CANNIBAL=1 WAIT=-1 GROW=0 ./progOr something like that (maybe make the size bigger but that’s up to +you). You could also set the max size to be the highest possible by +saying
+MAXSIZE=-1Which I’ve done in the play.sh script.
+I discuss how this mode works in [spoilers.markdown][].
+Here’s an example run where I spelt ‘SNAKE’. As you can see it’s far +from perfect: the snake after all does not leave gaps and the game does +not support diagonal movement and I also made a mistake and had to +modify what I was aiming for. But you can consider this mode drawing or +practice mode.
+ oooooooooo ooooo o oo ooooo
+ o o oo o oo o ooo o
+ oooo o oo ooooooo oooo ooooo
+ o o ooo oo o ooo o
+ oooo o oo ooo oooooooo>
+Photosensitive epilepsy/stimulation overload trigger +warning: For those who have epilepsy or are susceptible to +stimulation overload from flashing lights please do be careful with this +mode. Depending on your susceptibility this could very well trigger +seizures or other discomforts and I wouldn’t want that to happen to +anyone. This mode has a lot of flashing of different colours and +depending on the exact parameters you specify it could easily be in the +trigger range for epilepsy but potentially stimulation overload too.
+If you’re still here you can create this effect like:
+WAIT=0 WALLS=1 CANNIBAL=1 ./progAs long as the bug evades you’ll find that the snake is growing +because it’s actually eating bugs; the computer is actually playing the +game by itself! You can still move the snake though it won’t seem like +you have much control over it. If you pause the game you can see that +it’s bigger (you probably will be able to even without pausing it).
+Basically the snake is moving so fast that because the bug evades the +bug will inevitably move in the snake path’s and therefore be eaten.
+You might find it goes quicker if you set the evade count to a lower +value for example 1. Or you might not. But as long as the bug moves the +computer can play by itself.
+Quick tests suggest that bigger dimensions it gets bigger quicker. +But it also seems to take longer for it to get a bug as it gets longer. +I believe these happen because the bug has to find a X and Y coordinate +that’s not occupied and so with fewer available it has a harder time +finding a place in that one line.
+Here’s how to make the bug move every time the snake moves:
+CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 ./progIf you want to see the game ‘win’ try something like:
+CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 MAXSIZE=10 ./progHere’s a fun one I just thought of (a long time after these other +modes were thought of):
+WAIT=0 EVADE=1 GROW=-1 SIZE=9 MAXSIZE=10 CANNIBAL=1 ./progWhat you’ll see is that first the snake will get to the size of 9 - +whether it shows to be 10 characters depends on whether or not it gets a +bug before it can get to that length - and then it’ll start shrinking +until it’s just the head then the head again (i.e. 1 then 0 in size) and +then win! This is also in the play.sh script.
+Of course you can also do something like:
+WAIT=5 WALLS=1 CANNIBAL=1 ./progWhich although might not seem like it is significantly slower.
+If you press different direction keys in some pattern you will see a +pattern form too (entirely dependent on how fast you react, what keys +you press and I suppose also how much the bug moves and the chance of +being eaten also).
+Here’s an idea: how many moves is needed to make it possible to have +some sort of success in catching bugs? Obviously the dimensions will +affect this value.
+How long will it take for the snake to run into itself? For instance +by doing:
+WAIT=0 ./progOn my 13” MacBook Pro (replaced later by a 16”) at the default +settings going horizontally the snake ran into itself at:
+OW!
+X:57/156 Y:20/41 S:155/997 B:30
+What if you set the size to a high value? The lowest? There are bound +to be other games to play with this feature!
+In this mode you’re simultaneously growing and shrinking but because +the max size is unsigned you can win both by shrinking to the point of +losing your body or growing to the full size directly. This is how it +works:
+You have a negative shedding growth and a positive growth size: +negative and positive winning. Depending on the parameters specified it +might go one way over the other. Try:
+SIZE=150 SHEDS=-5 GROW=10 SHED=50 MAXSIZE=250 ./progWhat this does is set the size to 150 (to make it not go down so +quickly) and then for each bug you eat it adds 5 to the size; for every +50 moves (that you don’t get the bug) you shrink by 5. You will find +that at times you might only decrease in size but then at other times - +depending on size of screen and how lucky you are with where the bug is +relative to where it last was - you might be growing. Combining this +with the drawing mode could be interesting but I never actually tried +it.
+The parameters above were chosen after playing with the mode a bit to +find a value that wouldn’t take that much time but still would pose a +challenge. One could reverse it so that one shrinks more than +growing.
+The crazy.log.html (crazy.log.md on GitHub) file has several screen +dumps with this idea.
+You could also have it so that eating a bug makes you shrink and +moving so many moves you grow. I tried this and for me it was far more +fun. The play.sh also has this mode:
+SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./progObviously there are many other variations. The play.sh script has some of them (the above is option +5; others are 4, 15 and 16 but only options 4 and 5 are called Grow / +Shrink).
+Photosensitive epilepsy/stimulation overload trigger +warning: For those who have epilepsy or are susceptible to +stimulation overload from flashing lights and patterns please do be +careful with this mode. Depending on your susceptibility this could very +well trigger seizures or other discomforts and I wouldn’t want that to +happen to anyone. The default wait time is 231ms, the typical range of +seizures is 3 to 30 flashes per seconds (but can be higher) and that +means that at EVADE=1 every 231ms there will be a flash +in addition to the snake moving too.
+Anyway this might sound absurd but it can be somewhat hypnotic by +doing:
+EVADE=1 CANNIBAL=1 ./progAnd just moving the snake about not trying to catch any bugs - just +let whatever happens happen.
+If you just need something to stimulate your mind to keep you calm or +simply you want to play a game without having to worry about winning or +losing you can set the growth size to 0 and then just move the snake +round and round eating bugs until you want to quit. You can enable +cannibalism so that you don’t have to worry about even running into +yourself and losing:
+GROW=0 CANNIBAL=1 ./progAs I was writing this document and the game modes I have thought of
+other game modes (they were added) and I have come back to this document
+to add more modes. That’s the beauty of the variables I have
+implemented: together with the imagination and even just chance
+different modes of play can be devised. The drawing mode was a happy
+chance of how the timeout() function works for example.
As a person who was heavily involved in MUD development and design I +know very well how games need to be fun and have different options, not +be too easy but not be too hard either. And a game developer who does +not play their games is a game developer who is detached from their +player base and the wants and needs are therefore disregarded if even +heard. I have played this game a lot for this reason as well as I simply +enjoy it and thinking up new modes.
+The following scripts are provided to simplify and automate some of +the things this file discusses as well as allowing to easily change the +colours.
+To do this use the snake-colours script +like so:
+./snake-coloursIf you want to pass in specific variables you can do that too +e.g.:
+CANNIBAL=1 ./snake-coloursThe script will prompt you for the colour of the walls, snake head, +body and the bug.
+The play.sh script has the gameplay modes +described in this file and is menu based (there might be differences and +some of the modes I include a variation). The script tests the terminal +(allowing you to continue even if problems are detected), prompts you if +you want to select colours (like those in snake-colours above) and then +gives you a list of game modes to play. You can pass in the game +variables but note that if you pass in a variable that the mode you +choose uses then the script will override what you pass in.
+To use you can do something like:
+./play.shOr to make it so walls are an obstacle:
+WALLS=0 ./play.shThe game variables can be used for a variety of things including +different gameplay modes (as shown above) but here are some general +notes about a few of them.
+Besides the different game modes, if you find it too fast/slow you +can change it to a value that’s better for you. Remember it’s a wait +time so if you want the snake to move slower you have to increase the +wait time!
+For every EVADE snake movements the bug
+will try and find a new place to occupy. It might be very close to it
+and I do not ensure it’s a new place at all.
Each time the bug is placed the counter will be reset whether from +eating the bug or not reaching it in time. Default 300 moves.
+This can help you get out of a trap but it can also sometimes make it +harder.
+997 might be a bit high for many people. Or maybe you don’t have +enough time. Or you’re not in the mood to lose. Or you want to get close +to a size you were last time but quickly. It might be in conjunction +with drawing mode.
+Note on snake size: there’s a stated size (in the score line) which +is what the snake will ‘populate’ in the snake arrays and the visible +size: as I have discussed here and there the snake doesn’t grow +immediately so it takes time before the snake is visibly that many +characters. In other words just because it says SIZE 29 doesn’t mean it +will instantly be 29 characters on the screen.
+Similar to the initial size. Notice that the above point on size +applies here: it will take the number of movements before it is ‘its +full size’.
+I believe that this file has given more than enough information for +anyone to get an idea of what is possible with my implementation of +Snake. I realise also that most people won’t have read this far and +that’s quite all right; I’m very thorough and pay attention to details +obsessively. Creating this game was a delight and I am quite proud of +what I have done.
+Thank you for whatever level of attention you felt you could or +wanted to give and enjoy it in whatever way you see fit!
+Much more importantly is that Snake aside I hope I have +demonstrated how with the imagination and creativity almost anything is +possible. This is something that applies in our world +in every way and everyone can do something amazing if they +believe in themselves! I would have never thought this the case +for me years ago but I know it’s true now. Believe in yourself. +Always.
+| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ In the gameplay.html (gameplay.md on GitHub) file I have given enough +information to know how the game looks, how to move the snake, how to +pause etc. In this file I am including (some) information on how the +game actually works: the technical details as it were, obfuscation +techniques and also some of the ways I reduced the iocccsize so I could +do more with the code. Probably this file causes more spoilage than the +other files though that’s not to say everything will be answered +here.
+If you’re still interested in this file (i.e. you’re a judge or +someone who wants to know more about it) read on. Yes I write a lot. But +you probably already knew that so no harm there, hey?
+ +The gameplay I have already described but this explains the way +things are actually done in more detail.
+First I initialise curses; if this fails I print an error message
+(curses error) and return 1. Next the screen size is
+determined and I subtract 1 from both x/y. If
+it’s too small curses is ended and an error message
+(screen too small) is printed - returning 1.
Next the max size of the snake is determined: if MAXSIZE
+is specified then it’s the value of that; otherwise it’s the default.
+But in either case if it’s too big based on the screen dimensions it’s
+capsized. The arrays for the snake are this size + 1. Other
+environmental variables are also processed.
Then the arrays are allocated; if either return NULL curses is ended,
+an error message (memory error) is printed and return
+1.
If no errors have occurred I set up colours, turn on bold, set up the
+keypad etc., place the snake, the bug, draw the walls and then it waits
+for the first move (or quit of course). Technically I don’t ‘wait’ for
+input as such: unless the wait time is negative (drawing mode) it’s just
+a non-blocking call up to the WAIT milliseconds but in no
+event will the snake move until it is told to at least once (in drawing
+mode it will always wait).
In drawing mode if you pause then when you resume (hit space) the
+snake will move the last direction it had been told to move - because
+the direction is set and input was detected (the same goes for another
+key e.g. g - just like in normal mode). But it will then
+wait again for input.
The snake function both draws the snake and updates the arrays; if +the new size is >= the max size the game is won (the winning size +will be shown as the max).
+Else the arrays are updated; when the array is being updated the body +of the snake is drawn. After this the head is drawn. Depending on the +length of the snake wrt the size the snake technically it might be that +the number of characters the snake takes is increased rather than having +moved the snake forward (i.e. the tail moved by one). This registers +where the snake is and where it was but no longer is (important +particularly for negative growth/shed sizes).
+After that a bug is placed (but not drawn) in a pseudo-randomly +selected area of the field. This function takes a parameter which if not +0 and the bug had already been placed somewhere (in other words if the +bug was just eaten) call the snake function. As long as the game isn’t +won at that point find a new place to place the bug.
+Next the score line is updated and then whilst the game is not over +(player loses/quits/wins) the movement proceeds; if the game hasn’t +started yet it will jump to the top of the loop. If it’s paused the same +thing until it’s resumed. The rest of the loop can be described +thusly.
+It checks the current max y/x, draws the
+walls, bug, gets any input from keyboard, and then if there is movement
+checks for collisions; if current max x/y are
+is/are smaller than original then it’s automatically a game over from
+running into walls regardless of that feature status - this simplifies
+things greatly (see below for more details).
If cannibal mode is disabled (the default) and the snake runs into +itself then it’s also game over. Or did it run into the wall? If walls +are passable (the default) it will come out from the other side; else +it’s game over. If it comes out on the other side and the snake is there +and cannibal mode is disabled then it’s also game over.
+As the snake moves it will for each move grow by one up to the +technical size. This might at times look a bit different from other +times but it’s a visual thing based on the internal state of the game +and the features enabled for example shedding and shedding size.
+Finally if the snake’s new coordinates are on a bug a new bug is +placed on the field, the snake grows and the score is updated. Otherwise +if the bug has waited the number of moves that it waits before it finds +a new place the bug is placed in another location too (and the snake +does not grow). In either event the snake location is updated and +redrawn.
+There’s also the processing of any shedding at this point. If the +snake eats a bug the snake will not shed; if it sheds the snake will not +move in the same manner as if it simply moved. Growth/movement can in +some ways be conflated.
+The way the snake looks to move is that every time the snake moves
+the last position (the tail) is replaced with a space
+(' '). If the snake eats a bug then the snake will get
+bigger in the next N movements. Note that if the snake
+isn’t its full size it will only be growing and so might not appear to
+be moving at the tail end; I have discussed this more than enough
+elsewhere.
There are two arrays that store the last Y/X coordinates of the +snake’s positions that are shifted forward as the snake moves about the +field looking for bugs.
+How do these modes work? The timeout() function takes a
+signed int: if it’s < 0 it waits indefinitely; if it’s
+0 it returns immediately even if there’s no input waiting
+(if input is there then it’s used instead); and if it’s
+> 0 it waits that many milliseconds. Thus a negative
+value allows for complete control over the snake’s movement (speed and
+otherwise), a value of 0 means the snake will move as long
+as it’s not paused etc. and otherwise it waits up to that many
+milliseconds (which is how the 0 value comes into
+play).
This was an interesting thing to work out. Because the snake could +occupy more than one row and more than one column at a time the two +arrays approach seemed good to me.
+I already mentioned how the array is shifted but to be clear: the +snake head is always at element size - 1 and the tail is at 0 and so +it’s just a matter of verifying if the snake head is at a place that’s +occupied by the snake, the wall or the bug.
+The bug does its best to avoid landing where the snake is but given +that snakes are very flexible and cunning who can tell what might +happen? As I noted these bugs are not bugs! :)
+How does cannibalism change collision detection? The fact that each +time the snake function is called there is a space printed at the tail +end was a problem to resolve: what happened is if the snake went through +itself then at some point there would be a space that breaks the snake +into more than one piece (where the tail was but that had been another +part of the snake)!
+So what I do: first the tail is cleared, then as the snake +coordinates arrays is being updated I redraw the snake chars. Then at +the end the head is drawn (it used to be the head was drawn first but by +drawing it after you can see where the head is inside itself, creepy as +it is). Thinking on it this very possibly is why (or partly) the +negative shedding leaves the head somewhere in the middle of the snake +(or that the head of the snake is left behind) but again I don’t think +that as a problem.
+There are other strange ways this looks. Not only does it look like +lines intersecting at any number of places but it looks particularly +strange when the snake goes the opposite direction: it has to unwind as +it were, and for a moment you might not see what is truly happening. +There are two ways you can look at this I think.
+First is that it’s physically impossible in real life so the fact +this cheat mode exists for a different kind of play means it should look +unnatural: for it is unnatural!
+The second is you can look at it as if two more snakes are having fun +with each other. This can also appear to defy physics!
+Choose your poison. Or rather your venom.
+What happens if the window is resized during the game? If the size is +increased then the border can (and will) stay the same; but if the +window is shrunk then what? It would be very difficult if not impossible +to redraw the snake: where would the snake be and what about the border? +The arrays would have to be adjusted too! Another issue: what if the +snake is beyond the new walls?
+The best thing I could think of is that if the window is shrunk then +the snake is assumed to run into the wall: in other words the game is at +that point over. If the window is increased in size nothing happens +other than the game won’t be the full window size (or technically a few +rows/columns fewer): everything will remain the same. This GREATLY +simplified things.
+There was another problem at some point where the snake would quickly +grow if it was increased in size but I believe that’s resolved. Anyway +what kind of field in real life increases in size like that so suddenly? +:)
+I make use of the C token pasting operator. Although this is well +known it’s probably less known and either way it’s not as simple as just +having the code that it translates to. For example why have:
+ attroff(COLOR_PAIR(L));
+When you can have instead:
+ attroff(m(PAIR(L)));
+It’s an extra layer of indirection. This used to be more used but
+when making it easier for users to change the colour pairs I no longer
+used it to set up colour pairs. It might not defeat cpp but
+it does make for more reading otherwise (depending on how you look at it
+maybe less?).
It also helps save iocccsize bytes (but not as many as it used +to!).
What is this block of code?
+ F(MAXSIZE)g d g = f((a)((*y-2)*(y[-2]-2)),g);
+ F(GROW)N d
+ F(SIZE)A d
+ F(WAIT)y[-3] = (int) strtol(z,NULL,0));
+ F(EVADE)W d
+ /* and so on */
+There are several things going on here but I’ll focus on the main one
+here and leave the others to their own: the F macro and the
+associated d macro are here important.
In addition to saving many bytes this also makes for interesting
+reading. What this actually is doing is checking for environmental
+variables. If getenv() does not return NULL then strtoul()
+is called on it (except for the one that clearly shows strtol()).
There are other things going on (below).
+Now it might not defeat the cpp but can you tell me what this is even +with the cpp?
+ f((a)((*y-2)*(y[-2]-2)),g);
+The cpp might get it closer to what you might expect in a normal +program but I refer to the array offsets (below also).
Stringification: there isn’t a single " in the
+source code! The F macro uses the macro Z
+which does the real work. At first F was only
+getenv but I changed it to use stringification. Following
+the creation of the Z macro I made it use it instead. If I
+remember rightly that uses extra bytes but somehow it felt better this
+way in my head. Then to save many bytes I made the macro do much more
+than that (that is the F macro).
As for the Z() macro it means that I could remove "s;
+for example:
#define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n), Y, y[-2], X,*y,A,g, c);
+Which as I have discussed elsewhere is the entire set of parameters
+to both mvprintw() and printf() plus the end
+of the function call (i.e. );.
BTW: There’s another really cool benefit of stringification: because +more than one space is converted to a single space I can have it spread +across columns on the format and even new lines if necessary. This fact +along with the next technique causes some beautifiers problems +too.
Another use of the Z macro is this curious line:
static const char*s =Z( ; +<>^v);
+There are a number of curious parts. First is the Z() part makes it
+look like a function call. Of course that wouldn’t work as such because
+an array initialiser needs an initialiser list or a string literal.
+Whether that’s immediately obvious to others I don’t know but then how
+is this even possible? A ; followed by ,
++, <, >, ^,
+v?
A very minor factor is that the character ‘v’ is there and it’s
+(v) also used as a macro (well, define) ‘might’ be of
+something to some people. Or programs maybe? For instance using in vim
+* on the v in that array and it takes you to
+other instances of v: as in \<v\>: which
+happens to be something else entirely. That itself however might or
+might not be significant for this instance!
But what function would take the string (…function and string?) that
+it takes? The sequence of characters would normally cause compiler
+errors like that. I have it in the function that uses it; static since
+it need never change: only one copy is needed. Why I have it in the
+function is to move it further away from the definition of
+Z(). I will also be returning to this one again.
Another example is using the E() function e.g.:
E(Z(YOU WIN!));
+Which happens to be in the same function the above char array is +defined. Also it’s more like a string than anything but yet it’s not +visibly a string.
+Some beautifiers didn’t like this at all.
There’s an #undef following an if in a
+way to draw attention from what to do if the if is true.
There are a couple case statements that fall through to the next +case condition. Actually one falls twice: into the one below which falls +into the one below that. This means the order it is in is required. It +also reduces the iocccsize a reasonable amount (by having it in the main +loop I could do these things which reduced the number of keywords +needed).
+When quit is requested it falls into pause. But then pause falls into
+the default (so no recognised key is pressed) which does
+x = 0;. Outside the switch block there’s
+if (x) P = 0;. So what this amounts to is: if quit is
+requested pause will be changed to the opposite state. This will then
+fall to setting x to 0. And because x is 0 the state of pause won’t be
+updated again. Now because Q = 1; (or actually
+l) it doesn’t matter if P || !P because Q will
+also jump to the top of the ‘loop’ only to exit since the condition no
+longer is true.
But then if pause is pressed the same thing applies: it will be
+updated to the opposite state and then it will fall to the default case
+which sets x = 0; which means it won’t update pause
+outside the switch block. If pause was enabled then the loop will start
+over only this time because quit wasn’t requested it will re-enter the
+loop.
So basically quit can both pause and resume but it doesn’t matter +because it will still quit; and pause will fall to the default statement +without being updated again either. In other words the only time pause +will be updated (disabled) outside the switch is if a direction +key is pressed. But of course it’s disabled inside the switch if pause +key is pressed when it’s already paused! This removed three break +statements because the order allows it; if the order was changed then +the code would break in another way and not the good kind!
On the note of pausing there’s at least one instance of removing
+if..else (besides those in e.g. environmental variable
+processing) that’s curious: I converted this:
if (!P) P = 1;
+ else P = 0;
+To this:
+ (!P && (P = 1))||(P=0);Makes use of the different namespaces to have variables and
+labels be the same identifier. For example in main() there’s a variable
+that also have the same name label for goto (there used to
+be two pairs in main()).
And on that note: makes use of the ugly goto in
+numerous places. This includes simulating a while() loop,
+breaking out of that ‘loop’, breaking out of two loops (a
+for() in the 'while()'), re-entering (as in
+starting over) a for() loop and for dealing with
+errors.
Why do I check for Q || P and then
+goto x only to then check for !Q && !D
+to then goto x? Do I need to check Q at all?
+In fact I do but at a quick glance it might be easy to miss.
Uses an unnamed array (compound literal) for several different
+game variables. The pointer to the first element is defined like
+int *j:
static int Y,X,*j = (int[8]){231,0}, *U, *V, *e, *n, Q, P,x,*y,l=4,*D;
+Does the 231 seem familiar? It should: it’s the default key delay for
+the timeout() call (which I will return to again). The rest
+are 0 default. But what about j? Is it even used? Actually
+in main:
y = j+3;
+But aside from that j is not used. However…
Major abuse of negative pointer offsets along with confusing +names. I’ll say this in two parts: the negative offsets and then the +confusing names.
+Can you honestly tell me that this is easy to follow?
+ if (Y < 1 || Y >= y[-2] || X < 2 || X >= *y || y[-2] >= y[-1] || *y >= 1[y])
+ {
+ if (*p && !(*y>=1[y]||y[-2]>=y[-1]))
+ {
+ if (Y<1||Y>=y[-2])
+ Y = Y<1?y[-2]-1:1;
+ if (X<2||X>=*y)
+ X = X<2?*y-1:2;
+ }
+ /* ... */
+ }
+That’s not the only place negative offsets occur! Observe the ternary +operators there too.
+I’ll go back to the F() macro and in particular what
+follows one of them:
g d g = f((a)((*y-2)*(y[-2]-2)),g);
+To parse that at the very least you have to know that y
+points to j + 3. Then you have to know that j
+points to the first element of an unnamed array (compound literal). But
+then you also have to know that that array has different types of data.
+Once you figure out the elements in question you then have to account
+for the offset that y is. Then once you have that figured
+out you need to consider the negative offsets to y (this
+also applies to the block above of course and maybe more so). Then you
+have to think of operator precedence etc. I mean what is:
(*y-2) * (y[-2]-2])
+…about? An offset of -2 and subtract -2
+from that. Multiply that by the *y (which is
+j[3]) - 2.
But why is there a g d g = when the other lines only
+have the variable (or location) and a d? (Excepting the one
+that doesn’t refer to d.)
On the confusing names etc.: the getmaxyx() function
+gets the maximum Y, X dimensions. Now in the first call in
+main() I have the code and tests following:
getmaxyx(v, *y, y[-2]);
+ --*y; --y[-2];
+ if (y[-2] < 9 || *y < 9)
+ return E(Z(term too small));
+Clearly the max x must be stored in *y and
+the max y must be stored in .. y[-2]. After
+all anyone knows that the coordinates are in the order
+(X,Y)! In fact the output of my entry and my documentation
+demonstrates this clearly: X before Y! It’s so
+true in fact that the documentation for curses more than once states
+that it’s actually (Y,X)! But the fact I don’t even refer
+to a x is possibly confusing even without the negative
+offset.
Of course I don’t have to use negative offsets at all! I could use +positive offsets for every time I use negative offsets. But where’s the +fun in that? :)
+BTW: Some of these are not in the table of expressions in the HACKING.html (HACKING.md on GitHub) file. Also there might be +some code in there that is no longer correct (it was before I started +obfuscation) though it might be able to give an idea. Spoilers abound in +that file (in addition to this file) but the HACKING.html (HACKING.md on GitHub) file states things that are +no longer true: a way of obfuscation itself! Instead of misleading +comments it’s misleading statements in the file that explains what some +of the things are for!
Misleading variable names for coordinates. In other words the
+Y coordinate is called X and the
+X coordinate is called Y. This adds to the
+already confusing array references but it also adds to the confusion of
+the order of the curses functions coordinates: it appears that
+yes X does come first because the function calls that take
+coordinates suggest it - but in actuality it’s just that the names are
+deliberately swapped. Of course that’s after you determine that the
+variables X and Y are only the snake head but
+at that point you still have to figure out the array confusions. Also if
+you’re tired V could easily be read as Y and I
+have a lot of fun with V which I will get to next (and
+again). Finally why not have them be two different names unrelated to
+coordinates? I thought of this and I think it’s more confusing to have
+something that is associated to what it is but incorrectly rather than
+something unrelated entirely: the mind constructs things from what it
+has seen, what it experiences, what it knows, and since X
+and Y are used for coordinates the natural response of the
+brain is to assume Y is what is really X and
+X is what is really Y.
More confusion with variable names: If you notice the
+int *U, *V they are also part of the compound literal: that
+is they point into it:
U=y+2; V=U+1;
+Except that these two are done in two different places: away from the
+assignment of y to remove them further away from the
+contexts they are used in.
There’s another reason for moving it further down: if you look at the
+bug drawing function (which comes before main()) you will
+see that there I set the bug location to refer to the y
+‘array’. I do blank out the old location at *U, *V but then
+assign the new location differently. Then in main() I draw
+it at *U, *V and also check for the bug being eaten there
+too.
There’s another array that holds a variety of data.
+ static k p[5]={1,0},W=300;
+It holds the following information (but not necessarily in this +order):
+ WALLS
+ CANNIBAL
+ SHED
+ SHEDS
+ EVADE
+But how will you know that just looking at the declaration? Many +variables and they’re part of collections but the many layers of +indirection cause confusion to the already unhelpful variable names (for +those that even have names!).
There’s an array of pointers to functions too: it holds three
+functions but rather than always use the array I only do some of the
+times. I even use the same function in the same general area with both
+names in order to mislead. Incidentally splint seems to
+have a parse error on the declaration though I don’t know why.
On arrays/pointers in general I also use e.g. 3[p]
+and p[3] (there are many other examples that I have already
+shown).
Expanding on referring to the same thing in more than one way +take a look at:
+ case KEY_LEFT:
+ V[1] = l-3;
+ break;
+ case KEY_RIGHT:
+ V[l/l] = l/2;
+ break;
+ case KEY_UP:
+ V[l-3] = l-1;
+ break;
+ case KEY_DOWN:
+ V[l/4] = l;
+ break;
+What’s going on there? First of all the letter ell looks very much
+like the digit one but above there are only two references to the digit
+1; the others are the letter l. If you’re
+tired though, don’t have the best vision (I’m speaking from experience
+here and that’s not even considering the font) it would be easy to miss.
+Online in a text editor you have syntax highlighting of course but
+there’s more to it than that. Also in other parts of the code there are
+references to the digit 1 instead of the letter ell as part of array
+indexes too (so which is it?).
Second is that the index referenced in each of the case statements
+evaluate to the same: 1. That’s all accomplished by taking advantage of
+the value of l: 4.
And then to further make use I have done the same for the value to +assign to that element: 4 - 3 is 1, 4 / 2 is 2, 4 - 1 is 3 and 4 is +obviously 4.
+But doing it this way means you have to first of all know that
+there’s the variable l (conveniently declared at the top of
+the file); then you have to determine (once you’re at this place in the
+code) if it’s the digit or the letter - for each reference (since both
+exist)! After all it’s entirely possible that I could have negative
+offsets here (and there’s nothing that would prevent negating a variable
+either in this case): I have done it many other places! But even after
+all of that you still need to do the calculations when I could simply
+have had:
V[1] = 1; V[1] = 2; ...
+Etc.
Variable reuse. For example in the function E() there’s a
+variable that keeps track of whether or not it’s been called; that
+variable is h and it says whether to call
+endwin() or not in as in some cases E() is called more than
+once. Now instead of setting the variable h to 1 I set it
+to l: it looks like a 1 yes but it’s actually 4. Does it
+matter that it’s 4? No. The only thing that matters is that it’s a
+non-zero value. The same applies to Q which I also set to
+l.
I also use *y as a boolean. If you observe that I
+set x to *y (which itself might make one believe they’re
+related in some form): it used to be just a 1. However it is only used
+as a boolean. *y as I described as the LINES variable on
+invocation of the program. Since I check if it’s < 10 before
+continuing the game I know that it will always be non-zero. But what if
+someone tries LINES=0? In fact it will be ignored: if the terminal is 25
+lines and you specify 0 lines then it’ll be 25. Therefore I can use it
+as a boolean here since it needs there to be non-zero. Then in order to
+get the opposite, 0, I can do !*y which I do
+elsewhere.
More generally on variable reuse: the compound literals and +arrays of the same data type but which are completely different things +means I can use the same name in different parts and so it’s then a +matter of determining which element/offset to use.
Why do I compare if the original dimensions are >= the current +when the idea is to make sure that they’re actually bigger? How does +that work? Why not just check if it’s > than the current?
More confusion with the max x/y dimensions: Going back to max x/y +dimensions and coordinates of the snake; I showed this code before:
+ if (Y < 1 || Y >= y[-2] || X < 2 || X >= *y || y[-2] >= y[-1] || *y >= 1[y])
+ {
+ if (*p && !(*y>=1[y]||y[-2]>=y[-1]))
+ {
+ if (Y<1||Y>=y[-2])
+ Y = Y<1?y[-2]-1:1;
+ if (X<2||X>=*y)
+ X = X<2?*y-1:2;
+ }
+ else
+ {
+ /* ... */
+ }
+ }
+I asked if you could honestly figure that out easily. But here’s a +bit more elaboration. I already talked about the negative offsets - and +they should talk enough themselves - but look at the mess. Even if they +weren’t negative offsets there are a lot of array references (though by +a pointer to an element in the unnamed array) and comparisons all +jumbled together. There’s more.
+I have the ! of a || to flip the logic:
+that’s not how a normal person would write code. Well…not usually, I
+guess; I have done it in times where it seems more appropriate but it’s
+not the way it’s usually done (I think?).
Even so that part is doing more than one thing: Not only is that code +making sure the max dimensions aren’t smaller (from the original) but it +also makes sure that the wall isn’t hit (when it’s not allowed) as well +as making the snake go through to the other side of the wall if the +snake is allowed to and hits the wall! And even when the snake is +allowed to go through walls if the screen size has shrunk it will reach +the else part.
+It also has the ternary operator to adjust the coordinates of the
+snake head should the snake go through a wall and those also have
+negative offsets. But how can you figure that out just looking at that
+mess? Unhelpfully the X/Y seem to both be compared to y but
+never x (that variable is unrelated and not even in this
+part of the code).
The function B() ends with a goto. Why?
+It’s also another place where there’s at least one negative
+offset.
A note on the many #defines in my entry. You say you
+dislike them:
Doing masses of #defines to obscure the source has become 'old'. We
+ tend to 'see thru' masses of #defines due to our pre-processor tests
+ that we apply. Simply abusing #defines or -Dfoo=bar won't go as far
+ as a program that is more well rounded in confusion.
+As I have noted though many of these save quite a lot of bytes too.
+It’s true that they might aid (but barely) in obfuscation but I think
+that my entry is more rounded in confusion; certainly the other
+techniques I have mentioned include much more than just
+#defines. One of the #defines that saves a
+significant number of bytes is of course J: I use that
+twice and it would be a huge burden to include it twice!
At the same time I have removed some #defines that have
+added bytes; I’ve also removed some that saved bytes and in fact I think
+it makes it more obscure if in no other way but making it more difficult
+to look at (for example I might have been able to save bytes by defining
+for the array offsets but I find that it looks much better - which is to
+say much worse - the way I have it now).
Going back to the curious
+static char s[]=Z(; +<>^v);: there’s something
+magical about the number 7 isn’t there? I do not refer to primes
+although certainly one could say that primes have a magic to them.
+No. I’m instead referring to another property: bitwise AND. For example
+consider the following:
7 & 0
+ 7 & 1
+ 7 & 2
+ 7 & 3
+ 7 & 4
+ 7 & 5
+ 7 & 6
+ 7 & 7
+What is the result of each operation? Quite simply it’s the number
+itself! How does this relate to the s array though? Let me
+back up. Is there any place in the code that has the number 7 (by itself
+- 997 doesn’t count)? A quick check will answer that: no. But here’s a
+curious thing:
q X, Y, s[*D+2&(1<<3)-!Q]);
+We KNOW that at this place Q is 0 so the !Q
+has to be 1. Now 1 << 3 is 8. Therefore
(1 << 3) - !Q == 7
+But what’s *D? If the snake hasn’t moved once yet it’s
+0; otherwise it’s the number of the direction the snake is currently
+facing. Thus
*D&(1<<3)-!Q
+…refers to *D. However if I were to have that be the
+element it wouldn’t work right: I wanted the semicolon in there but the
+space was needed to reduce the iocccsize down by one (ironic that adding
+a byte reduces it by one but there it is). I could have had it later in
+the array but this felt slightly more confusing: more operators (so
+precedence) to consider (and in fact GCC complains about this but as is
+clearly demonstrated it’s not important). By adding a +2 it
+adjusts the index to be the correct value in order to show the proper
+index of the char array.
But how do you know this? You have to know first of all that Q will
+always be 0 at this point (so therefore !Q == 1). You also have to
+understand that in the range N = 0..7 the value of
+N & 7 is N (which might not be something
+everyone realises). Obviously you need to know that (1 << 3) is 8
+in order to get the final *D & 7.
You furthermore need to know that *D will always be in
+the range 0..4 which means that *D & 7
+will always be in the range 0..4 (If I were to have added a
+& 7 to the end it’d still be the correct value! But the
+use of bytes couldn’t be justified there.) Finally adding 2 will get the
+correct value of the array index. After all that it will print the
+correct character for the head!
But there’s another trick here: there’s never a direct assignment to
+*D: D is set to 1 + V and
+*D is never touched directly: it’s all through
+1[V]. The references to directions
+i.e. KEY_LEFT etc. and also the switch() to see how to
+modify X/Y are all done through the V pointer. But then
+what is V anyway? It’s U + 1; and
+U happens to be y + 2. But y
+happens to be j + 3. Of course j is a pointer
+to the first element of the compound literal! So there are quite a lot
+of levels of indirection in this part alone (not to mention other
+places)!
There are a few differences in this version that are worth noting. +First of all is a define that’s in the compiler invocation Makefile is +in the source file itself. Second are the following obfuscation +techniques. To simplify integrating the explanations I have simply +yanked and pasted them below:
+Variable reuse in ways that might not be so intuitive. For
+example in the function E() there’s a variable that keeps track of
+whether or not it’s been called; that variable is h and it
+says whether to call endwin() or not as in some cases E()
+is called more than once. Now instead of setting the variable
+h to 1 I set it to *y. Now as you can see
+*y starts as a NULL pointer; this is set to the compound
+literal j + 3. This in turn is what LINES is (number of
+lines). So y = j + 3; and j I discuss
+elsewhere. But what if LINES is set to 0? LINES being set to 0 sets it
+to the terminal max size but in any event I test for values < 10 and
+if either LINES or COLUMNS is < 10 then E() is called. However E()
+always returns 1 and since this is in main() and it’s an error I simply
+return E() and thus it’s only called once. Similarly for Q
+I set it to l. But then..what is l?
In fact l is assigned a value 4 via a compound
+literal. It’s declared at the top of the code and then in main()
+assigned like: l = ++(int) { 3 } ; which equates to 4. I
+think that’s pretty cool!
What is this?
+for(I=0; A&&I<A-(A-A||A); ++I). In fact the
+A-(A-A||A) is equivalent to A-1:
+A here is always > 0 so A-A is 0; however
+because A is non-zero the expression A-A||A is
+equivalent to 0 || A and since A is non-zero we know that
+the value is 1 and thus it’s A - 1! I quite
+like this technique: it doesn’t make sense at first glance and it
+probably remains elusive until you look at it more thoroughly. It
+compares that I < A - 1 but the 1 is
+calculated by subtracting from A the value of subtracting
+A from itself || A and A is
+always non-zero (as the condition says) which means it’s
+A - 1!
There might be more things that differ but those are the significant +ones.
+Of course none of the above is so easy to see without a beautifier. +Why did I choose the format I did? Well it came about in a sequence of +possible layouts. I tried first to make it look like the game might +itself but the trouble with that is it’s too random. Then I thought +maybe I could spell out the words SNAKE 2020 or IOCCC 2020 or something +like that. But these were proving to be a problem for me; what I was +seeing on the screen just wasn’t looking right in my head.
+At that point I just started playing with different patterns and I +quite like what it’s turned out to be. I don’t know what I would call it +but one could look at it and think of columns holding up a structure. +There’s also a symmetry to it and almost certainly that’s what is most +attractive about it to me for I love symmetry.
+Some final notes on the layout I’d like to point out. First there are
+53 lines counting blank lines. Second is that if you count the number of
+lines (of code) in each ‘block’ they should all be a prime (except the
+two blocks of 1, I guess). Third is that I tried to make sure that each
+column of the code (not counting the preprocessor - see below on that)
+start and stop at the same position (as in column in the row/column
+kind) on each row. Below I show the start and end column of each
+horizontal block. The #undef in the middle should be
+centred based on its length and the 120 columns for total. The
+preprocessor directives should also be centred in that way (that was my
+goal anyway).
The following were intentional:
+53 lines.
The number of lines per vertical block (3, 5, 3, 11, 7, 1, 1, 5, +7).
Number of horizontal blocks (5)
Number of vertical blocks (9)
iocccsize 2053.
The symmetry was even more important to my mind; observe that each +vertical block (or column) starts and ends on the same column (or at +least that was my goal; I think I did it but maybe I missed one here +and/or there):
+Furthermore based on the length of each C preprocessor directive (the
+#defines, the #includes, the
+#undef) I have attempted to centre them in the file based
+on the 120 columns total. I believe I have succeeded in that though
+again maybe I have an off by one or two here and/or there.
Most of the primes above were intentional.
+This happened in a number of ways and I have already documented some +in other parts. Now there are a number of reasons to do this: amongst +them to make it possible to obfuscate (plus a more unique layout) but +also to add more features.
+Interestingly some of the obfuscation saved bytes; others of course
+added bytes. For example token pasting saved a few bytes; on the other
+hand the combining ifs to the complicated one above added 9
+bytes: but another one saved a byte. I have done this more than once so
+it’s entirely possible there are others I am forgetting.
Obviously a more significant one is #define q mvprintw:
+this saved a total of 47 bytes! This was however done towards the end
+(or so I thought it was the end; it was anything but) when I started
+looking at obfuscation rather than adding features. But then because of
+this I looked at adding more features!
Another thing that reduced a lot of bytes - 34 to be exact - is using
+the mvvline() and mvhline() ncurses functions
+rather than use three for loops to draw the walls.
Much later on I made it so that the E() function returns
+1; this was a clever way to have what I already did have - return 1 if
+there’s an error (i.e. memory error or curses error or screen too small)
+but otherwise return 0 without having to have a separate int and I could
+get rid of two {} pairs as well as add another use for the
+E() function: it calls endwin() exactly once (even though
+when there isn’t an error condition it can be called more than once) as
+well as printing the final score - or else an error. And now it also
+allows for printing an error and returning from main() at the same time!
+This also means that by returning E() I am returning 1 but
+if the end of main() is reached 0 is returned. In either case curses is
+ended.
One of these optimisations saved four bytes simply by taking +advantage of the value of a loop iterator after the loop. The loop +is:
+for (I = 0; A && I < A - 1; ++I)However after the loop I had this:
+A > 1 && q(n[A - 1],e[A - 1], 'o');When I was considering moving that line to before the loop it
+occurred to me that as long as A > 0 then
+A - 1 == I after the loop. I still had to check for
+A > 1 but I could refer to just I instead
+of A - 1!
There were quite a few other things besides these and these gains
+were useful to add additional features and also to add proper error
+reporting (when calloc() failed I called
+abort() and before that if initialising ncurses failed I
+also called abort() and did not report any errors in either
+event but I do now).
As I was getting to the end of obfuscation (wrt size available - and +it was getting pretty obfuscated too - the majority of the techniques +were already in place at this point) I was looking at the code to see if +I could figure out how to save even more bytes. Here are some that I +found interesting ways to go about the problem.
+First, the function that turns on colours looked like:
+if (h) attron(m(PAIR(L)));
+else attroff(m(PAIR(L)));But I thought is that else actually needed? If I were to switch the
+order so that the attroff() is called first then I could
+just do an if (h) - therefore getting rid of the
+else! But then I thought why not do what I’ve done
+elsewhere? I had:
attroff(m(PAIR(L)));
+if (h) attron(m(PAIR(L)));But I then saved an extra byte by making it:
+attroff(m(PAIR(L)));
+h && attron(m(PAIR(L)));That saved two bytes.
+Second let’s look at the B() function: there were two
+things I thought of that would save some bytes (I no longer know how
+many but a few at least possibly six or even more). I had the following
+code:
if (V && U && o) { S(N); b(1); }This made sure that only if the snake has caught a bug should it grow
+by the growth size and increase the bug count. But was checking for the
+V or U necessary? I gave it a look over and it
+occurred to me: the only time B() is called with
+1 (thus o is 1 so the if is true) was if the
+bug actually is caught. That means I could remove the
+V && U && part of the if! Thus it became
+instead:
if (o) { S(N); b(1); }Then this
+!o && V && U && q U,V,' ');…is to make sure to only clear the spot the bug is (or was) at if the +bug was NOT eaten. Why not eaten? Because if it was eaten it means the +snake’s head will be there!
+But why did I check for the bug having coordinates? Because I thought +that if the bug has coordinates obviously the function has been called +before which means that we do clear (write a space) over a place the bug +was actually at. But I thought could there maybe be another way of +verifying if the game has started yet?
+Before I ask that question I’ll point out this: is there a reason it +has to be checked? Yes because right after this line new coordinates are +found. But unfortunately the snake has to be placed first since the bug +cannot be placed where the snake already is. So if I had to make sure +the game had started (the snake has moved at least once) what could I +check? In fact that means the direction is not 0! So I could just +replace it with:
+!o && D && q *U,*V,' ');That reduced the count further. But then I wondered if even that much
+is needed. The condition is that the bug has evaded which means the
+snake has moved already (so game started). What calls to this function
+happen before the game starts though? The original placement of the bug.
+Well if the above check makes sure that the bug isn’t where the snake is
+at it means that the snake has already been placed, right? Going further
+the snake always starts out at the centre (or rather the close as
+possible - rounding maybe an issue depending on the max y/x): in other
+words not at 0, 0. This means that the only thing I should have to check
+for is that !o: because after this call before the game
+even starts the score line is updated: so the empty space will be
+overwritten anyway. And if the score line is made an empty string it
+doesn’t matter because it’ll appear as all spaces anyway. Thus I could
+save another three bytes by changing it to:
!o && q *U,*V,' ');Technically it would be a " " (see HACKING.html or on GitHub HACKING.md) else for N (not referring to the
+variable though it might very well matter here too) movements (i.e. due
+to sizes) you will at times (e.g. after eating a bug) see the snake body
+char at 0,0. I’m not bothered about that though because the score line
+is meant to display something, it squeezes a few more bytes and having a
+space is hardly an imposition on usage (and for that matter having a ‘o’
+at 0,0 isn’t either).
| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ Terminals are very easy to mess up and for that reason they deserve +their own file. I discuss terminal capabilities, dimensions (setting and +how to restore sanity if it causes any problems) as well as colours +(limitations etc.).
+ +Although it should be playable without the features I use (except +that cursor movement is required) I have provided a source file that can +test your terminal for the capabilities I use in the entry.
+It also tries to determine the minimum number of columns to play +without the status line overflowing - even with the dynamic length. +Try:
+$ make testThis compiles the program termcaps.c and +runs it; you should see something like:
+terminal supports cursor movement
+terminal supports making cursor invisible
+terminal supports bold
+terminal supports colours
+
+terminal rows 41 (38 playable)
+terminal cols 155 (153 playable)
+
+snake size: 997 (max size: 5776)
+ bugs: 199 (max size: 1155)
+
+at least 34 columns (currently 155) recommended for snake size 997
+at least 37 columns (currently 155) recommended for capped snake size 5776
+
+No problems detected.
+Terminal rows and cols are the current values (the number of columns
+shown later as well). The snake size is the default size to win (based
+on the variables, see below). The max size: 5776 is the
+capped size (or ‘capsize’). The 199 bugs is approximately the
+snake_size / growth_size i.e. how many bugs it will take to
+win the game. The max bugs in the bugs line is
+approximately max_snake_size / growth_size i.e. how many
+bugs it’ll take to win at the maximum (capped) snake size.
If I pass in any of the variables GROW,
+MAXSIZE,
+SIZE and/or
+LINES or
+COLUMNS it bases its calculations on those
+variables. For example:
terminal supports cursor movement
+terminal supports making cursor invisible
+terminal supports bold
+terminal supports colours
+
+terminal rows 41 (38 playable)
+terminal cols 155 (153 playable)
+
+snake size: 5776 (max size: 5776)
+ bugs: 5771 (max size: 5771)
+
+at least 37 columns (currently 155) recommended for snake size 5776
+at least 37 columns (currently 155) recommended for capped snake size 5776
+
+No problems detected.As you can see it tells you the max size (the ‘capsized’ snake) as +well as the requested max size (if the requested size is bigger than the +capped size then it will be set to that value instead) of the snake. The +capped size is like in the Snake game itself but the suggested minimum +number of columns is based on a number of variables.
+If I were to do the above but set columns to less < 35 then there +would be a problem. But why if the utility recommends at least 37? This +is because the max size of the snake also depends on the terminal size. +This utility is not perfect: I don’t consider shedding; I set growth to +5 if it’s 0 (since it would be indeterminate how many bugs are possible +before ‘winning’ since there would be no ‘winning’); and there are +probably other things too.
+As for capabilities: besides cursor movement the others are just nice +to have. From my tests keeping the cursor visible doesn’t ruin gameplay +but it is less distracting if nothing else to have it invisible (in fact +due to the sequence of function calls the cursor would mostly appear to +be not in the game field itself - from memory at least).
+Maybe there are other capabilities I should check for that I’m +unaware of but this should give you an idea.
+If there are any problems it will say how many (fatal and otherwise). +The fatal problems are if curses fails to initialise or if movement of +cursor cannot be done (both seem unlikely to happen nowadays).
+Both scripts run this utility and prompt if there are any problems; +you can continue either way.
+There are a number of ways to do this and only one will keep the +terminal sane all the time but that’s by running it under a terminal +emulator that you can resize the window with first. But what if you want +exacts?
+To specify the lines/columns of curses program per instance use the
+LINES and COLUMNS environmental variables.
For demonstration consider the following:
+ $ echo $LINES $COLUMNS
+ 42 157
+Note: I subtract 1 from the max y/x and the max coords in the game +output include the walls and score line but these values are for the +terminal itself.
+Say you want 55 columns:
+ $ COLUMNS=55 ./prog
+This will force curses to detect the max columns (X) of your terminal +(for the game dimensions) - to be 55; it might look like this +instead:
+ X:27/54 Y:20/41 S:5/997 B:0
+But problems can arise with messing with dimensions: in my tests this +held only to lines.
+Terminal sanity can be compromised and it can cause all sorts of +output problems; it might not appear right away but it will show its +ugly head eventually if you use it. For example pager output might be +screwy, echo might be turned off, output of commands might overwrite the +input of those commands, etc. For the game you might not see the final +score.
+In my tests there is one thing that at least allows you to see the
+final score and that is by running clear before running the
+program. That’s what the prog.alt version does. You might run it as one
+of:
LINES=20 ./prog.alt
+ LINES=20 ./snake.alt
+Even if the final score is visible though the terminal may very well
+still be messed up. There are numerous things you might try but probably
+the easiest is to run the game again without modifying the dimensions
+(and then just quit) or even just the reset command.
The Linux man page says:
+You might have to run the `reset` tool like:
+
+ <LF>reset<LF>
+
+ (the line-feed character is normally control-J) to get the terminal
+ to work, as carriage-return may no longer work in the abnormal state.
+If you don’t have the reset utility you can try:
+ echo -e \\033c
+Otherwise try clear; stty sane (that will turn on echo
+also) or if all else fails log out and back in.
For more information on this subject generally see the Linux +keyboard and console HOWTO (specifically Linux +keyboard and console HOWTO section 4). Yes this works under macOS +(at least it did for me).
+So players 1, 2 and 3 are content with not meddling with the terminal +sizes outside what’s actually possible with their screen. But then +player 4 comes along and thinks he/she will be clever and mess with the +dimensions a bit. Let’s say they have a 13” monitor and they think it’d +be funny to see how 997 lines would work; they might try something +like:
+ LINES=997 ./prog
+What then? Well expect trouble; curses will not know any better and +the max number of lines will be 997. If they were to do:
+ COLUMNS=997 ./prog
+Then they will see a different effect. Again the same reason: the +player trying to be funny.
+The game will try and be funny in return and you can expect it to not +function properly. Because that’s what funny means in this context.
+It’d be nice if curses could detect hitting more than one arrow key
+at the same time so that diagonal directions would be possible but
+unfortunately it’s not (cat -v confirms this). Even if I
+were to define four extra keys what would they be? And what about the
+head character? The HACKING.html (HACKING on GitHub) file has information on
+this.
I also limit the terminal size to 10 lines/columns but most likely it +would require the cheat modes to win and in any event allowing certain +low values causes problems e.g. LINES=6 COLUMNS=6 and +LINES=3 COLUMNS=3 caused some nasty issues.
+N.B.: If all you’re after is wanting to change the colours see the +script snake-colours.sh instead. The rest +of this file was mostly notes for the judges; here I discuss the +background, foreground as well as limitations (in particular runtime +limitations).
+The documentation I have seen says that the background will always be +black and from a quick test this holds even with a terminal that has a +white background. There is a way to force the background to remain white +though that’s by way of monochrome (so no colours); I discuss that and +another related terminal specific in the troubleshooting guide (there’s +another way but it would take me over the iocccsize; I talk about it in +the HACKING.html (HACKING on GitHub) file).
+According to my tests for monochrome terminals the screen has the +white and black background; if terminal is configured to be black +background white foreground that is the order it is too. You can try +that like:
+ TERM=linux-m ./prog
+But I do not specifically check if the terminal has colours. Is there +a curses implementation that has a problem here? Below I explain what to +comment out should you need colours to be disabled but I personally have +not run into any problems even with monochrome terminals. Later I say +what to do to remove colour support in the source code itself.
+According to X/Open Curses, Issue 7 (pg. 57):
+With init_pair() and pair_content(), the value of pair must be in a range
+from 0 to and including COLOR_PAIRS-1. (There may be an
+implementation-specific upper limit on the valid value of pair, but any such
+limit is at least 63.) Valid values for f and b are the range from 0 to and
+including COLORS-1.
+The manpage for init_pair() says this:
These limits apply to color values and color pairs. Values outside these limits are not legal, and may result in a runtime error:
+
+ [...]
+
+ COLOR_PAIRS corresponds to the terminal database's max_pairs capability, (see terminfo(5)).
+Based on the two as well as the fact that monochrome terminals (I’ve +tried under both Linux and macOS) seem to work fine I believe I am fine +here too. And to confirm this I wrote the following code with the output +below:
+ #include <curses.h>
+
+ int main()
+ {
+ initscr();
+ start_color();
+ printw("COLORS: %d\nCOLOR_PAIRS: %d\n", COLORS, COLOR_PAIRS);
+ getch();
+ endwin();
+ }
+
+ TERM=linux-m ./colour_pairs
+ COLORS: 0
+ COLOR_PAIRS: 0
+Without specifying the TERM I get:
+ COLORS: 256
+ COLOR_PAIRS: 32767
+In the case that colour is supported the standard states that there +will be more possible colour pairs than I have used so that’s not a +problem either. Perhaps even it would only be a runtime error if the +colours are supported and the number is out of the range? To try and +figure this out I did another test:
+ int main()
+ {
+ initscr();
+ start_color();
+
+ init_pair(COLOR_PAIRS+1, COLOR_WHITE, COLOR_GREEN);
+ bkgd(COLOR_PAIR(COLOR_PAIRS+1));
+ init_pair(1, COLOR_WHITE, COLOR_BLACK);
+ attron(COLOR_PAIR(1));
+ printw("COLORS: %d\nCOLOR_PAIRS: %d\n", COLORS, COLOR_PAIRS);
+
+ getch();
+ endwin();
+ }
+Running it like
+ TERM=linux-m ./colour_pairs
+I got the output as if colours weren’t used: 0 and 0. With coloured
+terminals however it was not visible. If I however comment out the call
+to bkgd() I get instead:
COLORS: 256
+ COLOR_PAIRS: 32767
+Which leads me to believe that if you try using a value outside the +range it won’t work for a colour-capable terminal but it doesn’t matter +for monochrome terminals one way or another. And since I don’t go beyond +the ranges (as I cited earlier) there shouldn’t be any problem here.
+Nevertheless if it is a problem and you don’t want to use a +monochrome terminal (or can’t):
+First comment out the code that has start_color(); and
+then add return; to the top of the C()
+function (the rest of the code won’t be reached). If necessary you can
+also comment out the init_pair() calls.
Perhaps you only need to add the return; to
+C(). My guess is that’s the case but I do not know for
+certain.
| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ In this file I have a list of a number of possible problems that +could show up under some situations as well as some things that might +appear to be happening or be a problem but actually are not. For each +one I have included a summary, a more thorough description and any +resolutions (from easiest to hardest). For anything that can cause more +than one issue I will list those separately to keep it as structured as +possible/easiest to find.
+- The top wall isn’t (all/some +of the time) visible!
+I set SIZE=0 GROW=1, ate a bug but still only +the head was visible! Why?
I like black text on a white background but +even with a white background terminal it ends up black!
Can I move the snake faster temporarily +without having to modify the wait time?
On environmental variables more generally and +string to int conversions
Because the score line is dynamic in length depending on the terminal +size it can happen that the score/status wraps to the next line(s). This +can be fixed by modifying the status line (see HACKING.html or on GitHub HACKING.md) but you shouldn’t need it to be very +wide.
+RESOLUTION #0: Use a bigger screen (it doesn’t take that many +columns)
+Tip: try running make test to get an
+estimate of the minimum number of columns needed. This won’t work right
+if you change the score line!
Note that this utility is not perfect; I describe this elsewhere. +Either way a small screen will make it harder to play esp without the +cheat modes enabled (but they can still be fun with large +screens).
RESOLUTION #1: Open the prog.c and change +the line
+ #define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n),Y,y[-2],X, *y, A, g, c );
+To be something like:
+ #define J Z(B:%zu\n), c);
+Recompile and try again. That will then show only the number of bugs +eaten. See the HACKING.html (HACKING.md on GitHub) file for other data you can +include (though there are some discrepancies here and there as you’ll +see).
Unless the game is paused this should not be happening. It can appear +sometimes that there isn’t movement and sometimes it might have to take +a moment to ‘unwind’ but as long as the head is moving then everything +is fine.
+The tail might not always be moving which could make it seem like the +snake isn’t moving but as long as the head is moving then the snake can +be assumed to be moving.
+RESOLUTION #0: Unless the game is paused as long as the snake +head is moving everything is fine even if it seems sometimes that the +snake tail is still: this is normal and there are a few conditions that +this might especially show itself e.g. when the snake is growing.
+If you want to see this in action try:
+ SIZE=150 SHEDS=10 SHED=1 ./prog
+Once you start moving you’ll see that the tail stays still for a +while and when it does move you’ll see it doesn’t move for long.
+Sometimes also it might appear that the movement is delayed but there +are numerous causes for this some of which are a busy computer but +ultimately it’ll catch up; just give it the time it needs.
BOTTOM LINE: If you want more information then see the bugs.html (bugs.md on GitHub) +file.
Possibly this could happen with too long a score line which I already +discussed. It also used to be possible during cannibal mode whilst +cannibalising: you would just have to continue moving on and you’d +eventually find your head (assuming the rest of the screen isn’t covered +with your body!). And what else could you expect if you decided to shove +your head up your …. ? :))
+RESOLUTION #0: Actually I thought it would look better that you +could see the head even when cannibalising so you should be able to lose +your head and at the same time see it!
RESOLUTION #1: If the terminal width is too small then it could
+be that the score line is covering the wall, snake and even bug; I gave
+two resolutions to that earlier in this file. To get a recommended
+number of columns use make test.
This has happened to me a number of times and early in development +there were times that I could swear I even saw it beyond the walls but +this should not be a problem now if it ever was.
+However I have made a lot of effort to think of situations that might +make the bug location calculation and placement be a problem and even +did some debug output and wrote another test program to calculate the +maximum number of places the bug could be put etc.
+One of the reasons I have the terminal dimensions have a minimum and +also a maximum has to do with the way the bug placement and max size of +the snake (and so arrays) are calculated: it could happen that because +of it being unsigned without there being restrictions it could end up +being a bad value that causes the bug placement loop to never end (in +this case the snake would also stop moving).
+RESOLUTION #0: If evasion mode is enabled (default after 300 +moves) just wait it out and the bug will move to a new place and it +might be easier to see in the new place.
RESOLUTION #1: The terminal size is too small for the score line. +See the earlier problem for how to resolve this if that’s the +case.
RESOLUTION #2: Sometimes you have to look closely for it can be
+easy to miss. If it’s against the wall and the screen is Snakey
+or you’re tired or the colour contrast isn’t right for you or something
+like that it might be harder to see. Try pausing the game (space) and
+scan the screen for it. If you really are desperate copy and paste the
+entire file into a document and search for the bug char (if it’s an
+editor like vi/vim you will have to be sure to escape the char since
+* is a regexp metacharacter). This has happened to me
+numerous times.
RESOLUTION #3: If the colour contrast is not good for your eyes +use the snake-colours script to select a +different colour scheme.
This should not be possible either but without certain safety checks +in place I have caused this (and I did this deliberately in development +to find all the conditions I could to resolve them).
+RESOLUTION #0: If this does happen it likely is to do with +placement of the bug; that is why I have the max size of the snake +capped a fair bit below the maximum number of coordinates in the game +field: because the bug isn’t to be at a place the snake already +occupies.
RESOLUTION #1: If the system rand() implementation
+is really bad maybe it’s having a problem finding a location to place
+the bug. The best I can think of is to try increasing the screen size or
+decrease the max size of the snake.
BOTTOM LINE: If the bug cannot find a place to be this will +likely cause the game to freeze because the game loops until it finds an +available place for the bug to be; if the pRNG is so bad wrt where the +snake occupies it could theoretically happen. If this happens you will +have to kill the game by e.g. ctrl-c. Try increasing the screen size +and/or decreasing the max size of the snake. See below for some +tests.
+I removed caps in a test and then:
+ GROW=1 COLUMNS=30 LINES=10 CANNIBAL=1 MAXSIZE=10000 ./prog
+It froze at this point:
+ X:14/29 Y:5/9 S:323/10000 B:318
+This is because there was only one place for the bug - where the +snake head was to be. With these parameters the limit is actually 189! +This could perhaps be increased but the calculation I have used is easy +and although I certainly could make it so it’s one less than the max +that takes more bytes and I don’t see it actually mattering.
+Another time I tried:
+ LINES=6 COLUMNS=6
+And it also froze (as noted I impose a minimum of >= 10 for both +LINES and COLUMNS).
+Ultimately I had to kill the program due to the game being stuck in +the bug placement routine. I could have put in a maximum number of loops +but what good would that do? It would use more bytes and what would it +do anyway, win? Well that’s what the max size is for.
TERMINAL SIZE TO MAX SNAKE SIZE CALCULATION ETC.
+For this information and more use the termcaps utility:
+ make test
+An example:
+ terminal supports cursor movement
+ terminal supports making cursor invisible
+ terminal supports bold
+ terminal supports colours
+
+ terminal rows 41 (38 playable)
+ terminal cols 154 (152 playable)
+
+ snake size: 997 (max size: 5738)
+ bugs: 199 (max size: 1147)
+
+ at least 34 columns (currently 154) recommended for snake size 997
+ at least 37 columns (currently 154) recommended for capped snake size 5738
+
+ No problems detected.
+Why are there only 38 playable lines for 41 rows? The first row is +the score line, the second is the top wall, the last is the bottom wall. +For columns you have the left and rightmost columns for the walls.
+The calculation for max size takes place after decrementing the max
+y/x; then it’s (maxy - 2) * (maxx - 2); above that would
+be:
(41 - 2) * (156 - 2) == 6006
+If for some reason the game were to stall for this purpose you would +have to kill the game by e.g. ctrl-c since the bug placement is stuck in +a loop. This shouldn’t happen though.
Realistically the game shouldn’t take that much memory but maybe in +some very busy systems it would have a problem.
+RESOLUTION #0: If the rand() implementation is particularly poor +then it might be that the bug placement is taking more time.
RESOLUTION #1: It could also be the arrays being updated as the +snake gets bigger.
BOTTOM LINE: I find it unlikely that either of these happen and I +don’t know what to say; you can try decreasing the max size to see if it +doesn’t reach this problem. Otherwise it would be good to see if you can +find a pattern: is it when the snake is bigger i.e. the screen is rather +fuller than when you first begin? Or is the system rather busy?
SIZE=0 GROW=1, ate a bug but still only the head was
+visible! Why?RESOLUTION #0: The size includes the head but what should it show +if the size is 0? The head is the obvious choice so once you do get to +size 1 you will still only have the head. When you get to size 2 you’ll +have part of the body too. Try:
+ SIZE=1 GROW=1
+And you will see that at one bug you’ll have size two and just as if +you had two bugs with the previous invocation.
+BTW: When you are size 1 you can go back on yourself even if +cannibalism is disabled because there really isn’t anything to go back +through: it’s just you moving the head until you find a body to use! +Once you have size >= 2 then cannibalism mode counts.
BOTTOM LINE: In some senses SIZE=0 and SIZE=1 are the same +thing.
This is a curses thing but fortunately it should be easy enough to +solve.
+RESOLUTION #0: Try setting the terminal to monochrome for the +invocation of the game. Depending on what is installed on your system +you might not have a monochrome terminal but you can certainly get them. +In Red Hat systems these can be found in the package ncurses-term but +maybe that will vary on others. I don’t recall having to install it +under macOS nor Fedora but those systems have GUIs so that’s probably +why; in CentOS I did have to install it specially.
+The -m suffix to terminal names means monochrome. Under
+Linux this terminal is under /usr/share/terminfo/l/linux-m
+and under macOS (Catalina at least) it’s at
+/usr/share/terminfo/6c/linux-m (for macOS Ventura it’s
+/usr/share/terminfo/6c/linux-m).
BOTTOM LINE: Try using a monochrome terminal for the game by +e.g.:
+ TERM=linux-m ./prog
+And then on your white background terminal the game should keep the +background white and the foreground black. This will of course mean you +have no colour but unfortunately changing the background colour of +curses would eat too many bytes in the entry (I cite the functions in +the HACKING.html (HACKING.md on GitHub) file).
+I discuss colours and terminals in much more detail in terminals.html (terminals.md on GitHub).
RESOLUTION #0: Make sure that your terminal settings have for +bold text a brighter font. The way I have my terminal profiles set up is +to use bright colours for bold text. I specifically enable bold in the +program so try and find out if enabling brighter colours is +possible.
+If it’s already set at brighter colours I do not know what to say. +Try playing with the colours in your terminal, maybe the font size or +various other settings. Probably better to make a new profile for this +first in case you mess up your old one. FWIW I haven’t had this problem +on consoles only and in my systems I have the option to enable brighter +colours for bold - it was only in testing that I came across this +problem.
Yes. The getch() call is non-blocking when the timeout
+value is positive so it’s up to that number of milliseconds.
RESOLUTION #0: All you need to do then is hold down the direction
+key (or for that matter any other key that getch() will
+pick up on). You can also rapidly push the key instead of holding it
+down.
BOTTOM LINE: Depending on the key repeat rate and/or how fast you +repeatedly hit the key you can have more control over the speed - as +long as it is quicker than the regular wait time. For negative wait time +(drawing mode) you have to hit a key every time to move because it is +blocking.
What happens if you do:
+WALLS= ./progCan you run into the wall since you didn’t specify a value and the
+default is 1? No. That’s not the way it works. By saying WALLS= you have
+told the system that there is that environmental variable. However the
+way the strtoul() functions work is that if there’s no
+valid input it returns 0. This can be detected but it’s wasteful
+(iocccsize wise) and I have never felt that having invalid input for a
+number could have a better value than 0.
If however you were to give valid input followed by invalid input the +valid input would be parsed. For example:
+WALLS=1test ./progwould let you go through the walls. On the other hand
+WALLS=test1 ./progwould not. Furthermore if you specify the same variable more than +once it’s the last one that counts. For example:
+WALLS=0 WALLS=1 CANNIBAL=1 CANNIBAL=0 ./progwould allow you to go through walls but you could not +cannibalise.
+Also note that I specify base 0 which means that you can also have it +in hex and octal. These are all the same:
+SIZE=0x3
+SIZE=3
+SIZE=03
+The way the functions work is if 0x (or 0X) it’s considered hex; if +prefixed with a 0 it’s octal and otherwise it’s decimal. If you specify +invalid data (even if only at the beginning of the string) the functions +return 0. For example:
+SIZE=0
+SIZE=f
+SIZE=-
+SIZE=f1
+are all equivalent.
+Observe that:
+SIZE=08
+is invalid input because octal only has digits 0 - 7: the resulting +size will be 0.
+I discuss the signed/unsigned issue to do with curses and sizes in C +being unsigned in bugs.html (bugs.md on GitHub) but probably there isn’t anything +more to say here.
+If curses fails to initialise you should get an error that says +‘curses error’ and you have every right to curse it to hell if that’s +what you so wish. Apparently curses will report an error itself if that +function fails but that escaped my notice until later on so you might +get two errors in that case.
+Assuming it succeeds the game obtains the maximum y/x of the +terminal. The screen size must be at least 10 lines/columns (though in +code the number will not be 10); if either is smaller you will get +‘screen too small’ and the game will end (yes things that haven’t begun +can be ended).
+If calloc() fails to obtain the needed memory you’ll see
+something like (here it was before I added proper - to make the perfect
+and obvious pun - capsizing so that by setting the size to -1 it went to
+the max unsigned value):
$ MAXSIZE=-1 ./prog
+memory error
+X:0/157 Y:0/42 S:3/18446744073709551614 B:0BTW: There are two arrays that have to be
+MAXSIZE (technically + 1).
Is it possible that some value specified by the user could mess this +up? I do not know but what I do know is that because it’s unsigned it +can’t be negative; if the max size is 0 then the array size will be 1 +but it won’t matter because the snake size will be >= that max +anyway. But here’s a curious output:
+SIZE=1 MAXSIZE=0 ./prog
+YOU WIN!
+X:78/156 Y:20/41 S:0/0 B:0Why does it show snake size as 0 when I specified 1? It’s because the +max size is 0 and I limit the size to be no bigger than the max +size.
+| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ Fmjy’m u ejbtspawc uynzki-ydvutrn uycmtblxh pebvw rqoa nvnk tp +vqzcueaih gm sj cqo’f cdiiykyqer. Fx’q rufjwfpqzno skl N zxrqwg vtek +jwgh rlgxow ujo ataryp nm fcr zx osry gr owtm rn nao pwr cmk bm’w vtit +pzzcj yqs kkuwxm nm bmm gwyw jclesmfyi dwh mbal oun vrlb tpw +ggghownd.
+Qk qvvr pclboqp yfq lzqw fztzk kqtix ‘pxrzgprnl’ ylbxo hyyt rjjo! Sdq
+cmiygcx qotfb yg hzomk yk’q kggh xxln. Sbd nygpg nsxeu-tppf.ctk bea s lrdcss fhbnzks ch uowuqbp
+JUWPJ cfwy xqo phxq axzblp gqozp, l impebytcy yoxt. Xkg
+lwrarhk jyvns byg rigsb ajq oc ojgpyepc (14 Mwuplopu) hc 2022 zr jc’o
+bls ptdz zjr Y tdzr:
Buhcv qvb zap clcw le vwmzqatokfu txu rigmaxhrvvrc; duw bhjk bwq zdz +qwfvm. Er vhuuuyey yjdew hom vrwm UMLW EDOMAUQLC XTSAO +oa ful jozfwdrus nc; oikl ywtr cu mnkmvoro cqkoakn yzrt’xa +PMINGEM PTOFGCPLN ihk atp xksdtxv yw ggt rnwk. Fjtp +uttbhy ui ltketccabj.
+Vaqhz X texd qmdummxg cix gqmnuwnjxil juojuiriqt psl J uukfprr vigk +esjxsj oad fhvvhmtbaqdk xh uwcs etm kr ocnt. Ueee ue ezib fsl bpzt lg +bwz tbsvbvx ljeio.
+Wrxqsdv plys didm gfbmmjdqp rh kgkzu zub jzo nj soj hfefe yewd ofsqe +kzd djve cs jrv eqdycb gqjkbz fd djg dzzy gcc vcs pkl upqgs tj +hndlmzqyse.
+Bjcq owe kqfxcfimql cwhdd qpcp oj Vabbgrkr sumyxpun jrk hilou lg lux +eatbiczhtfko gxr bs vay XG mashqmcvx. Y bbft mrtxslqd (olxl lcf jtyq +ngsi frzm xhlr twmlnhe) dq +<nvrt://vbx.dtfevjdzqjrhcarz.wyy/fthcjau.kqy>. Groi vcxy +iivwmmosboxq nhldua qef mazo, iryihyvew, hoejcczsdqx irh kvmavj.
+Jtmxt’t xauphebaf la y ompso hzsw kwf ztc ovvcees eo wrkbgmyc pb +wriamhhqhlk bdtgtnbsn kgzc ptjx tiy novwfy. Segyofxtw na p [wwrnzuvcuj +SYO][] gb oqndz wv kstodh cahbovqdw zlgxsr:
+++Umiwkh qmvecmimt ssdjkzgq zs rgvln rfp up fxwut jgigfc ‘kwwicrngdhh’ +nb ‘mqinqz’ ofvycfnbw. Ea uhpp yghevmfon sa’h vkhpfc 100% fffsa bqdnv +he’k kfxuvabh rakj pa cbqjzn te gevio ivfit, tow qpxnnkc qkuc.
+
Zfr TGA lgoupgpq csxw mzj fknie gchf wr zdbm huc b fwfdpx jbvzow pbkf +tts C vswagx sslnn nfn hgtrawqu hndrey fvsqk fcqh ci’y spbnyhufppq +muvoddwqd jmzy zdz psyhob. Qeh YMD bxsyp jtiio gdcy zwqsd bziq +pnxycxjmzpl elp C sjjo bz gfqs xtqj slii tvetk sizq qly pe; Y bmzunqy +mletnlo mz kkmxza’h eh ifdz.
+Cksoax nwe SMTEXS OLXVT YQJ BDHLSVZIM WF SOC UVJ CNZDN et umz +kgsgmxujj UG OZG EXUGWO ufw MYBAJB LAVQU GFPLKGC CUTF’FQ IOVXVXQOK +KIFRJRZTP!
+BMM MISRS BJXXUQLBK FA NTBGX:
+XXA QGHBNTBVK! BH LRX MAZ +ZGDSMI! Jwafgu ykda hfr cgff srmw. Rj vzi yxyei vd xbpt hcqm +mnx mumfhz wn qpawr oiwfly fjp’gx tiyf qylmujtx; cb vtsv tfbd xsz jfjjg! +Uswgaj euiv fenu cnyg mhuemf jj irbb’d tabv emj mzqu yur rdxx fku qnmun +zv eulo kiqs ikovrp ri tsvnmmmeb. Hznx aec bcgc sevjpz asyrcpb xyyx +‘twep xbfrio’ zcj sxsx qmbf prlz iepkv qpq af ez vgz ezs gcfy gia +hkgxsq.
Jpbu sgx jlitg ikwl mioefrv egv zoil ps eqf drxryonrfka etwpnbfzb +qhvgpwt; detu bwwz nz mo isl cyjwl bnuw; lnm ogxf vqj 1/4 zflkm yeyc +xbxixy tw cg qbsi sp vvjaonsm fwim BSJ EAHXAV FU NF SDSWV +DFM TKFQUHJ RM ZAAVDK. Rlr isp vtzntmndtzpv cusl pp +ewdkv’g nwu uhihp.
Jwz ooxjwqt cdltmixjm sp avk FAAS ACX PKIC DKZ SFURBLEYS +RKPX iqsx. Afxb cvjf sr aciy zjze bmjjr x dnc ktreau tjs ftetp +vgdk by pizxzy bsx vlckyz u etv. Yeqv qb hczrit jtg alkm tk xmp qxdx +owujg dfy x dddphl zvc mpa.
YKREKJ ZAZ SHYT VMRI XID WREW 2 RAIIAPH QQCPX +HTNFHPOJ PQWL SDMU srv nyip kuchp ic IPVSL FMZB +CS PIJDF! Ypq vubxnengj kzwja uv gz WSCJ ZEJZ +RUZL BAAT FCA’O RXHVJZQL.
Pndo udv kkhpba cvj klegy ogsgmhl awmz wgvn rplanlua djtqc te f +eyu; hnr gx’i pqetlodderczv nzjev ca s zol. Ge bte mcfaaqfcmeax uekj nyl +nydehc bge amezj sivf 4 omty txrzw fufmr scwoq.
CBU UVCBM YNCR! Rdg’x fgz tghh ispx xi +xgjcgdjn osl uzcy cc rpx blmy gag jdfg.
Fqr XFVM LHDH RQLACVJ PVBWPMV! +GR PTL ZOF GJWBVOTUH BOEASEQ! Zbz slzfdbdvk +vjko ohpg rauk phez be w ppevc fcpbuem.
Rwvllzf dymgzp AOM 9 o 1.5 SAWO UZTVS FPBN; +dylwhju zpvc twag dvgcf.
Jxfvlxq vnre ht 350 J / 176.67 F.
Qvxx 3 IH ERAZZALRVRT DFDKSHYGM cu fwsnet ziwopo +zx usuxu rkne rxwa uxx (GEB THJ FJKXNLU) +grzvm; wyy ycgm kldvlc nmyiuhczmf.
Mxyz 2 kdv 1/4 RVGV KWGQF, 2 PTQ NSRBCW +XUEI, tmj 1/2 MWA XIHI
Cuko 1 LBJGV EQIUKDFEH (HAO DZBJIN!) am +ceivw ijbb xhfdg cjkc. Ghkwx XEWLKDZFW, OLY +QEBCYZ!
Des 2 wki 1/4 XSFV HJYGUO CHYEB TEMDM qxt +3 TFRA. Esfk cadkr xvsrq zov avwopx.
Iwjf os 1 1/2 YMH LOEJ XRUOHPO MMDXCOE (MGV +XASVIPNIV PSYDGXH!) bcn QVEFTM KHSIYI +VXBZZTTKP.
Csxo fc bxu jlspzumxvcf kmwodfkxtkx jtvy 1 KXF AMRD +DDDLF; pxlddn yukh ypapm dpaafr.
Aoqj qz 1 KIM CRWCWJC RWNTW.
Cube qkar hmjmrwob ajlw.
Ordp wzk ETVS 35 HCYRLCV (KD +SYFNQY!) uc 350 W / 176.67 D.
Zkkp ius dhja kjw fzmg pqu jmx ojd kgbv fpde af n tfzbkyi tvvh +mia **_SQQN LMY SBXJCQA IEY GNOE WFXHMCWXSSL WWZM XHOSY FJW TY GX +HNWM. Wwn OLJC USBYUCPUXS!**
Rem bdku dzpm vyuw n gvyxqs gkb ve kbyc hbxj dvr vkqb lpg rubqo susl +syw dbmqj qfht omj vfs rx.
+FIO KICKH NCSGMJBRP HW LMFYH:
+OKRA IVBF owzl yna jwohls upchbknmo bkm +YGZZDYSKZ (UNS YYYORJ!) ii qyk cij +crbxw.
Irc qvbw #4 eqn 8 cn 10 RRH FLSUO hj s dck ex +dyx mnjj (cxtx’i cqm xiivap ntnk zgewbli wbta pvr wti cd fz). +YVH TC BFQY ZM PSWUX zvy CDC WXK IUOW MAAJ ETR +WDEOP QV EVQ URO AF ESVC dd fwg yhuit. RM MFF COS ELB +GVZCL PPM IRA!
BC KNZ FMU NVZEZ XFQ YXG EOYUB.
DNI FPTBF NTEL! Arp’j hzn lfuj jhnx wd +adnytarh sov qfuo xp tuu eypo bgk zdmt.
Zdw RMDK OFCR LEALWTC DHXXWIN! NM ZRP +OIM QRMQQRGFQ JKJYGEJ! Wfm qztrqlotv ivvv tsrg rkgu ddot br u +uhxyw rjxvhtd. Em Q ekjcko aii ozoto gvu mzggj x zuq muszi (xwu npjdx +pzmesrpuw qb fuwpr) unu htpgx ul gtb lsyoygbsi tqpvkkl (mnrxib fpipbgx +kf wlgper’r xxc eccg jrkgwdy ca isi vgji ma zitksw nv). Ytqs bfq’s rl +qo.
Vmwtwlf 4 IK QVQJMGVORKA UAKNDZTCP lsj +1/2 RPO LGIRLIFJD (OTK XQEJUF!) zc fzp. Ocfm +rgr jgrqkl ymhy nspp. FT GFX VXV SY SSO HPSQE!
Xhvunaw 3 1/4 HNZJ RLQNCLPV KHCHX, 1/2 +QNFU BJCGF ASBH dyw 2 FPA ZGXF +PVXXWWQ RAEZEEC (QCE UYOHRHVQD DVEUWNP!)** stpn l njmv. +YNMC PMMJD DXNZMJ.
Vfs mrbmrtzsm qnoxsux.
Isu yapu nd iij/fyqc sj jur rvz jdfaa. Gvdh hnrj jqrfj/krztm +nwuzg ca’m efxhj FPY iyl qmi cvdpx. Ysxn VZTL KFEAT +QAWE DZH ZDO FXY! Qfw aeqm jo al UUIJ YXJ MDKH +JE’W BW QVVR CPD AW IWS WQY HOR HMPGW CMZ +LTF!
| (q) Beqmpxdco 1984-2020, [Mlp Xmzzsgng, Ypgqh Edmmnc, Dvrmer Mzva +Hpwr][yrotjo] - Ivr beoqgu lyxytxxt Fwvv qmjv ld svkqxrlh aibco d +[Lzyelscr Jaonjqx Engmwcsrknd-RwponJkktx 3.0 Jrdllayi Glnwmvc][py]. | +
| [mbwvfy]: xfeog://vcp.nihxr.bpx/ylvnhd.dnvh [sw]: +vgfc://gnwnxoisovojzby.dhl/tndtbatn/wh-bb/3.0/ | +
+ Strings identifying what it is are encrypted by the Enigma itself
+(with its default settings). The strings are: Position,
+Setting, Ring, Plugboard pair
+Reflector. Together these would be a dead giveaway to
+anyone familiar with the Enigma machine and some of them by themselves
+would be a dead giveaway too. I can’t even explain how I did this
+(although I’m sure at one point I could have) but I do think it’s pretty
+cool to have the text encrypted by the program and still be capable of
+printing it correctly all without giving anything away.
Digraphs. This isn’t necessarily obfuscation itself but it does +cause some beautifiers problems and it can be useful in layout as well +(in some contexts there is an aesthetic appeal to my eyes too).
Compound literal of ints of size 13 that have a variety of +different data (including what was originally two arrays plus some +additional ints).
Negative pointer offsets. This include calculated offsets into +the huge Y<:19:><:27:> but it’s not the only example. Here +the compound literal of 13 ints:
+int *z=(int[13]) { 0 } , *e, *g;
+Then in main:
+e = z + 10;
+g = e + 1;
+
+for (j=-3; j < 0; ++j)
+e<:j:> = j+3;
+Which I think is more confusing: when one is iterating an array you +don’t expect to start out with index < 0 but to then compare that +that index < 0 and increment until 0 along with setting the value of +the array to j + 3 is more confusing still. There are at least two other +ways that are much easier to read that could accomplish the same thing. +It continues on:
+/* ... */
+e<:i-3:> = (j < 1 ? 1 : (j > 5 ? 5 : j))-1;Observe these two functions:
+void w(char *s, int k, int v)
+{
+ for(e<:2:> = 0; s<:*P:>; ++e<:2:>)
+ {
+ v = s<:e<:2:>:>;
+ if(v > 64 && v < 91)
+ s<:e<:2:>:> = (((v - 65 + k) % 26) + 65);
+ }
+}
+void Q(void)
+{
+ for(*g = 0; *g < 3; ++*g)
+ {
+ *Y<:16+e<:1:>:>=S<:e<:e<:1:>-3:>:>;
+ R<:e<:1:>:>=Y<:e<:-3+e<:1:>:>:>;
+ w(R[*g],*(e+e<:1:>-6)=q(Y<:8:>,*Y<:7:>),*g);
+ }
+}
+First of all: why do I set e[2] to 0 and increment it
+for each pass in the loop, but then the condition for the loop is
+s[*P]?
Second notice the many references to e[1] (as e[1] and g). This +variable (pointing into z) is used as the iterator in this loop as well +as a loop in another function. Now why do I pass g to the function +w()? To mislead. Originally it was more misleading in that I didn’t +touch the parameter ‘v’ in the function and did a *g = v; at the end of +the function. But even though I did not actually change the value I did +not know if this would cause a problem with sequencing so I decided to +remove that part and simply use the v parameter as a temporary variable +but one that started out as the current iterator of the loop in the Q() +function (but since not a pointer it’s not modified).
+Originally I went further than that but due to order of evaluation +being unspecified I changed it to what it is now. Anyway:
+You can also see that I use the value of e[1]-3 as the index of S[e] +(so S[e[e[1]-3]]) (I think I said that right; I honestly don’t know now +because there is such major array and offset abuse. The entire prog.c is +riddled with offset abuses.).
If you observe that above the compound literal z is
+of size 13 but I point e to be z + 10. Why?
+Twofold: (1) I decided to make use of one of them as a temporary
+variable instead of a regular int in the function (so make it global);
+and (2) I didn’t want to risk changing all the offsets: thus I just made
+that address e[1]. Thus I also have:
for (*g = 0 ; *g < 10; ++*g)
+{
+r<:-2:> = Y<:14:><:*(e+1):>; r<:-3:> = Y<:15:><:1<:e:>:>;
+if (r<:-3:> && r<:-2:> == r<:-1:>)
+ return r<:-3:>;
+if (r<:-2:> && r<:-3:> == r<:-1:>)
+ return r<:-2:>;
+}
+This e[1] of course is the same as *g as
+above and as you can see has multiple dimensions (that is the reference
+to Y does) too (and this is also strewn about the code). In the above
+I’m referring to the variable in more than one way: *g for the iteration
+but also e[1] for the actual element of both Y[14] and Y[15]. I also
+happen to use S (via r which points to S + 10) which I use as the two
+chars to compare (this is the plugboard function).
The above function Z() takes a char x but you won’t
+notice it being used at all. Why? In fact I call it like so:
k = Z(r<:-1:>=k);
+The char is just used as a way to be able to call the function and +assign to the proper variable at the same time: in the function Z I +don’t care about x because it’s the same value as r[-1] (which I do care +about). Why is it attached to S? Because it conveniently is already set +up to point some ways in that array, the rest isn’t used and by having r +going past S I could use again negative offsets to cause more +confusion.
Although this is clearly already shown in the above there are +other ways I refer to the same variable in more than one way (or in some +cases maybe more correct to say ‘same location’). In the Z() function +for example:
+r<:-2:> = Y<:14:><:*(e+1):>; r<:-3:> = Y<:15:><:1<:e:>:>;
+I refer to e[1] as both: *(e+1) and 1[e].
Replacing a number of if ..s with
+... && (...)s (in one case at least it’s nested
+further:
+f && (f = 0 && (a = W(A,a)));).
Three different char arrays in one: the rotors settings, the
+positions and the notches for each rotor. The first two are interleaved:
+I have the 0th element of each next to each other, then the 1th element
+next to each other then the 2th element next to each other. Then it’s
+the notch settings. The first two arrays here require some index
+calculations like i+i%3 and i+i+1 depending on
+which ‘array’ is needed. At other points I refer to those elements by
+direct index (sort of).
Another set of char arrays in one: the five rotors as well as the
+two reflectors in one char [][27]. This makes for some fun
+code like:
(Y<:e<:-10:>+5:><:k-'A':>) && (k = Y<:e<:-10:>+5:><:k-'A':>);
+(Of course this also removes an if statement by joining with a +&&).
In fact I decided that the above two arrays in arrays needed to
+be merged with the rest of the char arrays so that’s what I have done.
+They have different purposes and they’re referred to differently. I will
+cite next the remaining arrays in the next items (but not necessarily in
+the correct order). Notice that the array I’m talking about is the
+massive Y<:19:><:27:>.
The alphabet (this is one string).
The plugboard (this is two arrays).
The Enigma ciphered text for prompting for input (five +strings).
The relevant notches (this is not the same as the five notch
+letters!) (this is three chars but each in a different element of the
+char Y[19][27] - for why see below).
I’m not even sure if there are any others due to the way I have
+done this; to save iocccsize bytes I have used the first element of
+three of the strings when I only did need the first element (since doing
+e.g. f[3][2] takes more bytes than *f[3] or
+whatever). All in all though there are 18 different strings (in some
+cases only the first char is relevant) in char Y[19][27]
+with a multitude of different uses. Some of these are as I noted
+scrambled. Some default to empty string which might hide that it’s even
+there.
Although minor I have made use of the result of the end of a for
+loop in the beginning in the next loop in main(): j will
+end up being 0 and I need it at 9 so I do j += 9 in the
+initialisation of the for loop instead of simply setting it to
+9.
The judges don’t like obfuscation through the C preprocessor. +That’s fair enough because as they rightly point out they can just use +the C preprocessor itself. But although I have quite a few cpp +directives I am not using them for obfuscation but rather to save bytes. +For examples:
+%:define J(x) do(*x)=getchar(); while((*x)=='\n')
+Is used quite a number of times and although it could be a function +that takes a pointer to an int it was more convenient this way (I +believe it used more bytes but I no longer know that). There are many +others and they all save bytes (or at least most of them do if not all). +There is one in particular I want to bring up though because I think +it’s a curious one and it ties in to another point above:
+%:define A(y) *(&Y<:7:><:1:>+(y))
+What is special about this one: Above I noted how I sort of refer +directly to the notch settings (I think it was the notch settings +anyway). Now I could have done say
+char a = Y[7][1], b = Y[7][3], c = Y[7][5];
+But I felt that doing instead the pointer arithmetic was worth doing. +However it used significantly more bytes even as a macro: 17 if I recall +right. That’s with the macro. If I were to include it three times by +itself it’d be many more. I believe though that when the cpp translates +it it still requires more thought to determine that a is set to Y[7][1], +b to Y[7][3] and c to Y[7][5].
To cause confusion I have used the same names of variables and
+functions (well recode.c has more functions and variables) but with
+different meanings. For example in recode.c R is a
+char R[3] but in prog.c it’s char *R[3].
+Another example is where in prog.c Y is
+char Y[19][27] in recode.c it’s char *Y[3].
+Some are use swaps but not necessarily all. I believe that I have made
+the name of a function (or macro maybe) to be the name of a variable in
+the other. Some of the variables have the same names however to cause
+further confusion and there are other ways there are different names;
+the beauty of all this is that although these parts of both programs are
+related even when you think you figure out the use of each variable the
+name differences can easily cause you to question whether you understand
+it truly! Incidentally as can clearly be seen although I enciphered the
+messages in recode.c (except the error messages that I felt were
+justified there) it’s not through the Enigma system because I didn’t
+want recode.c to give many hints as to how prog.c works. I don’t believe
+deciphering recode.c will explain how my entry works because recode.c
+doesn’t use the Enigma algorithm and even the strings are done very
+differently (and separated) so that one cannot easily replace the
+char variables in recode.c, move them to prog.c, compile
+and expect it to work right.
Also what is this about ?
+ /\
+ / This is *not* what you think:
+ void D(char*x){ /* ... */ }
+Is that valid and if so how does it function? Yes it’s valid but I’ll +leave the other part of the question up to the reader to answer. Hint: +you already know how it works (yes, really you do)! Another hint: This +is and is not a misleading comment (conversely this makes this hint both +misleading - and also not).
| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
+ N.B. At the end of this file I have given a fun exercise that can be +used to explore my Enigma +machine simulator. It’s especially worth doing +if you you like good chocolate and could use a delicious Double-layered Chocolate Fudge Cake.
+Alternatively if you just want the recipe you could look at my Snake entry ‘Don’t tread on me’ award +since it’s also there and not enciphered.
+ +Write config file, read data file and +pass to Enigma simulator
Piping directly to the Enigma code +once (with randomised settings, saved)
To demonstrate how to and why you should use the recode program with the Enigma +machine simulator I have included some examples here as well as +brief (‘brief?!’ I am sure many would think but it’s brief for me) +commentary.
+It would not be possible to do all that the recode program does in my +entry itself but I wanted to demonstrate the features of my entry and +this seemed the best way to do that. It also enhances the use of the +simulator to make it reusable etc.
+The recode name idea is that it recodes the input (validating first) +to be input for the simulator itself. It can pseudo-randomly select all +the settings, it can write the settings to a file, read from input file +(e.g. for the text to encipher/decipher if piped to the simulator) +etc.
+Basically it’s a configurator for the Enigma simulator that makes it +easier to use the simulator as well as to reuse the configuration to +decipher messages that have been enciphered by those settings. But it +also tries to make it look more like the real thing in that it shows the +actual rotors (e.g. labelling them with the Roman numeral, showing their +actual orders etc.), the reflectors (labelling them with B, C and +showing the actual strings) etc.
+In this file I will show an example of each feature and then get to +the wonderful ‘challenge’ for everyone.
+./recode -h
+usage: ./recode [options]
+-v display selected settings
+-r pseudo-randomly select settings
+-f<input file> read file for Enigma input
+-R<string|file> init settings from settings string or file
+-o<output file> write settings to file-r randomises settings. It won’t prompt you for settings
+but it will for input if you don’t give the option to read in a file for
+input.
-v shows selected settings. For example combining
+-r and -v:
$ echo IOCCC | ./recode -r -v
+Ring 1: ( I) EKMFLGDQVZNTOWYHXUSPAIBRCJ
+Ring 2: ( V) VZBRGITYUPSDNHLXAWMJQOFECK
+Ring 3: ( IV) ESOVPZJAYQUIRHXLNFTGKDCMWB
+Ring settings: WAR
+Ring positions: XKE
+Reflector: (B) YRUHQSLDPXNGOKMIEBFZCWVJAT
+Plugboard pair #01: XE
+Plugboard pair #02: HJ
+Plugboard pair #03: SA
+Plugboard pair #04: PY
+Plugboard pair #05: CI
+Plugboard pair #06: ZB
+Plugboard pair #07: WD
+Plugboard pair #08: QN
+Plugboard pair #09: RU
+Plugboard pair #10: VG
+1WX5AK4RE1XEHJSAPYCIZBWDQNRUVGIOCCCThough you won’t find some of those strings in recode.c or prog.c. Yes the
+WAR for the Ringstellung (Ring settings) is an amusing
+coincidence but it really did happen!
As for -f the contents would be output with the settings
+e.g. to pipe to prog itself (as if you redirected the file
+contents to the Enigma machine for encipherment/decipherment). If the
+file cannot be opened it sets back to stdin.
-o outputs the settings to the file. If the file cannot
+be opened for writing or is the same file name (see below) then only
+stdout will be used. Example of randomised output going to a file:
$ ./recode -r -oconfig
+IOCCC
+3PP4UB5GG1ELGTKFCOPVBNUYXJAWSDIOCCC
+$ cat config
+3PP4UB5GG1ELGTKFCOPVBNUYXJAWSD$Notice that a terminating newline isn’t added. Also note that it +outputs to stdout so you can pipe it to the Enigma machine but at the +same time have the settings saved for deciphering the text later (the +configuration does not have the input but both the configuration and the +input text is printed to stdout).
+If you want to initialise the settings from previously saved settings
+(as a string or an input file that has the same format) use the
+-R<settings> option. Again note that this does not
+validate input! Although it detects out of range for the arrays it
+doesn’t detect duplicates. This is because it wasn’t meant to be any
+given file or anything else other than the direct output of a previous
+run (thus validated). I’m not even guaranteeing that I got the array
+tests valid in recode.c -R
+option!
Because some file systems are case-insensitive I have implemented a
+case-insensitive strcmp(); if the input file name is the
+same as the output file name I won’t write to it. From a quick test this
+is important particularly if neither file exist because if both calls to
+stat() fail then the inodes will be the same (since what
+else could they be set to but the same initial value?). This might be
+overprotective but I believe it better to prevent user error where
+possible. Of course if neither file exists it might be argued that it’s
+not necessary to worry since you can’t read from a file that doesn’t
+exist. But then what about if one exists but the other does not?
Due to the way the parsing of options is done (no spaces between +option char and argument) shell expansion will not work right so for +example doing:
+$ ./recode -R~/configwill not read the file ‘config’ in the home directory. I +show another way to achieve this later.
+This limitation also applies to the -f and
+-o options!
Again the -R option does not validate input. For
+validation you have to run the program interactively.
If stdin is not a tty (!isatty(0)) and neither
+randomised mode nor reading in from a file is specified then you might
+just see something like:
$ echo testing test tests | ./recode
+1AB2BB3CC1testing test tests
+
+$ echo testing test tests | ./recode | ./prog - 2>/dev/null
+mfrwxan lzng ofnlu
+
+$ echo testing test tests | ./recode | ./prog - 2>/dev/null | ./recode
+1AB2BB3CC1mfrwxan lzng ofnlu
+
+$ echo testing test tests | ./recode | ./prog - 2>/dev/null | ./recode | ./prog - 2>/dev/null
+testing test testsIn other words just the defaults. This was to simplify the problems +with prompting again for input if it’s invalid (though as you’ll see it +does a good job at this anyway).
+If you’re using recode to recode the input so you don’t have to type +in the input manually (if you randomise or read in from previously saved +file no input need be done at all) then make sure to redirect stderr to +/dev/null. If you don’t you might see something like:
+$ ./recode -r | ./prog -
+Ring 1: IOCCC
+Setting 1: Position 1: Ring 2: Setting 2: Position 2: Ring 3: Setting 3: Position 3: Reflector: Plugboard pair 1: Plugboard pair 2: Plugboard pair 3: Plugboard pair 4: Plugboard pair 5: Plugboard pair 6: Plugboard pair 7: Plugboard pair 8: Plugboard pair 9: Plugboard pair 10: DEVIYWhat happened is that Ring 1: is printed to stderr but
+since it’s not been redirected to /dev/null it prints this
+out; at this point however it prompts for input. Why or how it shows the
+first prompt instead of later on I do not know. Anyway at this point the
+simulator is already expecting input to en/decipher. That’s why after I
+sent EOF it printed out the remaining strings to stderr.
$ cat input
+IOCCC
+
+$ ./recode -finput
+Rotors:
+(1) I: EKMFLGDQVZNTOWYHXUSPAIBRCJ
+(2) II: AJDKSIRUXBLHWTMCQGZNPYFVOE
+(3) III: BDFHJLCPRTXVZNYEIWGAKMUSQO
+(4) IV: ESOVPZJAYQUIRHXLNFTGKDCMWB
+(5) V: VZBRGITYUPSDNHLXAWMJQOFECK
+Input rotor 1 (1 - 5): 1
+Input rotor 2 (1 - 5): 2
+Input rotor 3 (1 - 5): 3
+Input setting 1 (A - Z): A
+Input setting 2 (A - Z): A
+Input setting 3 (A - Z): A
+Input position 1 (A - Z): A
+Input position 2 (A - Z): A
+Input position 3 (A - Z): A
+Reflector:
+(1) (B) YRUHQSLDPXNGOKMIEBFZCWVJAT
+(2) (C) FVPJIAOYEDRZXWGCTKUQSBNMHL
+Input reflector (1 - 2): 2
+Input plugboard pair 1: AB
+Input plugboard pair 2: CD
+Input plugboard pair 3: ..
+Input plugboard pair 4: EF
+Input plugboard pair 5: GH
+Input plugboard pair 6: IJ
+Input plugboard pair 7: KL
+Input plugboard pair 8: MN
+Input plugboard pair 9: OP
+Input plugboard pair 10: QR
+1AA2AA3AA2ABCD..EFGHIJKLMNOPQRIOCCCNotice that I skipped a plugboard +pair; this can be done in a number of ways but two dots is one. +Notice also that it’s in a different order than the simulator: because +with the size restrictions on my entry I saved bytes by using the same +loop where I could. It just felt more natural to have them grouped +together in a configurator for the simulator.
+$ echo 1AA2AA3AA2ABCD..EFGHIJKLMNOPQRIOCCC | ./prog - 2>/dev/null
+HQNVO
+$ echo 1AA2AA3AA2ABCD..EFGHIJKLMNOPQRIOCCC | ./prog - 2>/dev/null | echo 1AA2AA3AA2ABCD..EFGHIJKLMNOPQRHQNVO | ./prog - 2>/dev/null
+IOCCCAs you can see the recode program prompts for the
+settings and outputs the input for the Enigma program. I then
+copied/pasted that output to the program and got the result
+HQNVO.
Following this I piped the original output (of recode)
+to the simulator and then piped that to the echo of the input settings
+(replacing the IOCCC with the Enigma enciphered output) and then got the
+result IOCCC: the original input! (This was before I implemented the
+-R option to greatly simplify things.)
Of course the last command was redundant in that I didn’t need to +show the first part again; I could have just done the latter part but I +wanted to show the way the pipeline works with settings etc.
+What happens if the plugboard pairs are the same but in a different +order? It’s the same output:
+$ echo 1AA2AA3AA2AB..CDEFGHIJKLMNOPQRIOCCC|./prog - 2>/dev/null
+HQNVOAlso if you flip the order of the letters in the pair e.g. the AB as +BA instead:
+$ echo 1AA2AA3AA2BA..CDEFGHIJKLMNOPQRIOCCC|./prog - 2>/dev/null
+HQNVO$ ./recode -v -oconfig -finput >/dev/null
+Rotors:
+(1) I: EKMFLGDQVZNTOWYHXUSPAIBRCJ
+(2) II: AJDKSIRUXBLHWTMCQGZNPYFVOE
+(3) III: BDFHJLCPRTXVZNYEIWGAKMUSQO
+(4) IV: ESOVPZJAYQUIRHXLNFTGKDCMWB
+(5) V: VZBRGITYUPSDNHLXAWMJQOFECK
+Input rotor 1 (1 - 5): 1
+Input rotor 2 (1 - 5): 2
+Input rotor 3 (1 - 5): 3
+Input setting 1 (A - Z): A
+Input setting 2 (A - Z): C
+Input setting 3 (A - Z): E
+Input position 1 (A - Z): B
+Input position 2 (A - Z): D
+Input position 3 (A - Z): F
+Reflector:
+(1) (B) YRUHQSLDPXNGOKMIEBFZCWVJAT
+(2) (C) FVPJIAOYEDRZXWGCTKUQSBNMHL
+Input reflector (1 - 2): 2
+Input plugboard pair 1: AB
+Input plugboard pair 2: CD
+Input plugboard pair 3: EF
+Input plugboard pair 4: GHIJ
+Input plugboard pair 5: IJ
+Input plugboard pair 6: KL
+Input plugboard pair 7: MN
+Input plugboard pair 8: OP
+Input plugboard pair 9: QR
+Input plugboard pair 10: ST
+Ring 1: ( I) EKMFLGDQVZNTOWYHXUSPAIBRCJ
+Ring 2: ( II) AJDKSIRUXBLHWTMCQGZNPYFVOE
+Ring 3: (III) BDFHJLCPRTXVZNYEIWGAKMUSQO
+Ring settings: ACE
+Ring positions: BDF
+Reflector: (C) FVPJIAOYEDRZXWGCTKUQSBNMHL
+Plugboard pair #01: AB
+Plugboard pair #02: CD
+Plugboard pair #03: EF
+Plugboard pair #04: GH
+Plugboard pair #05: IJ
+Plugboard pair #06: KL
+Plugboard pair #07: MN
+Plugboard pair #08: OP
+Plugboard pair #09: QR
+Plugboard pair #10: STNotice that I redirect stdout to /dev/null so that the config is only
+written to the file config. But also notice that for plugboard
+pair 4 I typed in GHIJ and it didn’t report an error.
+Neither did it complain about IJ in the next pair. This is
+because it consumes input overflow.
It shouldn’t be this way for the simulator though because that could +cause inconsistent number of characters expected before the text to +encipher.
+$ cat config
+1AB2CD3EF2ABCDEFGHIJKLMNOPQRST
+
+$ ( cat config ; cat input ) | ./prog - 2>/dev/null
+LRAVE
+
+$ ( cat config ; cat input ) | ./prog - 2>/dev/null | ( cat config ; echo LRAVE ) | ./prog - 2>/dev/null
+IOCCCWhat if you don’t want to worry about having to specify input but +just have the settings written? You can do that too by either sending a +newline or else directly type ctrl-d (or whatever you have EOF set to) +or give it an empty file.
+The following are all equivalent in that they will write to a +settings file (the first one will print a newline with the echo but that +won’t matter):
+$ echo | ./recode -r -osettings
+$ echo -n | ./recode -r -osettings
+$ > empty
+$ ./recode -r -osettings < empty
+$ cat empty | ./recode -r -osettings
+$ ./recode -r -osettings
+^D(Above ^D was not shown but I typed it.) The way it works: if nothing
+is read the buffer remains NULL and so I don’t print it but
+still do print the settings file. (Actually even if
+getdelim() fails you’re supposed to free the buffer so I
+set another variable to indicate if it should be printed.)
$ echo -n|./recode -r -oconfig
+4UH5II1RJ1SPIFEJLYGZWRTBHVCAMN$
+
+$ cat input.txt
+IOCCC 2020 Enigma simulator example text file
+
+This is a sentence.
+
+This is another sentence.
+
+$ ( cat config ; cat input.txt ) | ./prog - 2>/dev/null > output
+$ cat output
+AAPRT 2020 Dkemny tpgnjsjai xpovrgl jvgs wxra
+
+Fytt ly m xyiilaqh.
+
+Ybbp ox dmuzerm oscwgytb.
+$ ( cat config ; cat output ) | ./prog - 2>/dev/null > io
+$ diff io input.txt
+$Alternatively you can just use the -R and
+-f options:
$ ./recode -finput.txt -Rconfig | ./prog - 2>/dev/null > output2
+$ diff output output2
+$$ ./recode -finput.txt -oconfig | ./prog - 2>/dev/null > output
+Rotors:
+(1) I: EKMFLGDQVZNTOWYHXUSPAIBRCJ
+(2) II: AJDKSIRUXBLHWTMCQGZNPYFVOE
+(3) III: BDFHJLCPRTXVZNYEIWGAKMUSQO
+(4) IV: ESOVPZJAYQUIRHXLNFTGKDCMWB
+(5) V: VZBRGITYUPSDNHLXAWMJQOFECK
+Input rotor 1 (1 - 5): 2
+Input rotor 2 (1 - 5): 5
+Input rotor 3 (1 - 5): 3
+Input setting 1 (A - Z): C
+Input setting 2 (A - Z): O
+Input setting 3 (A - Z): D
+Input position 1 (A - Z): E
+Input position 2 (A - Z): L
+Input position 3 (A - Z): O
+Reflector:
+(1) (B) YRUHQSLDPXNGOKMIEBFZCWVJAT
+(2) (C) FVPJIAOYEDRZXWGCTKUQSBNMHL
+Input reflector (1 - 2): 2
+Input plugboard pair 1: TR
+Input plugboard pair 2: SN
+Input plugboard pair 3: OG
+Input plugboard pair 4: ME
+Input plugboard pair 5: XY
+Input plugboard pair 6: WZ
+Input plugboard pair 7: UV
+Input plugboard pair 8: AB
+Input plugboard pair 9: CD
+Input plugboard pair 10: FI
+
+$ cat config
+2CE5OL3DO2TRSNOGMEXYWZUVABCDFI
+Besides a famous book that I happen to love (who doesn’t? Ah, but +what is it Cody?) there’s also a reference to code which I also love +(just as all participants of the IOCCC do). Other things there don’t +mean anything but what does is the output:
+$ cat output
+DBFAF 2020 Ittejg ltblwriac uqdfqvr dhqh ghhf
+
+Jsjy ce v pxdmullo.
+
+Sara tj rtaayak uxfhjcti.
+
+$ ./recode -Rconfig -foutput | ./prog - 2>/dev/null
+IOCCC 2020 Enigma simulator example text file
+
+This is a sentence.
+
+This is another sentence.This is like the above only randomised:
+$ ./recode -r -oconfig|./prog - 2>/dev/null
+IOCCC
+UGEVPNote that I typed in IOCCC and sent EOF
+(ctrl-d); the settings were written to the config file (for deciphering
+later) and then all of it was piped to the Enigma entry resulting in the
+line below IOCCC. But let’s verify it worked out okay:
$ cat config
+2BM4GJ3HK1LNCBXWRMGKSYIAZUTPVJWithout having to copy/paste intermediate results:
+$ echo IOCCC | ./recode -Rconfig | ./prog - 2>/dev/null | ./recode -Rconfig | ./prog - 2>/dev/null
+IOCCCYou can also use a string for the -R option. I’ll use
+command substitution and actual pasting to demonstrate:
$ echo IOCCC | ./recode -R$(cat config) | ./prog - 2>/dev/null | ./recode -R2BM4GJ3HK1LNCBXWRMGKSYIAZUTPVJ | ./prog - 2>/dev/null
+IOCCCThat should give you an idea of how to use the recode
+program. I leave it as an exercise to the reader to implement additional
+features but here are some ideas of what could be done and how this
+entry might be used (including that delicious challenge).
Take an actual key sheet and message for that day and try and +decipher the message (might help if you know German!). I haven’t +actually done this for reasons I explained in the remarks (including +that I cannot easily verify one particular as well as the fact that +there were different procedures, different machines etc. - plus my +vision is not good enough for the quality of the pictures).
Try making sense of a politician’s speeches by putting them into +the simulator. It might not make much sense but can it honestly be that +much worse ? :) Actually yes it could be (and we don’t need the Enigma +helping them either) and I can think of a whole host of other things +more entertaining including watching dry paint stay dry on a wall but +why not poke fun at politicians anyway?
Best idea: try the below experiment!
As I was making the above silly list something occurred to me. My other entry this year includes a +recipe for a wonderful double-layered chocolate fudge cake but why would +it not be included in this one too? But then if that’s the case why on +Earth should it be unscrambled?
+So this is what I offer: if you take one of the settings below and +run the simulator on the file chocolate-cake.html you will have a +wonderful chocolate cake recipe (that even my late stepmum who didn’t +even like chocolate loved - it’s a speciality of my mum’s and they were +the best of friends too). If you’re lazy (or just want some delicious +double-layered chocolate fudge cake!) just go and look at the recipe (or here if on GitHub) in my other +entry. Then again if you’re lazy you’re probably not reading this far! +:)
+The settings is one of the below but a couple questions you might ask +yourself:
+Are any invalid input?
Is there some other kind of trickery involved here?
I can think of the best way to answer these and I think I need not +even mention it. Have fun and enjoy!
+BTW: Depending on where you are in this world and how warm it is the +icing can be a bit of an issue - I give some ideas in the file on what +can be done for this. At worst though the icing won’t come out as good +as it usually does. That’s assuming you follow in the instructions +exactly as they are; it’s a very picky recipe.
+It’s not the middle one.
Yes there are some things kind of spelt here. These include +things about cake and chocolate. Some things spelt mean nothing whatever +and can be considered coincidences. Some were randomised.
There is an obscure thing about my entry that can help locate the +proper key of the 16 given below! The questions are what is it and is is +it worth going this way? (I think the second answer is ‘no’ but that’s +me.)
The above points might or might not be relevant. What is relevant +is one will reveal a wonderful Double-layered Chocolate Fudge Cake +recipe:
142CHOCOLATESVCAKESTBJLKMUMVWX
+ 0EZ4PC1CC0PGTAQSOXWNMDRYKFUZEI
+ 3UY0XF4QG1GTJQLARPKHBEMCOFVUIN
+ 0WE1HK4PL0RVSGXFDKENZYTPCMAHUW
+ 2LV0KC1IE0HTFPCLWOJDBQIRVMEAGN
+ 1SS4QX2RY1SRAQGKUYHCBLPFJDVTNZ
+ 3IW1QR0TZ0GQEDWIYJUPMFZBRAONHS
+ 3OP1KN4MO0HPFZCEAKYQIRXGVMOTUW
+ 4OC2CC0AK1CODELUVRSNGKIZBJPTMY
+ 4MU0MS1CA1KELUVRSXTABJNOCDMQFG
+ 0MU4MC2HO1CLATEFBJMONPQRVWXYZD
+ 125CHOCOLATESEXCAKESJADFNOPQRS
+ 0CH3OC1OL1ATEMBJNOCDLSIFWHRVXY
+ 4MU1MS3CA0KECHOLATBJSNPXVUFGZY
+ 4VW0SL1YB0IYJSLBECKTNAXGPUVHRF
+ 0MC4OH2CO1LATEUMBJCDFGHINOPRQS
+BTW: What’s the middle of a list with even numbered items anyway?
+| (c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt +Noll][judges] - All rights reserved This work is licensed under a +[Creative Commons Attribution-ShareAlike 3.0 Unported License][cc]. | +
| [judges]: https://www.ioccc.org/judges.html [cc]: +http://creativecommons.org/licenses/by-sa/3.0/ | +
Download IOCCC 2020 entry source
Jump to: top
diff --git a/2020/kurdyukov1/.entry.json b/2020/kurdyukov1/.entry.json index b4c1e1b0d..d9981dc8e 100644 --- a/2020/kurdyukov1/.entry.json +++ b/2020/kurdyukov1/.entry.json @@ -9,14 +9,6 @@ { "author_handle" : "Ilya_Kurdyukov" } ], "manifest" : [ - { - "file_path" : "index.html", - "inventory_order" : 10, - "OK_to_edit" : false, - "display_as" : "html", - "display_via_github" : false, - "entry_text" : "entry home page" - }, { "file_path" : "prog.c", "inventory_order" : 20, @@ -89,6 +81,14 @@ "display_via_github" : false, "entry_text" : "download entry source" }, + { + "file_path" : "index.html", + "inventory_order" : 2000000000, + "OK_to_edit" : false, + "display_as" : "html", + "display_via_github" : false, + "entry_text" : "entry home page" + }, { "file_path" : ".entry.json", "inventory_order" : 4294967295, diff --git a/2020/kurdyukov1/index.html b/2020/kurdyukov1/index.html index 7b9bbfba1..1b326ec05 100644 --- a/2020/kurdyukov1/index.html +++ b/2020/kurdyukov1/index.html @@ -223,9 +223,9 @@ https://creativecommons.org/licenses/by-sa/4.0/