@ECHO OFF SETLOCAL UNALIAS * TEXT > NUL TICTACTO.WPM Plays Tic-Tac-Toe Original program by Tom Koos modified by Steve North, Donald Spencer, Tim Hartnell Ported to 4DOS & all comments by Michael Bednarek, Brisbane, Australia http://mbednarek.com/ Previously ported to VAX DCL, and WP-DOS(V6) macro. The traditional way of numbering a Tic-Tac-Toe board is in rows/columns: 1 2 3 4 5 6 7 8 9 This port arranges the display in line with the conventional PC number pad: 7 8 9 4 5 6 1 2 3 Howerver, for programmatical purposes, a more suitable system is circular, with 9 as zero in the centre: 1 2 3 8 0 4 7 6 5 Advantanges of the circular numbering system: First, neighbouring fields have neighbouring numbers (modulo 8). Second, corners are odd (uneven) numbers. Third, opposite fields are 4 apart. This program will at minumum achieve a draw. If the human player does not occupy a corner in the first move, the program will win. Unfortunately, the font metrics for a console font under any version of Windows are usually rather different to those under pure DOS, so the screen display is never quite right. The design used here works for me using an 8x12 bit mapped font in 4DOS under Win4; TC32 however seems to have a major problem with DRAWBOX and related commands. ENDTEXT :: myMod[n,m] returns m where n%%m returns 0 FUNCTION myMOD=`%@EVAL[((%1-1)%%%2+1)]` :: Translate between external and internal cell numbering FUNCTION x2i=`%@WORD[",",%1, ,7,6,5,8,0,4,1,2,3]` FUNCTION i2x=`%@WORD[",",%1, ,7,8,9,6,3,2,1,4]` :: Returns row/col for a given (external) cell number FUNCTION ticRow=`%@EVAL[18-%@INT[%@EVAL[(%1+2)/3)]]*4]` FUNCTION ticCol=`%@EVAL[%@myMOD[%1,3]*6+4]` :: Remember old cursor shape, disable cursor, clear screen SET oldCursor=%_CO:%_CI SETDOS /S0:0 ON BREAK GOTO theEnd CLS White ON Blue DRAWBOX 2 1 2 33 0 Yellow ON Yellow %+REM Draw "Menu" SCRPUT 2 2 White ON Black Exit SCRPUT 2 7 White ON Black About SCRPUT 2 3 Bright Red ON Black x %+REM "Accelerator" keys SCRPUT 2 7 Bright Red ON Black A :: Draw a box from (x,y) (8,4) to (26,16) DRAWBOX 4 8 16 26 2 BRIGHT Yellow ON Cyan FILL Cyan Shadow DRAWHLINE 8 8 18 1 BRIGHT Yellow ON Cyan DRAWHLINE 12 8 18 1 BRIGHT Yellow ON Cyan DRAWVLINE 4 14 13 1 BRIGHT Yellow ON Cyan DRAWVLINE 4 20 13 1 BRIGHT Yellow ON Cyan :: Put numbers on the board DO myMove = 1 TO 9 SCRPUT %@ticRow[%myMove] %@ticCol[%myMove] Red ON White ` `%[myMove]` ` ENDDO SET nMoves=0 SET myMove=5 SET Result=I start. SET validKeys=xa123456789 SET inAbout=N DO WHILE 2 = 2 IFF %inAbout EQ N THEN REM Make the PGM's move SET validKeys=%@REPLACE[%myMove,,%validkeys] %+REM Remove this move from the list of valid keys SCRPUT %@ticRow[%myMove] %@ticCol[%myMove] Bright White On Bright Black ME%` ` DRAWBOX 19 1 21 33 2 Black ON White FILL White %+REM Display state of play SCRPUT 20 %@INT[%@EVAL[(31-%@LEN[%Result])/2+2]] Black ON White %Result IF "%Result" EQ "I win!" .OR. "%Result" EQ "It's a draw." LEAVE ELSE SET inAbout=N ENDIFF REM Ask for the user's move INKEY /X /K"%validKeys" %%yourMove > NUL IF %yourMove EQ X LEAVE IFF %yourMove EQ A THEN DRAWBOX 4 35 16 72 2 BRIGHT Red ON Black FILL Black SCRPUT 5 37 Bright White ON Black About Tic-Tac-Toe SCRPUT 7 37 White ON Black Original program by Tom Koos SCRPUT 8 37 White ON Black modified by Steve North, SCRPUT 9 37 White ON Black Donald Spencer, Tim Hartnell SCRPUT 11 37 White ON Black Ported to 4DOS & all comments by SCRPUT 12 37 White ON Black by Michael Bednarek (2003) SCRPUT 13 37 White ON Black %= SCRPUT 14 37 White ON Black http://mbednarek.com/ INKEY /X %%yourMove > NUL DRAWBOX 4 35 16 72 0 Blue ON Blue FILL BLUE SET inAbout=Y ITERATE ENDIFF SET validKeys=%@REPLACE[%yourMove,,%validkeys] %+REM Remove this move from the list of valid keys SCRPUT %@ticRow[%yourMove] %@ticCol[%yourMove] Bright Green On Bright Black You SET nMoves=%@INC[%nMoves] %+REM Count moves (it's behind by one) SET tMove%nMoves=%@x2i[%yourMove] %+REM Translate the move (external to internal) and remember it SWITCH %nMoves CASE 1 SET myMove=%@MyMod[%@EVAL[%tMove1+1],8] %+REM The PGM's 2nd move is one to the right of the human's IFF %@EVAL[%tMove1 AND 1] EQ 1 THEN %+REM Did Human occupy a corner? SET Result=Doing well! ELSE SET Result=I'm winning! ENDIFF CASE 3 REM The PGM now tries to occupy the spot opposite the PGM's 2nd move IFF %tMove3 NE %@myMod[%@EVAL[%tMove2+4],8] THEN %+REM Did Human block this move? SET myMove=%@myMod[%@EVAL[%tMove2+4],8] SET Result=I win! ELSE %+REM Alternatively, the PGM 3rd move is two to the right of PGM's 2nd move SET myMove=%@myMod[%@EVAL[%tMove2+2],8] IFF "%Result" EQ "I'm winning!" THEN SET Result=I will win! ELSE SET Result=Very well. ENDIFF ENDIFF CASE 5 IFF %tMove5 NE %@myMod[%@EVAL[%tMove4+4],8] THEN %+REM Try to occupy the spot opposite the PGM's 3rd move SET myMove=%@myMod[%@EVAL[%tMove4+4],8] SET Result=I win! ELSE IFF %@EVAL[%tMove1 AND 1] EQ 1 THEN %+REM Did Human occupy a corner? SET myMove=%@myMod[%@EVAl[%tMove4+3],8] %+REM "Knight Move" relative to PGM's 3rd move SET Result=1 more chance for you... ELSE SET myMove=%@myMod[%@EVAL[%tMove4+7],8] %+REM Corner left of Human's 1st move SET Result=I win! ENDIFF ENDIFF CASE 7 IFF %tMove7 NE %@myMod[%@EVAL[%tMove6+4],8] THEN %+REM Try to occupy the spot opposite the PGM's 4th move SET myMove=%@myMod[%@EVAL[%tMove6+4],8] Set Result=I win! ELSE %+REM The PGM's 5th move is two to the left of the PGM's 4th move SET myMove=%@myMod[%@EVAL[%tMove6+6],8] SET Result=It's a draw. ENDIFF ENDSWITCH SET nMoves=%@INC[%nMoves] %+REM Count moves (it's behind by one) SET tMove%nMoves=%myMove %+REM Remember this move and SET myMove=%@i2x[%myMove] %+REM translate the move (internal to external) ENDDO :theEnd SETDOS /S%oldCursor