walshm@clutx.clarkson.edu (Matthew S. Walsh) (11/18/90)
Does anyone have a public domain program written in Pascal which plays TIC TAC TOE - allowing the player to play the computer? I don't need or want anything with graphics, just plain text characters for the board. Ideally I'd like something to run on UNIX. Thanks matt walsh Matthew Walsh::217-398-8593:205 E. Clark suite 204:Champaign Il 61820 walshm@clutx.clarkson.edu::Dynamic Entertaining Mechanical Systems, inc And if they catch you in the back seat trying to pick her locks, They're gonna send you back to mother in a cardboard box!
davidr@hplsla.HP.COM (David M. Reed) (11/22/90)
I copied this out of Turbo TECHNIX (March/April 1988). I really liked its presentation of abstract data types and their implementation. (And my kids like the game.) I do not know if this is what you want, but it should be a good start. ******************************************************** Unit TicTac; { TICTAC.PAS unit to implement abstract data types MOVE, LOCATION, GAME author bruce f. webster ( from Turbo TECHNIX, March/April 1988 ) last update 12 dec 87 } Interface { definitions for abstract data type Move } TYPE Move = (BLANK, X, O); FUNCTION Opposite (M : Move) : Move; { definitions for abstract data type Location } CONST GLim = 9; TYPE Location = 1..GLim; { definitions for abstract data type Game } TYPE Board = ARRAY [Location] OF Move; Game = RECORD Grid : Board; Next, Win : Move; Moves : INTEGER; END; FUNCTION GetLoc (VAR g : Game; l : Location) : Move; FUNCTION NextMove (g : Game) : Move; FUNCTION MovesMade (g : Game) : INTEGER; FUNCTION GameOver (VAR g : Game) : BOOLEAN; FUNCTION Winner (g : Game) : Move; PROCEDURE DoMove (VAR g : Game; l : Location); PROCEDURE NewGame (VAR g : Game; First : Move); Implementation {****************************************************************************} FUNCTION Opposite (M : Move) : Move; { return opposite of value passed } BEGIN CASE M OF BLANK : Opposite := BLANK; X : Opposite := O; O : Opposite := X; END; { CASE M } END; { of PROC Opposite } {****************************************************************************} PROCEDURE SetLoc (VAR g : Game; l : Location; m : Move); { sets a location in the game to a given value } BEGIN g.Grid [l] := m; END; { of PROC SetLoc } {****************************************************************************} FUNCTION GetLoc (VAR g : Game; l : Location) : Move; { returns value of a given location in the game } BEGIN GetLoc := g.Grid [l]; END; { of PROC GetLoc } {****************************************************************************} FUNCTION NextMove (g : Game) : Move; { returns next move } BEGIN NextMove := g.Next; END; { of FUNC NextMove } {****************************************************************************} FUNCTION MovesMade (g : Game) : INTEGER; { returns number of moves made in game so far } BEGIN MovesMade := g.Moves; END; { of FUNC MovesMade } {****************************************************************************} PROCEDURE InARow (VAR g : Game; i, j, k : Location); { checks for three X's or O's in a row } BEGIN WITH g DO BEGIN IF (Win = BLANK) THEN BEGIN IF ( (Grid [i] = Grid [j] ) AND (Grid [j] = Grid [k] ) AND (Grid [i] <> BLANK) ) THEN Win := Grid [i]; END; { IF Win } END; { WITH g } END; { of PROC InARow } {****************************************************************************} PROCEDURE CheckForWin (VAR g : Game; l : Location); { see if last move won the game } BEGIN CASE l OF 1 : BEGIN InARow (g, 1, 2, 3); InARow (g, 1, 5, 9); InARow (g, 1, 4, 7); END; 2 : BEGIN InARow (g, 1, 2, 3); InARow (g, 2, 5, 8); END; 3 : BEGIN InARow (g, 1, 2, 3); InARow (g, 3, 5, 7); InARow (g, 3, 6, 9); END; 4 : BEGIN InARow (g, 1, 4, 7); InARow (g, 4, 5, 6); END; 5 : BEGIN InARow (g, 1, 5, 9); InARow (g, 2, 5, 8); InARow (g, 3, 5, 7); InARow (g, 4, 5, 6); END; 6 : BEGIN InARow (g, 3, 6, 9); InARow (g, 4, 5, 6); END; 7 : BEGIN InARow (g, 1, 4, 7); InARow (g, 3, 5, 7); InARow (g, 7, 8, 9); END; 8 : BEGIN InARow (g, 2, 5, 8); InARow (g, 7, 8, 9); END; 9 : BEGIN InARow (g, 1, 5, 9); InARow (g, 3, 6, 9); InARow (g, 7, 8, 9); END; END; { CASE } END; { of PROC CheckForWin } {****************************************************************************} FUNCTION GameOver (VAR g : Game) : BOOLEAN; { returns status of game (over or not ) } BEGIN GameOver := ( (g.Win <> BLANK) OR (g.Moves = GLim) ); END; { of FUNC GameOver } {****************************************************************************} FUNCTION Winner (g : Game) : Move; { returns winner of game } BEGIN Winner := g.Win; END; { of FUNC Winner } {****************************************************************************} PROCEDURE DoMove (VAR g : Game; l : Location); { make next move in game } BEGIN WITH g DO BEGIN SetLoc ( g, l, g.Next); Moves := Moves + 1; CheckForWin (g, l); IF NOT (GameOver (g) ) THEN Next := Opposite (Next) ELSE Next := BLANK; END; { WITH g } END; { if PROC DoMove } {****************************************************************************} PROCEDURE NewGame (VAR g : Game; First : Move); { initialize a new game } VAR loop : INTEGER; BEGIN WITH g DO BEGIN FOR loop := 1 TO GLim DO SetLoc (g, loop, BLANK); Next := First; Win := BLANK; Moves := 0; END; { WITH g } END; { of PROC NewGame } END. { of Unit TicTac } ***************************************************************************** Unit TicTacMv; { TICTACMV.PAS unit for making computer's moves for tictactoe author bruce f. webster ( from Turbo TECHNIX, March/April 1988 ) last update 12 dec 87 } Interface Uses TicTac; VAR CFlag : INTEGER; { 0 if computer moves first, 1 otherwise } CMove : Move; { contains computer's marker (X, O) } PROCEDURE GenerateMove (g : Game; VAR l : Location); Implementation {****************************************************************************} FUNCTION WinFound (g : Game; VAR l : Location) : BOOLEAN; { checks for winning move in game } VAR temp : Game; count : INTEGER; BEGIN count := 1; WinFound := FALSE; REPEAT IF (GetLoc (g, count) = BLANK) THEN BEGIN temp := g; DoMove (temp, count); IF (GameOver (temp) AND (Winner (temp) <> BLANK) ) THEN BEGIN l := 1; WinFound := TRUE; Exit; END; { IF GameOver } END; { IF GetLoc } count := SUCC (count); UNTIL (count > Glim); END; { of FUNC WinFound } {****************************************************************************} FUNCTION BlockFound (g : Game; VAR l : Location) : BOOLEAN; { checks for blocking move in game } VAR temp : Game; count : INTEGER; loc : Location; BEGIN count := 1; BlockFound := FALSE; REPEAT IF (GetLoc (g, count) = BLANK) THEN BEGIN temp := g; DoMove (temp, count); IF NOT (WinFound (temp, loc) ) THEN BEGIN l := count; BlockFound := TRUE; Exit; END; { IF NOT WinFound } END; { IF GetLoc } count := SUCC (count); UNTIL (count > GLim); END; { of FUNC BlockFound } {****************************************************************************} PROCEDURE GenerateMove (g : Game; VAR l : Location); { generates next move for computer } VAR NMoves : INTEGER; BEGIN l := 5; NMoves := MovesMade (g); IF (NMoves <= 2) THEN BEGIN IF (GetLoc (g, l) = BLANK) THEN Exit; END; { IF NMoves } IF (WinFound (g, l) ) THEN Exit; IF (NMoves > 2) AND (BlockFound (g, l) ) THEN Exit; REPEAT IF (NMoves <= 4) THEN l := 1 + 2 * Random (5) ELSE l := 1 + Random (Glim); UNTIL (GetLoc (g, l) = BLANK); END; { of PROC GenerateMoves } END. { of Unit TicTacMv } ***************************************************************************** Unit TicTacIO; { TICTACIO.PAS unit to implement screen and keyboard routines for tictactoe author bruce f. webster ( from Turbo TECHNIX, March/April 1988 ) last update 12 dec 87 } Interface Uses CRT, TicTac; { define types for parameters } TYPE CharSet = SET OF Char; MsgStr = STRING [80]; PROCEDURE DisplayGame (theGame : Game); PROCEDURE DrawGrid; PROCEDURE ReadChar (VAR Ch : Char; Prompt : MsgStr; OKSet : CharSet); PROCEDURE ReadInt (VAR Val : INTEGER; Prompt : MsgStr; Low, High : INTEGER); Implementation CONST BoardX = 10; { positioning for tictactoe grid } BoardY = 10; Bar = #186; { special characters used for grid } Line = #205; Cross = #206; {**************************************************************************} PROCEDURE DrawGrid; { draws full-sized tictactoe grid, with smaller numbers one beside it } {==========================================================================} PROCEDURE DrawHorz (x, y : INTEGER); { draws horizontal bar for tictactoe grid } BEGIN GotoXY (x, y); WRITE (Line, Line, Line, Line, Line, Cross); WRITE (Line, Line, Line, Line, Line, Cross); WRITE (Line, Line, Line, Line, Line); END; { of locPROC DrawHorz } {==========================================================================} PROCEDURE DrawVert (x, y : INTEGER); { draws vertical bar for tictactoe grid } VAR loop, count : INTEGER; BEGIN FOR loop := 1 TO 3 DO BEGIN FOR count := 0 TO 2 DO BEGIN GotoXY (x, y + count); WRITE (Bar); END; { FOR count } y := y + 4; END; { FOR loop } END; { of locPROC DrawVert } {==========================================================================} PROCEDURE DrawMoves (x, y : INTEGER); { draws 3x3 grid with numbered positions } BEGIN GotoXY (x, y); WRITE ('1', Bar, '2', Bar, '3'); GotoXY (x, y + 1); WRITE (Line, Cross, Line, Cross, Line); GotoXY (x, y + 2); WRITE ('4', Bar, '5', Bar, '6'); GotoXY (x, y + 3); WRITE (Line, Cross, Line, Cross, Line); GotoXY (x, y + 4); WRITE ('7', Bar, '8', Bar, '9'); END; { of locPROC DrawMoves } {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * } BEGIN { of PROC DrawGrid } DrawHorz (BoardX, BoardY); DrawHorz (BoardX, BoardY + 4); DrawVert (BoardX + 5, BoardY - 3); DrawVert (BoardX + 11, BoardY - 3); DrawMoves (BoardX + 20, BoardY); END; { of PROC DrawGrid } {**************************************************************************} PROCEDURE DisplayGame (theGame : Game); { draws status of tictactoe game on screen } VAR count, col, row : INTEGER; m : Move; BEGIN FOR count := 1 TO GLim DO BEGIN m := GetLoc (theGame, count); col := BoardX + 2 + 6 * ( (count - 1) MOD 3); row := BoardY - 2 + 4 * ( (count - 1) DIV 3); GotoXY (col, row); CASE m OF BLANK : WRITE (' '); X : WRITE ('X'); O : WRITE ('O'); END; { CASE m } END; { FOR count } END; { of PROC DisplayGame } {**************************************************************************} PROCEDURE ReadChar (VAR Ch : Char; Prompt : MsgStr; OKSet : CharSet); { prompt for and get one character of a given set } BEGIN GotoXY ( 1, 1); ClrEol; WRITE (Prompt); REPEAT Ch := UpCase (ReadKey); UNTIL Ch IN OKSet; WRITE (Ch); END; { of PROC ReadChar } {**************************************************************************} PROCEDURE ReadInt (VAR Val : INTEGER; Prompt : MsgStr; Low, High : INTEGER); { prompt for and get an integer value in a given range } BEGIN {$I-} REPEAT GotoXY (1, 1); ClrEol; WRITE (Prompt, ' (', Low, ', ', High, '): '); READLN (Val); UNTIL (IOResult = 0) AND (Val >= Low) AND (Val <= High); {$I+} END; { of PROC ReadInt } END. { of Unit TicTacIO } ***************************************************************************** PROGRAM TicTacToe; { TTT.PAS implementation of tic-tac-toe on the computer author bruce f. webster ( from Turbo TECHNIX, March/April 1988 ) last update 12 dec 87 } Uses CRT, TicTac, TicTacMv, TicTacIO; VAR theGame : Game; Next : Location; Ch : CHAR; {***************************************************************************} PROCEDURE StartGame (VAR theGame : Game); { set up a new game } VAR answer : CHAR; BEGIN ClrScr; ReadChar (answer, 'Who moves first: H)uman or C)omputer ? ', ['H', 'C'] ); IF (answer = 'C') THEN CFlag := 0 ELSE CFlag := 1; ReadChar (answer, 'Do you wish to be X or O ? ', ['X', 'O'] ); IF (answer = 'X') THEN CMove := O ELSE CMove := X; IF (CFlag <> 0) THEN NewGame (theGame, Opposite (CMove) ) ELSE NewGame (theGame, CMove); DrawGrid; DisplayGame (theGame); END; { of PROC StartGame } {***************************************************************************} PROCEDURE GetMove (theGame : Game; VAR l : Location); { select the next move for the game } CONST bell = #7; VAR loc : INTEGER; BEGIN IF (MovesMade (theGame) MOD 2) = CFlag THEN GenerateMove (theGame, l) ELSE BEGIN REPEAT ReadInt (loc, 'Enter move: ', 1, GLim); IF (GetLoc (theGame, loc) <> BLANK) THEN WRITE (bell); UNTIL (GetLoc (theGame, loc) = BLANK); l := loc; END; { ELSE } END; { of PROC GetMove } {***************************************************************************} PROCEDURE ShowResults (VAR theGame : Game); { show results of tictactoe game } VAR m : Move; BEGIN m := Winner (theGame); GotoXY (1, 1); ClrEol; CASE m OF BLANK : WRITE ('The game was a draw'); X : WRITE ('The winner is X'); O : WRITE ('The winner is O'); END; { CASE m } WRITE (' -- press any key to continue (Q to quit) '); END; { of PROC ShowResults } { * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * } BEGIN { of PROG TicTacToe } REPEAT StartGame (theGame); REPEAT GetMove (theGame, Next); DoMove (theGame, Next); DisplayGame (theGame); UNTIL (GameOver (theGame) ); ShowResults (theGame); Ch := UpCase (ReadKey); UNTIL (Ch = 'Q'); END. { of PROG TicTacToe }
gideon@cs.utexas.edu (Timothy Tin-Wai Chan) (11/23/90)
I have spotted a bug (or was it a typo?) in the Tic Tac Toe program. In the unit "TicTacMv", in the procedure BlockFound, the line l := 1; should be changed to: l := count; -- Tim