Merge pull request #1307 from xexyl/1991-brnstnd-fmt

This commit is contained in:
Landon Curt Noll authored and GitHub committed 2023-11-06 11:21:40 -07:00
commit a0e05f1263
15 files changed
+464 -135

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
brnstnd brnstnd
brnstnd.orig brnstnd.orig
prog.orig prog.orig
sorta
+1
View File
@@ -133,6 +133,7 @@ all: data ${TARGET}
${PROG}: ${PROG}.c ${PROG}: ${PROG}.c
${CC} ${CFLAGS} $< -o $@ ${LDFLAGS} ${CC} ${CFLAGS} $< -o $@ ${LDFLAGS}
${LN} -sf $@ sorta
# alternative executable # alternative executable
# #
+26 -26
View File
@@ -21,7 +21,7 @@ For info on more examples, read the [sorta.README.md](sorta.README.md) file.
The author wished to win the "most useful program" award and documented this in The author wished to win the "most useful program" award and documented this in
the source code. The judges were unmoved by this blatant attempt to influence the source code. The judges were unmoved by this blatant attempt to influence
the contest and rejected this idea... so we gave it the `Best of Show` the contest and rejected this idea... so we gave it the "Best of Show"
instead!! instead!!
NOTE: One should remove the final trailing newline to obtain the NOTE: One should remove the final trailing newline to obtain the
@@ -51,12 +51,12 @@ instance, not to mention infinite-depth tail recursion.
SORTA ignores its arguments (though it makes them available to the SORTA ignores its arguments (though it makes them available to the
script); it takes all commands, character by character, from its script); it takes all commands, character by character, from its
standard input. Unrecognized commands are repeated with a '?'. standard input. Unrecognized commands are repeated with a `'?'`.
SORTA maintains an `i stack` for integers and an `s stack` for strings. It also SORTA maintains an `i` stack for integers and an `s` stack for strings. It also
keeps track of `programs`, one for each character. Operations are silently keeps track of `programs`, one for each character. Operations are silently
ignored if the relevant stacks are too low, except as noted for `s`, `S`, and ignored if the relevant stacks are too low, except as noted for `s`, `S`, and
`!`. If the `i stack` or `s stack` (or the stack of buffered macro commands) `!`. If the `i` stack or `s` stack (or the stack of buffered macro commands)
grows too high, SORTA will exit silently with exit code 2. (Compile parameter grows too high, SORTA will exit silently with exit code 2. (Compile parameter
`-Do=250` controls what 'too high' means; you should not make `o` larger than `-Do=250` controls what 'too high' means; you should not make `o` larger than
250.) I don't think it's possible to crash the interpreter. 250.) I don't think it's possible to crash the interpreter.
@@ -68,24 +68,24 @@ delimiters.
### Basic operations ### Basic operations
- `q`: quit - `q`: quit
- `number`: push that number on top of the i stack - `number`: push that number on top of the `i` stack
- `"string"`: push that string on top of the s stack, no length limit - `"string"`: push that string on top of the `s` stack, no length limit
- `[string]`: push that string on top of the s stack, no length limit - `[string]`: push that string on top of the `s` stack, no length limit
- `#`: make top of i stack non-destructively in ASCII, push result onto s stack - `#`: make top of `i` stack non-destructively in ASCII, push result onto `s` stack
- ` ` `: print top of s stack non-destructively - ` ` `: print top of `s` stack non-destructively
- `$`: print top of s stack non-destructively without newline - `$`: print top of `s` stack non-destructively without newline
- `d`: drop (pop) top of i stack - `d`: drop (pop) top of `i` stack
- `D`: dup (duplicate) top of i stack - `D`: dup (duplicate) top of `i` stack
- `'`: dup top of s stack - `'`: dup top of s stack
- `s`: pop top of i stack. If it is `n`, swap `(n+1)`th-top of `i` stack with top. - `s`: pop top of i stack. If it is `n`, swap `(n+1)`th-top of `i` stack with top.
`1s`, for example, swaps the top two elements; `2s` swaps the top with the `1s`, for example, swaps the top two elements; `2s` swaps the top with the
third down; etc. This always pops the top of the `i stack`, even upon third down; etc. This always pops the top of the `i` stack, even upon
failure. failure.
- `S`: pop top of `i stack`, then act just like `s` but upon `s stack` - `S`: pop top of `i` stack, then act just like `s` but upon `s` stack
- `l`: pop top of `s stack`, push its length onto `i stack` - `l`: pop top of `s` stack, push its length onto `i` stack
- `a`: push argc onto i stack - `a`: push argc onto i stack
- `A`: pop top of `i stack`, push `argv[i]` on top of `s stack` - `A`: pop top of `i` stack, push `argv[i]` on top of `s` stack
- `T`: concatenate top two elements of `s stack` - `T`: concatenate top two elements of `s` stack
- ` _ `: negate top of i stack - ` _ `: negate top of i stack
- `+`: pop top two elements of i stack, add, push back sum - `+`: pop top two elements of i stack, add, push back sum
- ` * `: pop top two elements of i stack, multiply, push back product - ` * `: pop top two elements of i stack, multiply, push back product
@@ -97,17 +97,17 @@ delimiters.
### System operations ### System operations
- `o`: pop top of `s stack` and top two of `i stack`; `open(s1,i2,i1);` leave - `o`: pop top of `s` stack and top two of `i` stack; `open(s1,i2,i1);` leave
result on `i stack` result on `i` stack
- `O`: pop top of `i stack`, `close()` it - `O`: pop top of `i` stack, `close()` it
- `u`: pop top of `i stack`, `dup()` it, push result back on - `u`: pop top of `i` stack, `dup()` it, push result back on
- `F`: `fork()`, push result on `i stack`. This is not always safe while SORTA is - `F`: `fork()`, push result on `i` stack. This is not always safe while SORTA is
reading keyboard input or a script, as the forked programs share file reading keyboard input or a script, as the forked programs share file
descriptors. It is always safe inside a program (see below). descriptors. It is always safe inside a program (see below).
- `P`: `pipe()`, push two ends `<p0> <p1>` onto `i stack`. In case of trouble, - `P`: `pipe()`, push two ends `<p0> <p1>` onto `i` stack. In case of trouble,
`push <?> <-1>` onto `i stack`. `push <?> <-1>` onto `i` stack.
- `w`: `wait()`, push successful result (or -1 if no children) on `i stack` - `w`: `wait()`, push successful result (or -1 if no children) on `i` stack
- `!`: `execvp()` top of the `s stack`. Arguments are the next things down on - `!`: `execvp()` top of the `s` stack. Arguments are the next things down on
the s stack, in reverse order from popping; the first character of the s stack, in reverse order from popping; the first character of
each string is lopped off. There must be an empty string somewhere each string is lopped off. There must be an empty string somewhere
in the s stack to terminate the argument list. If the execve() in the s stack to terminate the argument list. If the execve()
+15 -15
View File
@@ -56,13 +56,13 @@ This is self-explanatory.
1=D 1s# " | "T 1s#T " is "T D&1sD&& #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 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 `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 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 and 3145. Notice that `SORTA` doesn't have a language primitive for
converting a string to a number, yet `icalc` is performing arithmetic on converting a string to a number, yet `icalc` is performing arithmetic on
its arguments. Can you see how? (Hint: Try `./brnstnd "2 2+" 3 < sorta.icalc`. 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, 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, and if you can't remember offhand that `D2sD3s&2sD&1sD&&&D&` means XOR,
you can just look it up here. you can just look it up here.
@@ -74,7 +74,7 @@ you can just look it up here.
0""1=y` 0""1=y`
This script prints out the maximum file descriptor available from This script prints out the maximum file descriptor available from
repeated dup()s---usually NOFILE - 1. Can you see how it works? repeated `dup(2)`s---usually `NOFILE - 1`. Can you see how it works?
# [sorta.iecho](sorta.iecho) # [sorta.iecho](sorta.iecho)
@@ -83,9 +83,9 @@ repeated dup()s---usually NOFILE - 1. Can you see how it works?
[1s1+DA$ld1s1_+D=sD=x]:xld [ given n (argc-n), print args ]ld [1s1+DA$ld1s1_+D=sD=x]:xld [ given n (argc-n), print args ]ld
0a1_+D=x""` [ simulation of /bin/echo ]ld 0a1_+D=x""` [ simulation of /bin/echo ]ld
This is a clone of /bin/echo, minus any argument processing and escape This is a clone of `/bin/echo`, minus any argument processing and escape
interpretation. (Note that, unlike some shells' echo, /bin/echo prints a interpretation. (Note that, unlike some shells' echo, `/bin/echo` prints a
newline even with no arguments.) Try sorta foo bar < iecho. newline even with no arguments.) Try `./sorta foo bar < iecho`.
# [sorta.ifact1](sorta.ifact1) # [sorta.ifact1](sorta.ifact1)
@@ -105,8 +105,8 @@ multiply by 2, print it, multiply by 3, print it, etc.
1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 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 `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) keeping the current `n!` `n` on the stack. Each call to routine `f (1=f)`
increments n by 1 and prints out the factorial. increments `n` by `1` and prints out the factorial.
# [sorta.ifact3](sorta.ifact3) # [sorta.ifact3](sorta.ifact3)
@@ -117,7 +117,7 @@ increments n by 1 and prints out the factorial.
0 1 10 1=x 0 1 10 1=x
Finally, `ifact3` removes all the redundancy from `ifact2`, and exhibits Finally, `ifact3` removes all the redundancy from `ifact2`, and exhibits
proper documentation of stack routines. You can replace the 10 by proper documentation of stack routines. You can replace the `10` by
anything in this version. anything in this version.
@@ -127,7 +127,7 @@ anything in this version.
["] "2 2+":xT ["]T " produces "T 1=x#T " which should be 4."T` ["] "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. Notice that `"2 2+"` is a literal string inside the program.
# [sorta.irot13](sorta.irot13) # [sorta.irot13](sorta.irot13)
@@ -144,7 +144,7 @@ Notice that "2 2+" is a literal string inside the program.
0Ou1=x [ in a more complex spawner, this would be done under a fork ]ld 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 Despite its name, `irot13` is meant to show how to exec a program with
redirection from within sorta. You run it as `./brnstnd blah < irot13`; it 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 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 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 subroutine `h` to print a nice error message if necessary. `0Ou` closes
@@ -170,8 +170,8 @@ This script illustrates the classic fork-exec-wait.
[3*#" "T$ldD=f]:f [3*#" "T$ldD=f]:f
1=f 1=f
The sole purpose of this script is to prove that sorta can do infinite The sole purpose of this script is to prove that `sorta` can do infinite
tail recursion (in this case, in program `f`). Do `./brnstnd < itailrec | more` 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. and watch the powers of 3 (mod your computer's word size) flow by.
+2 -2
View File
@@ -21,7 +21,7 @@ sorryfor_this_unused_but_very_needed_label:
``` ```
Notice that if you C beautify (cb) or pre-process this program, it Notice that if you C beautify (cb) or pre-process this program, it
will no longer work correctly, or at all. (go ahead, try it... :-)) will no longer work correctly, if at all. (go ahead, try it... :-))
NOTE: One should remove the final trailing newline to obtain the NOTE: One should remove the final trailing newline to obtain the
original source file. This step is not needed to compile original source file. This step is not needed to compile
@@ -36,7 +36,7 @@ letter and key. Thus, the message is code. That is why I use silly
comments, most often badly spaced. comments, most often badly spaced.
The space character sequence between the words is given by a simple The space character sequence between the words is given by a simple
computation on an octal constant (given here as an hex value, of computation in an octal constant (given here as an hex value, of
course). course).
This way, any message can be mixed into a code, since the keys allow This way, any message can be mixed into a code, since the keys allow
+2 -2
View File
@@ -40,7 +40,7 @@ include ../../var.mk
# #
CSILENCE= -Wno-bitwise-op-parentheses -Wno-error -Wno-implicit-function-declaration \ CSILENCE= -Wno-bitwise-op-parentheses -Wno-error -Wno-implicit-function-declaration \
-Wno-int-to-pointer-cast -Wno-return-type -Wno-sign-compare \ -Wno-int-to-pointer-cast -Wno-return-type -Wno-sign-compare \
-Wno-deprecated-non-prototype -Wno-deprecated-non-prototype -Wno-implicit-int
# Common C compiler warning flags # Common C compiler warning flags
# #
@@ -63,7 +63,7 @@ CDEFINE=
# Include files that are needed to compile # Include files that are needed to compile
# #
CINCLUDE= CINCLUDE= -include sys/types.h -include sys/socket.h -include unistd.h
# Optimization # Optimization
# #
+9 -9
View File
@@ -106,15 +106,15 @@ be computed by a boolean circuit. In addition, intermediate values
computed by the circuit can be shared between neighboring cells, computed by the circuit can be shared between neighboring cells,
reducing the number of gates per cell required. These ideas reducing the number of gates per cell required. These ideas
have been used before, to compute the next generation through a have been used before, to compute the next generation through a
series of bit blits. Instead of doing this, we map values in the series of [bit blits](https://en.wikipedia.org/wiki/Bit_blit).
circuit to bits in registers, so that the next generation can be
computed efficiently within registers, minimizing memory accesses. Instead of doing this, we map values in the circuit to bits in registers, so
As a result, the computation of the next generation is performed that the next generation can be computed efficiently within registers,
with about 1.6 instructions per life cell, consisting of .125 minimizing memory access. As a result, the computation of the next generation
memory accesses, .17 shifts, and 1.3 logic operations. The net is performed with about 1.6 instructions per life cell, consisting of .125
result is that the time to transfer the bits to the X server, and memory accesses, .17 shifts, and 1.3 logic operations. The net result is that
for the X server to draw them on the screen, dominates the time to the time to transfer the bits to the X server, and for the X server to draw them
compute the next generation. on the screen, dominates the time to compute the next generation.
## Copyright and CC BY-SA 4.0 License: ## Copyright and CC BY-SA 4.0 License:
+24 -23
View File
@@ -8,7 +8,7 @@ make all
## To use: ## To use:
```sh ```sh
./dds basic_program 2>/dev/null ./dds basic_program
./a.out 2>/dev/null ./a.out 2>/dev/null
``` ```
@@ -18,14 +18,17 @@ make all
The author suggests trying: The author suggests trying:
```sh ```sh
./dds LANDER.BAS 2>/dev/null ./dds LANDER.BAS
./a.out 2>/dev/null ./a.out
``` ```
Notice that a file `a.c` has been generated. Can you tell how `a.c` was
produced? How does `a.c` relate to [LANDER.BAS](LANDER.BAS)?
What happens if you give the program a C program like itself? Try: What happens if you give the program a C program like itself? Try:
```sh ```sh
./dds dds.c 2>/dev/null ./dds dds.c
``` ```
You'll get errors yes but what does the generated file look like? What about You'll get errors yes but what does the generated file look like? What about
@@ -34,9 +37,6 @@ other types of files?
## Judges' remarks: ## Judges' remarks:
Notice that a file `a.c` has been generated. Can you tell how `a.c` was
produced? How does `a.c` relate to [LANDER.BAS](LANDER.BAS)?
This obfuscated program translates BASIC programs into obfuscated This obfuscated program translates BASIC programs into obfuscated
C programs by way of an obfuscated algorithm. C programs by way of an obfuscated algorithm.
@@ -45,15 +45,16 @@ C programs by way of an obfuscated algorithm.
This program is a companion to the [DDS-BASIC interpreter This program is a companion to the [DDS-BASIC interpreter
program](/1990/dds/README.md) that was submitted to [the contest in program](/1990/dds/README.md) that was submitted to [the contest in
1990](/years.html#1990). This compiles BASIC programs into executable commands. 1990](/years.html#1990). This compiles BASIC programs into compilable C code
The input format is almost identical to the input format of the DDS-BASIC and then compiles it. The input format is almost identical to the input format
interpreter. The program needs an executable C compiler called `cc` in your of the DDS-BASIC interpreter. The program needs an executable C compiler called
path in order to work. `cc` in your path in order to work.
### Program commands: ### Program commands:
- variable names A to Z - variable names `A` to `Z`
- FOR var = exp TO exp - FOR var = exp TO exp
- GOSUB exp - GOSUB exp
- GOTO exp - GOTO exp
@@ -75,7 +76,7 @@ Many system calls and C library calls can be used.
- Free format positioning of tokens on the line. - Free format positioning of tokens on the line.
- No space is allowed before the line number. - No space is allowed before the line number.
- Exactly one space is needed after the FOR and NEXT tokens - Exactly one space is needed after the `FOR` and `NEXT` tokens.
- ALL INPUT MUST BE UPPERCASE. - ALL INPUT MUST BE UPPERCASE.
### Error checking / error reports: ### Error checking / error reports:
@@ -85,19 +86,19 @@ Other errors may produce errors in later phases of the compilation.
### Can you figure out how the compiler works? ### Can you figure out how the compiler works?
Hint: #### Hint:
The compiler is NOT written in C, so this is really a meta-obfuscated The compiler is NOT written in C, so this is really a meta-obfuscated
program. The C code is an interpreter for the four register, seven program. The C code is an interpreter for the four register, seven
instruction COGNIMP$ machine that is contained in the `s` string. instruction `COGNIMP$` machine that is contained in the `s` string.
(COGNIMP$ is named after the symbolic names of the seven instructions it (`COGNIMP$` is named after the symbolic names of the seven instructions it
defines (Print Output Goto Match If, iNcrement and Copy). The `$` sign defines (`Copy`, `Output`, `Goto`, `iNcrement`, `If`, `Match` and `Print`).
is used for labels.) The actual compiler is written in COGNIMP$.
Browsing through the COGNIMP$ code we encourage you to examine the The `$` sign is used for labels. The actual compiler is written in `COGNIMP$`.
loops for scanning the expression in the IF statement and the way a Browsing through the `COGNIMP$` code we encourage you to examine the loops for
decision tree is implemented in order to match the statements. (Of scanning the expression in the `IF` statement and the way a decision tree is
course the `s` string is encoded by adding one to every character of it just implemented in order to match the statements. (Of course the `s` string is
to confuse you). encoded by adding one to every character of it just to confuse you).
## Copyright and CC BY-SA 4.0 License: ## Copyright and CC BY-SA 4.0 License:
+2 -1
View File
@@ -66,7 +66,8 @@ version end up allowing any number of movements?
## Judges' remarks: ## Judges' remarks:
This entry has been updated to allow most ANSI C compilers. In 2023 this entry was been updated to allow most ANSI C compilers to compile
the code and to fix a bug where the win or lose message was not seen.
## Author's remarks: ## Author's remarks:
+6
View File
@@ -1,11 +1,17 @@
merlyn merlyn
merlyn.c merlyn.c
merlyn.alt.c
nextmove nextmove
nextmove.c nextmove.c
nextmove.alt.c
ttt ttt
ttt.alt
ttt_game ttt_game
ttt_game.c ttt_game.c
ttt_game.alt.c
westley westley
westley.alt
ttt.alt_old.c
ttt_old.c ttt_old.c
westley.orig westley.orig
prog.orig prog.orig
+59 -3
View File
@@ -117,8 +117,8 @@ OBJ= ${PROG}.o
DATA= DATA=
TARGET= ${PROG} ttt TARGET= ${PROG} ttt
# #
ALT_OBJ= ALT_OBJ= ${PROG}.alt.o
ALT_TARGET= ALT_TARGET= ${PROG}.alt ttt.alt
################# #################
@@ -190,6 +190,60 @@ ttt_game.c: westley.c
${CP} -f westley.c ttt_game.c ${CP} -f westley.c ttt_game.c
${CHMOD} +w ttt_game.c ${CHMOD} +w ttt_game.c
# alt version
#
# the hint file gives nextmove.alt.c as an example, provide a build rule for it
#
nextmove.alt: nextmove.alt.c ttt_alt
${CC} ${CFLAGS} -DP=putchar -DN="P(10)" -DQ="P(34)" -DF="S(&X)" \
-DJ=z[4] -DK=J[14] \
-DH="w[]={146,7,292,73,448,56,84,273,325,297,108,324,22,40,48,40,17,325}" \
nextmove.alt.c -o nextmove.alt
# the ttt_alt script has been added by the judges to make playing westley.alt easy
#
ttt_alt: ttt_alt.sh
${RM} -f ttt_alt
${CP} -f ttt_alt.sh ttt_alt
${CHMOD} +x ttt_alt
# used by the ttt_alt shell script when playing westley.alt
#
# The ttt_alt script controls what goes into merlyn.alt.c, and how it is used.
# The merlyn.alt program and source represent the best (hopefully) state that
# the program has evolved into thus far. The ttt_alt_game program and source
# represent the current state of the game.
#
merlyn.alt: merlyn.alt.c ttt_alt
@${CC} ${CFLAGS} -DP=putchar -DN="P(10)" -DQ="P(34)" -DF="S(&X)" \
-DJ=z[4] -DK=J[14] \
-DH="w[]={146,7,292,73,448,56,84,273,325,297,108,324,22,40,48,40,17,325}" \
merlyn.alt.c -o merlyn.alt
ttt_alt_game: ttt_alt_game.c ttt_alt
@${CC} ${CFLAGS} -DP=putchar -DN="P(10)" -DQ="P(34)" -DF="S(&X)" \
-DJ=z[4] -DK=J[14] \
-DH="w[]={146,7,292,73,448,56,84,273,325,297,108,324,22,40,48,40,17,325}" \
ttt_alt_game.c -o ttt_alt_game
# The upper level makefile wants to make these source files if they don't
# already exist.
#
merlyn.alt.c: westley.alt.c
${RM} -f merlyn.alt.c
${CP} -f westley.alt.c merlyn.alt.c
${CHMOD} +w merlyn.alt.c
nextmove.alt.c: westley.alt.c
${RM} -f nextmove.alt.c
${CP} -f westley.alt.c nextmove.alt.c
${CHMOD} +w nextmove.alt.c
ttt_alt_game.c: westley.alt.c
${RM} -f ttt_alt_game.c
${CP} -f westley.alt.c ttt_alt_game.c
${CHMOD} +w ttt_alt_game.c
# data files # data files
# #
data: ${DATA} data: ${DATA}
@@ -256,8 +310,10 @@ clean:
clobber: clean clobber: clean
${RM} -f ${TARGET} ${ALT_TARGET} ${RM} -f ${TARGET} ${ALT_TARGET}
${RM} -f merlyn merlyn.c nextmove.c nextmove.c ${RM} -f merlyn merlyn.c nextmove nextmove.c
${RM} -f merlyn.alt merlyn.alt.c nextmove.alt nextmove.alt.c
${RM} -f ttt_game ttt_game.c ttt_old.c ttt_new.c nextmove ${RM} -f ttt_game ttt_game.c ttt_old.c ttt_new.c nextmove
${RM} -f ttt.alt_game ttt.alt_game.c ttt.alt_old.c ttt_.altnew.c nextmove.alt
@-if [ -e sandwich ]; then \ @-if [ -e sandwich ]; then \
${RM} -f sandwich; \ ${RM} -f sandwich; \
echo 'ate sandwich'; \ echo 'ate sandwich'; \
+77 -54
View File
@@ -4,6 +4,8 @@
make all make all
``` ```
There is an [alternate version](#alternate-code) that is supposed to win all the
time.
### Bugs and (Mis)features: ### Bugs and (Mis)features:
@@ -18,7 +20,11 @@ For more detailed information see [1991 westley in bugs.md](/bugs.md#1991-westle
## To use: ## To use:
Make and run as follows:
NOTE: we have provided a script to perform the below procedure which we include
for the interested. See the [try](#try) section if you just want to play.
Make and then run as follows:
```sh ```sh
@@ -34,7 +40,7 @@ a move on a tic-tac-toe board:
7 8 9 7 8 9
``` ```
If you omit `move_location`, then the computer moves first. For your\ If you omit `move_location`, then the computer moves first. For your
next move, recompile `nextmove.c` and play it again: next move, recompile `nextmove.c` and play it again:
```sh ```sh
@@ -45,7 +51,8 @@ make nextmove
### Try: ### Try:
We have provided the shell script, [ttt](ttt.sh), to make it easier to play the game: We have provided the shell script, [ttt](ttt.sh), to make it easier to play the
game:
```sh ```sh
make ttt make ttt
@@ -54,12 +61,12 @@ make ttt
``` ```
The shell script determines when the game is over, and automatically\ The shell script determines when the game is over, and automatically replaces
replaces `merlyn.c` (copied from [westley.c](westley.c) with an improved version after a loss. The [ttt](ttt.sh) script\ `merlyn.c` (copied from [westley.c](westley.c) with an improved version after a
has two other modes: loss). The [ttt](ttt.sh) script has two other modes:
```sh ```sh
./ttt quitgame # cancel any game in process, revert to merlyn.c ./ttt quitgame # cancel any game in progress, revert to merlyn.c
./ttt clobber # remove all game files, revert to original source ./ttt clobber # remove all game files, revert to original source
``` ```
@@ -109,6 +116,24 @@ Redirects stderr to `/dev/null` by default. If you need errors or warnings use
`-e`. `-e`.
## Alternate code:
This version is based on the author's remarks. It is not supposed to lose but it
can lose if you cheat. That's supposed to happen. Can you win any other time?
### Alternate build:
```sh
make alt
```
### Alternate use:
Rather than use `ttt` try the script `ttt.alt` instead.
## Judges' remarks: ## Judges' remarks:
Can you figure out why the board looks different than the Can you figure out why the board looks different than the
@@ -119,12 +144,12 @@ What happens if you make a move already made?
Now go try and draw or better yet win (without cheating which is very easy :-) )! Now go try and draw or better yet win (without cheating which is very easy :-) )!
Why is win better than draw? Because everyone knows how to draw: Why is win better than draw? Because everyone knows how to draw:
> Professor Falken: Did you ever play tic-tac-toe? > Professor Falken: Did you ever play tic-tac-toe?\
> Jennifer Mack: Yeah. Of course. > Jennifer Mack: Yeah. Of course.\
> Falken: But you don't any more. > Falken: But you don't any more.\
> Jennifer: No. > Jennifer: No.\
> Falken: Why? > Falken: Why?\
> Jennifer: Because it's a boring game. It's always a tie. > Jennifer: Because it's a boring game. It's always a tie.\
> Falken: Exactly. There's no way to win. The game itself is pointless. But back > Falken: Exactly. There's no way to win. The game itself is pointless. But back
at the [International Obfuscated C Code Contest judges' at the [International Obfuscated C Code Contest judges'
room](https://www.ioccc.org/https://www.ioccc.org/judges.html) they believe that room](https://www.ioccc.org/https://www.ioccc.org/judges.html) they believe that
@@ -153,12 +178,11 @@ The program, when run, reproduces itself with both the player's
move and the computer's move added. Recompile THIS program (using move and the computer's move added. Recompile THIS program (using
the same compile line) and repeat until the game is finished. the same compile line) and repeat until the game is finished.
If the computer wins, the "straight face" in the upper right-hand If the computer wins, the "straight face" in the upper right-hand corner, ":-|",
corner ":-|" will change into a happy face ":-)". If it is a draw, will change into a happy face, ":-)". If it is a draw, the face does not
the face does not change. If the computer loses, the board is change. If the computer loses, the board is blanked and the face changes into a
blanked and the face changes into a sad face ":-(". If this sad face, ":-(". If this happens, the blank board should replace the original
happens, the blank board should replace the original program; the program; the computer changes its play and will continue to do so until it no
computer changes its play and will continue to do so until it no
longer loses games. longer loses games.
If you want a program that never loses, simply replace the string If you want a program that never loses, simply replace the string
@@ -166,47 +190,47 @@ If you want a program that never loses, simply replace the string
### How it works ### How it works
The vertical "bars" of the grid are identical functions, with the The vertical "bars" of the grid are identical functions, with the name of the
name of the function as a(), b(), or c(). After each function is function as `a()`, `b()`, or `c()`. After each function is called, the global
called, the global variables X and O contain bitmasks of where all variables `X` and `O` contain bitmasks of where all the `X`s and `O`s are placed.
the Xs and Os are placed. The statement: The statement:
```c ```c
X=- <char> -1; X=- <char> -1;
`` ```
evaluates to 1, -1, or -2 when X is 1 and <char> is one of "X", evaluates to `1`, `-1`, or `-2` when `X` is `1` and `<char>` is one of `"X"`,
"0", or " ". The function S(&X) sets the appropriate bits in `"0"`, or `" "`. The function `S(&X)` sets the appropriate bits in (global
(global var) X and O and resets the value of &X to 1. This is how vars) `X` and `O` and resets the value of `&X` to `1`. This is how the current
the current position is computed. position is computed.
The player's move is OR'd into the X mask, and (if it is the The player's move is bitwise ORed into the `X` mask, and (if it is the opening
opening move) the strategy string's first character is set to the move) the strategy string's first character is set to the player's first move
player's first move (or "5" if the computer moves first). (or `5` if the computer moves first).
The evalution function y(M) checks if the mask has a winning The evaluation function `y(M)` checks if the mask has a winning pattern (any of
pattern (any of the first eight bitmasks of the w[N array), and the first eight bitmasks of the `w[N]` array), and sets the variable `q` to `0`
sets the variable q to 0 if it does. if it does.
The player's move is evaluated to see if the human has won. The The player's move is evaluated to see if the human has won. The smiley is set
smiley is set to :-) or :-( depending on this. Also, if the human to ":-)" or ":-(" depending on this. Also, if the human has won, the digit in
has won, the digit in the strategy string is decremented to change the strategy string is decremented to change the computer's play for the next
the computer's play for next game. round.
If the human has NOT won, the e(N,M) function evaluates if ANY move If the human has NOT won, the `e(N,M)` function evaluates if ANY move
not in mask N can produce a win in mask M. The first evaluation not in mask `N` can produce a win in mask `M`. The first evaluation
e(X,O) checks if O can make a legal winning move. If so, the `e(X,O)` checks if `O` can make a legal winning move. If so, the
computer has a winning move and makes it. If not, the smiley is computer has a winning move and makes it. If not, the smiley is
set to :-|, and a non-winning move is generated. set to ":-|", and a non-winning move is generated.
A non-winning move first checks if X can win in one move; if so, A non-winning move first checks if `X` can win in one move; if so,
this move is selected, blocking the win. Otherwise, a move is this move is selected, blocking the win. Otherwise, a move is
generated by looking down the list of "move templates". The first generated by looking down the list of "move templates". The first
move template that generates a move that is NOT already occupied by move template that generates a move that is NOT already occupied by
an X or O is returned. an `X` or `O` is returned.
The "move templates" are static tic-tac-toe positions that generate The "move templates" are static tic-tac-toe positions that generate
possible moves by making the e() function return "winning" moves, possible moves by making the `e()` function return "winning" moves,
e.g. the template: e.g. the template:
``` ```
@@ -215,14 +239,13 @@ O , O (this is the value 40; 2^3+2^5)
. . . ("O"s are 1 bits, "." and "," are 0 bits) . . . ("O"s are 1 bits, "." and "," are 0 bits)
``` ```
...will make the e() function return a center move, as ...will make the `e()` function return a center move, as this is the only move
this is the only move that produces three in a row. Notice that that produces three in a row. Notice that the computer does not consider which
the computer does not consider which cells contain Xs and Os when cells contain `X`s and `O`s when making a template move, only which cells are
making a template move, only which cells are empty and not-empty. empty and not-empty. This makes for unusual play.
This makes for unusual play.
The templates are carefully chosen to eventually block all traps The templates are carefully chosen to eventually block all traps that `X` can
that X can try, by just blundering in the way. try, by just blundering in the way.
Also note that the winning & strategy templates are part of the Also note that the winning & strategy templates are part of the
compile line, making it possible to change the rules of the game by compile line, making it possible to change the rules of the game by
@@ -272,10 +295,10 @@ O , O
O , O O , O
``` ```
A digit of "9" means start with the first template, "8" starts at A digit of `9` means start with the first template, `8` starts at
the second, etc. The templates are scanned until a legal move is the second, etc. The templates are scanned until a legal move is
found. A template is scanned from bottom to top, right to left found. A template is scanned from bottom to top, right to left
(i.e. move 9 is tested first, then 8, down to move 1). (i.e. move `9` is tested first, then `8`, down to move `1`).
## Copyright and CC BY-SA 4.0 License: ## Copyright and CC BY-SA 4.0 License:
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env bash
#
# play tic-tac-toe
# parse args
#
export USAGE="usage: $0 [-h] [-e] <command>
-h show this help and exit
-e show compiler stdout
commands:
./ttt.alt [move_digit] play a place
./ttt.alt quitgame quit the game
./ttt.alt clobber clean the state of the game
"
export E_FLAG=
while getopts :he flag; do
case "$flag" in
h) echo "$USAGE" 1>&2
exit 2
;;
e) E_FLAG="1"
;;
\?) echo "$0: ERROR: invalid option: -$OPTARG" 1>&2
exit 3
;;
:) echo "$0: ERROR: option -$OPTARG requires an argument" 1>&2
exit 3
;;
*)
;;
esac
done
# check args
#
shift $(( OPTIND - 1 ));
if [[ $# -gt 1 ]]; then
echo "$USAGE" 1>&2
exit 1
elif [[ $# -eq 1 ]]; then
move="$1"
else
move=
fi
# catch the clobber case
#
if [[ clobber = "$move" ]]; then
echo "ttt.alt: rm -f ttt.alt_game ttt.alt_game.o ttt.alt_game.c ttt.alt_old.c ttt.alt_new.c" 1>&2
rm -f ttt.alt_game ttt.alt_game.o ttt.alt_game.c ttt.alt_old.c ttt.alt_new.c
echo "ttt.alt: rm -f merlyn.alt.c merlyn.o merlyn" 1>&2
rm -f merlyn.alt.c merlyn.o merlyn
echo "ttt.alt: removed all but the original source" 1>&2
exit 0
fi
# catch the quitgame case
#
if [[ quitgame = "$move" ]]; then
echo "ttt.alt: rm -f ttt.alt_game ttt.alt_game.o ttt.alt_game.c ttt.alt_old.c ttt.alt_new.c" 1>&2
rm -f ttt.alt_game ttt.alt_game.o ttt.alt_game.c ttt.alt_old.c ttt.alt_new.c
echo "ttt.alt: current game removed, perhaps you were going to lose? :-)" 1>&2
exit 0
fi
# watch for bad args
#
if [[ $# -eq 1 ]]; then
case "$move" in
1|2|3|4|5|6|7|8|9) ;;
*) echo "ttt.alt: bad argument" 1>&2
echo "usage: ./ttt.alt [move_digit]" 1>&2
echo " ./ttt.alt quitgame" 1>&2
echo " ./ttt.alt clobber" 1>&2
exit 2 ;;
esac
fi
# be sure that we have the merlyn.alt.c source
#
if [[ ! -f merlyn.alt.c ]]; then
if [[ -f westley.alt.c ]]; then
echo "ttt.alt: No merlyn.alt.c around these parts, using a copy of westley.alt.c" 1>&2
cp westley.alt.c merlyn.alt.c
chmod +w merlyn.alt.c
else
echo "ttt.alt: Ouch! merlyn.alt.c and no westley.alt.c are gone!" 1>&2
exit 3
fi
# check to see if the copy worked
if [[ ! -f merlyn.alt.c ]]; then
echo "ttt.alt: Darn, couldn't copy westley.alt.c to merlyn.alt.c" 1>&2
exit 4
fi
fi
# be sure we have the merlyn program
#
if [[ ! -f merlyn ]]; then
export status=0
if [[ -n $E_FLAG ]]; then
echo "ttt.alt: make merlyn" 1>&2
make merlyn
status=$?
else
make merlyn 2>/dev/null
status=$?
fi
if [[ "$status" -ne 0 ]]; then
echo "ttt.alt: something is wrong, fix merlyn.alt.c and build it yourself!" 1>&2
echo " or maybe type: ./ttt.alt clobber" 1>&2
exit 5
fi
rm -f ttt.alt_game.c
fi
# If we don't have a ttt.alt_game.c, or we cannot write it, make a new ttt.alt_game.c
#
if [[ ! -w ttt.alt_game.c ]]; then
rm -f ttt.alt_game.c
if [[ -z "$move" ]]; then
./merlyn | tee ttt.alt_game.c
else
./merlyn "$move" | tee ttt.alt_game.c
fi
chmod +w ttt.alt_game.c
# If we have a ttt.alt_game.c, then assume that we are in the process of playing
#
else
rm -f ttt.alt_game ttt.alt_new.c
export status=0
if [[ -n $E_FLAG ]]; then
echo "ttt.alt: make ttt.alt_game" 1>&2
make ttt.alt_game
status=$?
else
make ttt.alt_game 2>/dev/null
status=$?
fi
if [[ "$status" -ne 0 ]]; then
echo "ttt.alt: something is wrong, fix ttt.alt_game.c and build it yourself!" 1>&2
echo " or maybe type: ./ttt.alt quitgame" 1>&2
exit 6
fi
if [[ -z "$move" ]]; then
./ttt.alt_game | tee ttt.alt_new.c
else
./ttt.alt_game "$move" | tee ttt.alt_new.c
fi
chmod +w ttt.alt_new.c
if [[ -s ttt.alt_new.c ]]; then
mv ttt.alt_game.c ttt.alt_old.c
mv ttt.alt_new.c ttt.alt_game.c
# Check for win, lose, or draw
# For a win or draw, copy merlyn.alt.c to ttt.alt_game.c
# For a loss, copy ttt.alt_game.c to merlyn.alt.c, as it is now better
# Also remove the executable for the file replaced
# For anything else, game in progress, do not do anything
# SC2006 (style): Use $(...) notation instead of legacy backticks `...`.
# shellcheck disable=SC2006
if [[ -n "`grep ':-(' ttt.alt_game.c`" ]]; then
echo
echo "ttt.alt: I lose...I won't make THAT mistake again!"
chmod +w merlyn.alt.c
mv ttt.alt_game.c merlyn.alt.c
rm -f merlyn ttt.alt_game
else
# SC2006 (style): Use $(...) notation instead of legacy backticks `...`.
# shellcheck disable=SC2006
if [[ -n "`grep ':-)' ttt.alt_game.c`" ]]; then
echo
echo "ttt.alt: I win!"
cp merlyn.alt.c ttt.alt_game.c
rm -f ttt.alt_game
else
# SC2006 (style): Use $(...) notation instead of legacy backticks `...`.
# shellcheck disable=SC2006
if [[ -z "`grep 'X=-. ' ttt.alt_game.c`" && -z "`grep '^. ' ttt.alt_game.c`" ]]; then
echo
echo "ttt.alt: It's a draw - play again?"
cp merlyn.alt.c ttt.alt_game.c
rm -f ttt.alt_game
fi
fi
fi
else
echo "ttt.alt: something is wrong, maybe ttt.alt_old.c will help" 1>&2
echo " or maybe type: ./ttt.alt quitgame" 1>&2
echo " or maybe type: ./ttt.alt clobber" 1>&2
exit 7
fi
fi
+26
View File
@@ -0,0 +1,26 @@
a(X){/*/X=- a(X){/*/X=-
-1;F;X=- -1;F;X=-
-1;F;}/*/ -1;F;}/*/
char z[][100]={"char z[][100]={","a(X){/*/X=-","-1;F;X=-","-1;F;}/*/","9883857753 :-|",
"int q,i,j,k,X,O=0,H;S(x)int*x;{X+=X;O+=O;*x+1?*x+2||X++:O++;*x=1;}L(n){for(*",
"z[i=1]=n+97;i<4;i++)M(256),s(i),M(128),s(i),M(64),N;X*=8;O*=8;}s(R){char*r=z",
"[R];for(q&&Q;*r;)P(*r++);q&&(Q,P(44));}M(m){P(9);i-2||P(X&m?88:O&m?48:32);P(",
"9);}y(A){for(j=8;j;)~A&w[--j]||(q=0);}e(W,Z){for(i-=i*q;i<9&&q;)y(W|(1<<i++&",
"~Z));}R(){for(k=J[*J-48]-40;k;)e(w[k--],X|O);}main(u,v)char**v;{a(q=1);b(1);",
"c(1);*J=--u?O?*J:*v[1]:53;X|=u<<57-*v[u];y(X);K=40+q;q?e(O,X),q&&(K='|'),e(X",
",O),R(),O|=1<<--i:J[*J-48+(X=O=0)]--;L(q=0);for(s(i=0);q=i<12;)s(i++),i>4&&N",
";s(q=12);P(48);P('}');P(59);N;q=0;L(1);for(i=5;i<13;)s(i++),N;L(2);}",0};
b(X){/*/X=- b(X){/*/X=-
-1;F;X=- -1;F;X=-
-1;F;}/*/ -1;F;}/*/
int q,i,j,k,X,O=0,H;S(x)int*x;{X+=X;O+=O;*x+1?*x+2||X++:O++;*x=1;}L(n){for(*
z[i=1]=n+97;i<4;i++)M(256),s(i),M(128),s(i),M(64),N;X*=8;O*=8;}s(R){char*r=z
[R];for(q&&Q;*r;)P(*r++);q&&(Q,P(44));}M(m){P(9);i-2||P(X&m?88:O&m?48:32);P(
9);}y(A){for(j=8;j;)~A&w[--j]||(q=0);}e(W,Z){for(i-=i*q;i<9&&q;)y(W|(1<<i++&
~Z));}R(){for(k=J[*J-48]-40;k;)e(w[k--],X|O);}main(u,v)char**v;{a(q=1);b(1);
c(1);*J=--u?O?*J:*v[1]:53;X|=u<<57-*v[u];y(X);K=40+q;q?e(O,X),q&&(K='|'),e(X
,O),R(),O|=1<<--i:J[*J-48+(X=O=0)]--;L(q=0);for(s(i=0);q=i<12;)s(i++),i>4&&N
;s(q=12);P(48);P('}');P(59);N;q=0;L(1);for(i=5;i<13;)s(i++),N;L(2);}
c(X){/*/X=- c(X){/*/X=-
-1;F;X=- -1;F;X=-
-1;F;}/*/ -1;F;}/*/
+14
View File
@@ -1129,6 +1129,14 @@ him): `k` for forward, `h` for left and `l` for right. This version also has a
more useful way to exit, just entering `q` followed by enter. more useful way to exit, just entering `q` followed by enter.
## [1991/davidguy](1991/davidguy/davidguy.c) ([README.md](1991/davidguy/README.md]))
As some systems like macOS can be particular about not declaring functions Cody
added to the Makefile some `-include` options. These appear to not be strictly
necessary but it was done due to other syscalls being a problem not being
declared first.
## [1991/dds](1991/dds/dds.c) ([README.md](1991/dds/README.md])) ## [1991/dds](1991/dds/dds.c) ([README.md](1991/dds/README.md]))
Cody fixed a segfault that prevented this entry from working in any condition Cody fixed a segfault that prevented this entry from working in any condition
@@ -1282,6 +1290,12 @@ also improved it so that warnings/errors/about to compile messages are not shown
unless the `-e` option is used. This is because the errors being shown kind of unless the `-e` option is used. This is because the errors being shown kind of
ruins the experience. Finally he made it pass ShellCheck. ruins the experience. Finally he made it pass ShellCheck.
Cody also added the alt version which is based on the author's remarks, a
version that supposedly (:-) ) always wins.
Cody also fixed the make clobber rule where a file was left lying about when it
should have been removed.
## [1992/adrian](1992/adrian/adrian.c) ([README.md](1992/adrian/README.md])) ## [1992/adrian](1992/adrian/adrian.c) ([README.md](1992/adrian/README.md]))