diff --git a/1991/westley/.gitignore b/1991/westley/.gitignore index 71f95e6dc..ca193c527 100644 --- a/1991/westley/.gitignore +++ b/1991/westley/.gitignore @@ -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 diff --git a/1991/westley/Makefile b/1991/westley/Makefile index ecdd9f865..abcf45332 100644 --- a/1991/westley/Makefile +++ b/1991/westley/Makefile @@ -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'; \ diff --git a/1991/westley/README.md b/1991/westley/README.md index da639bffa..106af0511 100644 --- a/1991/westley/README.md +++ b/1991/westley/README.md @@ -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 @@ -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=- -1; -`` +``` -evaluates to 1, -1, or -2 when X is 1 and 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 `` 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: diff --git a/1991/westley/ttt.alt.sh b/1991/westley/ttt.alt.sh new file mode 100755 index 000000000..92146f8d1 --- /dev/null +++ b/1991/westley/ttt.alt.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# +# play tic-tac-toe + +# parse args +# + +export USAGE="usage: $0 [-h] [-e] + -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 + diff --git a/1991/westley/westley.alt.c b/1991/westley/westley.alt.c new file mode 100644 index 000000000..279041ca3 --- /dev/null +++ b/1991/westley/westley.alt.c @@ -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<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<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;}/*/ diff --git a/thanks-for-fixes.md b/thanks-for-fixes.md index 17e78bfd0..f8191f571 100644 --- a/thanks-for-fixes.md +++ b/thanks-for-fixes.md @@ -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 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]))