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.orig
prog.orig
sorta
+1
View File
@@ -133,6 +133,7 @@ all: data ${TARGET}
${PROG}: ${PROG}.c
${CC} ${CFLAGS} $< -o $@ ${LDFLAGS}
${LN} -sf $@ sorta
# 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 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!!
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
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
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
`-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.
@@ -68,24 +68,24 @@ delimiters.
### Basic operations
- `q`: quit
- `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
- `#`: 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 without newline
- `d`: drop (pop) top of i stack
- `D`: dup (duplicate) top of 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
- `#`: 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 without newline
- `d`: drop (pop) top of `i` stack
- `D`: dup (duplicate) top of `i` 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.
`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.
- `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`
- `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
- `a`: push argc onto i stack
- `A`: pop top of `i stack`, push `argv[i]` on top of `s stack`
- `T`: concatenate top two elements 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
- ` _ `: negate top of i stack
- `+`: pop top two elements of i stack, add, push back sum
- ` * `: pop top two elements of i stack, multiply, push back product
@@ -97,17 +97,17 @@ delimiters.
### System operations
- `o`: pop top of `s stack` and top two of `i stack`; `open(s1,i2,i1);` leave
result on `i stack`
- `O`: pop top of `i stack`, `close()` it
- `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
- `o`: pop top of `s` stack and top two of `i` stack; `open(s1,i2,i1);` leave
result on `i` stack
- `O`: pop top of `i` stack, `close()` it
- `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
reading keyboard input or a script, as the forked programs share file
descriptors. It is always safe inside a program (see below).
- `P`: `pipe()`, push two ends `<p0> <p1>` onto `i stack`. In case of trouble,
`push <?> <-1>` onto `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
- `P`: `pipe()`, push two ends `<p0> <p1>` onto `i` stack. In case of trouble,
`push <?> <-1>` onto `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
the s stack, in reverse order from popping; the first character of
each string is lopped off. There must be an empty string somewhere
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 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
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
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,
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.
@@ -74,7 +74,7 @@ you can just look it up here.
0""1=y`
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)
@@ -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
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.
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`.
# [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
`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.
keeping the current `n!` `n` on the stack. Each call to routine `f (1=f)`
increments `n` by `1` and prints out the factorial.
# [sorta.ifact3](sorta.ifact3)
@@ -117,7 +117,7 @@ increments n by 1 and prints out the factorial.
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
proper documentation of stack routines. You can replace the `10` by
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`
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)
@@ -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
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
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
@@ -170,8 +170,8 @@ 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 `./brnstnd < itailrec | more`
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.
+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
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
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.
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).
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 \
-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
#
@@ -63,7 +63,7 @@ CDEFINE=
# Include files that are needed to compile
#
CINCLUDE=
CINCLUDE= -include sys/types.h -include sys/socket.h -include unistd.h
# 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,
reducing the number of gates per cell required. These ideas
have been used before, to compute the next generation through a
series of bit blits. Instead of doing this, we map values in the
circuit to bits in registers, so that the next generation can be
computed efficiently within registers, minimizing memory accesses.
As a result, the computation of the next generation is performed
with about 1.6 instructions per life cell, consisting of .125
memory accesses, .17 shifts, and 1.3 logic operations. The net
result is that the time to transfer the bits to the X server, and
for the X server to draw them on the screen, dominates the time to
compute the next generation.
series of [bit blits](https://en.wikipedia.org/wiki/Bit_blit).
Instead of doing this, we map values in the circuit to bits in registers, so
that the next generation can be computed efficiently within registers,
minimizing memory access. As a result, the computation of the next generation
is performed with about 1.6 instructions per life cell, consisting of .125
memory accesses, .17 shifts, and 1.3 logic operations. The net result is that
the time to transfer the bits to the X server, and for the X server to draw them
on the screen, dominates the time to compute the next generation.
## Copyright and CC BY-SA 4.0 License:
+24 -23
View File
@@ -8,7 +8,7 @@ make all
## To use:
```sh
./dds basic_program 2>/dev/null
./dds basic_program
./a.out 2>/dev/null
```
@@ -18,14 +18,17 @@ make all
The author suggests trying:
```sh
./dds LANDER.BAS 2>/dev/null
./a.out 2>/dev/null
./dds LANDER.BAS
./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:
```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
@@ -34,9 +37,6 @@ other types of files?
## 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
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
program](/1990/dds/README.md) that was submitted to [the contest in
1990](/years.html#1990). This compiles BASIC programs into executable commands.
The input format is almost identical to the input format of the DDS-BASIC
interpreter. The program needs an executable C compiler called `cc` in your
path in order to work.
1990](/years.html#1990). This compiles BASIC programs into compilable C code
and then compiles it. The input format is almost identical to the input format
of the DDS-BASIC interpreter. The program needs an executable C compiler called
`cc` in your path in order to work.
### Program commands:
- variable names A to Z
- variable names `A` to `Z`
- FOR var = exp TO exp
- GOSUB 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.
- 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.
### 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?
Hint:
#### Hint:
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
instruction COGNIMP$ machine that is contained in the `s` string.
(COGNIMP$ is named after the symbolic names of the seven instructions it
defines (Print Output Goto Match If, iNcrement and Copy). The `$` sign
is used for labels.) The actual compiler is written in COGNIMP$.
Browsing through the COGNIMP$ code we encourage you to examine the
loops for scanning the expression in the IF statement and the way a
decision tree is implemented in order to match the statements. (Of
course the `s` string is encoded by adding one to every character of it just
to confuse you).
instruction `COGNIMP$` machine that is contained in the `s` string.
(`COGNIMP$` is named after the symbolic names of the seven instructions it
defines (`Copy`, `Output`, `Goto`, `iNcrement`, `If`, `Match` and `Print`).
The `$` sign is used for labels. The actual compiler is written in `COGNIMP$`.
Browsing through the `COGNIMP$` code we encourage you to examine the loops for
scanning the expression in the `IF` statement and the way a decision tree is
implemented in order to match the statements. (Of course the `s` string is
encoded by adding one to every character of it just to confuse you).
## 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:
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:
+6
View File
@@ -1,11 +1,17 @@
merlyn
merlyn.c
merlyn.alt.c
nextmove
nextmove.c
nextmove.alt.c
ttt
ttt.alt
ttt_game
ttt_game.c
ttt_game.alt.c
westley
westley.alt
ttt.alt_old.c
ttt_old.c
westley.orig
prog.orig
+59 -3
View File
@@ -117,8 +117,8 @@ OBJ= ${PROG}.o
DATA=
TARGET= ${PROG} ttt
#
ALT_OBJ=
ALT_TARGET=
ALT_OBJ= ${PROG}.alt.o
ALT_TARGET= ${PROG}.alt ttt.alt
#################
@@ -190,6 +190,60 @@ ttt_game.c: westley.c
${CP} -f westley.c 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: ${DATA}
@@ -256,8 +310,10 @@ clean:
clobber: clean
${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.alt_game ttt.alt_game.c ttt.alt_old.c ttt_.altnew.c nextmove.alt
@-if [ -e sandwich ]; then \
${RM} -f sandwich; \
echo 'ate sandwich'; \
+77 -54
View File
@@ -4,6 +4,8 @@
make all
```
There is an [alternate version](#alternate-code) that is supposed to win all the
time.
### Bugs and (Mis)features:
@@ -18,7 +20,11 @@ For more detailed information see [1991 westley in bugs.md](/bugs.md#1991-westle
## 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
@@ -34,7 +40,7 @@ a move on a tic-tac-toe board:
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:
```sh
@@ -45,7 +51,8 @@ make nextmove
### 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
make ttt
@@ -54,12 +61,12 @@ make ttt
```
The shell script determines when the game is over, and automatically\
replaces `merlyn.c` (copied from [westley.c](westley.c) with an improved version after a loss. The [ttt](ttt.sh) script\
has two other modes:
The shell script determines when the game is over, and automatically replaces
`merlyn.c` (copied from [westley.c](westley.c) with an improved version after a
loss). The [ttt](ttt.sh) script has two other modes:
```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
```
@@ -109,6 +116,24 @@ Redirects stderr to `/dev/null` by default. If you need errors or warnings use
`-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:
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 :-) )!
Why is win better than draw? Because everyone knows how to draw:
> Professor Falken: Did you ever play tic-tac-toe?
> Jennifer Mack: Yeah. Of course.
> Falken: But you don't any more.
> Jennifer: No.
> Falken: Why?
> Jennifer: Because it's a boring game. It's always a tie.
> Professor Falken: Did you ever play tic-tac-toe?\
> Jennifer Mack: Yeah. Of course.\
> Falken: But you don't any more.\
> Jennifer: No.\
> Falken: Why?\
> 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
at the [International Obfuscated C Code Contest judges'
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
the same compile line) and repeat until the game is finished.
If the computer wins, the "straight face" in the upper right-hand
corner ":-|" will change into a happy face ":-)". If it is a draw,
the face does not change. If the computer loses, the board is
blanked and the face changes into a sad face ":-(". If this
happens, the blank board should replace the original program; the
computer changes its play and will continue to do so until it no
If the computer wins, the "straight face" in the upper right-hand corner, ":-|",
will change into a happy face, ":-)". If it is a draw, the face does not
change. If the computer loses, the board is blanked and the face changes into a
sad face, ":-(". If this happens, the blank board should replace the original
program; the computer changes its play and will continue to do so until it no
longer loses games.
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
The vertical "bars" of the grid are identical functions, with the
name of the function as a(), b(), or c(). After each function is
called, the global variables X and O contain bitmasks of where all
the Xs and Os are placed. The statement:
The vertical "bars" of the grid are identical functions, with the name of the
function as `a()`, `b()`, or `c()`. After each function is called, the global
variables `X` and `O` contain bitmasks of where all the `X`s and `O`s are placed.
The statement:
```c
X=- <char> -1;
``
```
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
(global var) X and O and resets the value of &X to 1. This is how
the current position is computed.
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 (global
vars) `X` and `O` and resets the value of `&X` to `1`. This is how the current
position is computed.
The player's move is OR'd into the X mask, and (if it is the
opening move) the strategy string's first character is set to the
player's first move (or "5" if the computer moves first).
The player's move is bitwise ORed into the `X` mask, and (if it is the opening
move) the strategy string's first character is set to the player's first move
(or `5` if the computer moves first).
The evalution function y(M) checks if the mask has a winning
pattern (any of the first eight bitmasks of the w[N array), and
sets the variable q to 0 if it does.
The evaluation function `y(M)` checks if the mask has a winning pattern (any of
the first eight bitmasks of the `w[N]` array), and sets the variable `q` to `0`
if it does.
The player's move is evaluated to see if the human has won. The
smiley is set to :-) or :-( depending on this. Also, if the human
has won, the digit in the strategy string is decremented to change
the computer's play for next game.
The player's move is evaluated to see if the human has won. The smiley is set
to ":-)" or ":-(" depending on this. Also, if the human has won, the digit in
the strategy string is decremented to change the computer's play for the next
round.
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
e(X,O) checks if O can make a legal winning move. If so, the
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
`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
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
generated by looking down the list of "move templates". The first
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
possible moves by making the e() function return "winning" moves,
possible moves by making the `e()` function return "winning" moves,
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)
```
...will make the e() function return a center move, as
this is the only move that produces three in a row. Notice that
the computer does not consider which cells contain Xs and Os when
making a template move, only which cells are empty and not-empty.
This makes for unusual play.
...will make the `e()` function return a center move, as this is the only move
that produces three in a row. Notice that the computer does not consider which
cells contain `X`s and `O`s when making a template move, only which cells are
empty and not-empty. This makes for unusual play.
The templates are carefully chosen to eventually block all traps
that X can try, by just blundering in the way.
The templates are carefully chosen to eventually block all traps that `X` can
try, by just blundering in the way.
Also note that the winning & strategy templates are part of the
compile line, making it possible to change the rules of the game by
@@ -272,10 +295,10 @@ 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
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:
+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.
## [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]))
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
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]))