Fixes in 1991/westley + alt code

The alt code is based on the author's remarks where the computer will
always win (supposedly :-) :-) ). It involves the new script ttt.alt.sh
which is copied to ttt.alt with make. This script has changes from
ttt.sh so that it doesn't stomp over the files that ttt.sh makes.

The Makefile clobber rule had a typo fix as well which prevented a file
from being removed (a file specified twice instead).

The README.md file has been typo and format fixed.

The .gitignore file has been updated.
This commit is contained in:
Cody Boone Ferguson committed 2023-11-06 09:25:12 -08:00
1 parent e3322d8d89
commit f9169eca06
6 files changed
+368 -51

No files matched your search

+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'; \
+71 -48
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
@@ -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;}/*/
+6
View File
@@ -1282,6 +1282,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]))