Tic-tac-toe
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Play a game of tic-tac-toe.
Ensure that legal moves are played and that a winning position is notified.
Tic-tac-toe is also known as:
- noughts and crosses
- tic tac toe
- tick tack toe
- three in a row
- tres en rayo and
- Xs and Os
- See also
11l
UInt32 seed = 0
F nonrandom_choice(lst)
:seed = 1664525 * :seed + 1013904223
R lst[:seed % UInt32(lst.len)]
V board = Array(‘123456789’)
V wins = ([0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
F printboard()
print([0, 3, 6].map(x -> (:board[x .+ 3]).join(‘ ’)).join("\n"))
F score(board = board) -> (Char, [Int])?
L(w) :wins
V b = board[w[0]]
I b C ‘XO’ & all(w.map(i -> @board[i] == @b))
R (b, w.map(i -> i + 1))
R N
F finished()
R all(:board.map(b -> b C ‘XO’))
F space(board = board)
R board.filter(b -> b !C ‘XO’)
F my_turn(xo, &board)
V options = space()
V choice = nonrandom_choice(options)
board[Int(choice) - 1] = xo
R choice
F my_better_turn(xo, &board)
‘Will return a next winning move or block your winning move if possible’
V ox = I xo == ‘X’ {Char(‘O’)} E Char(‘X’)
Int? oneblock
V options = space(board).map(s -> Int(s) - 1)
Int choice
L(chc) options
V brd = copy(board)
brd[chc] = xo
I score(brd) != N
choice = chc
L.break
I oneblock == N
brd[chc] = ox
I score(brd) != N
oneblock = chc
L.was_no_break
choice = oneblock ? nonrandom_choice(options)
board[choice] = xo
R choice + 1
F your_turn(xo, &board)
V options = space()
L
V choice = input("\nPut your #. in any of these positions: #. ".format(xo, options.join(‘’))).trim((‘ ’, "\t", "\r", "\n"))
I choice C options
board[Int(choice) - 1] = xo
R choice
print(‘Whoops I don't understand the input’)
F me(xo = Char(‘X’))
printboard()
print("\nI go at "my_better_turn(xo, &:board))
R score()
F you(xo = Char(‘O’))
printboard()
print("\nYou went at "my_turn(xo, &:board))
R score()
L !finished()
(Char, [Int])? s = me(Char(‘X’))
I s != N
printboard()
print("\n#. wins along #.".format(s[0], s[1]))
L.break
I !finished()
s = you(Char(‘O’))
I s != N
printboard()
print("\n#. wins along #.".format(s[0], s[1]))
L.break
L.was_no_break
print("\nA draw")
- Output:
1 2 3 4 5 6 7 8 9 I go at 8 1 2 3 4 5 6 7 X 9 You went at 3 1 2 O 4 5 6 7 X 9 I go at 1 X 2 O 4 5 6 7 X 9 You went at 5 X 2 O 4 O 6 7 X 9 I go at 7 X 2 O 4 O 6 X X 9 You went at 9 X 2 O 4 O 6 X X O I go at 4 X 2 O X O 6 X X O X wins along [1, 4, 7]
Ada
with Ada.Text_IO, Ada.Numerics.Discrete_Random;
-- can play human-human, human-computer, computer-human or computer-computer
-- the computer isn't very clever: it just chooses a legal random move
procedure Tic_Tac_Toe is
type The_Range is range 1 .. 3;
type Board_Type is array (The_Range, The_Range) of Character;
package Rand is new Ada.Numerics.Discrete_Random(The_Range);
Gen: Rand.Generator; -- required for the random moves
procedure Show_Board(Board: Board_Type) is
use Ada.Text_IO;
begin
for Row in The_Range loop
for Column in The_Range loop
Put(Board(Row, Column));
end loop;
Put_Line("");
end loop;
Put_Line("");
end Show_Board;
function Find_Winner(Board: Board_Type) return Character is
-- if 'x' or 'o' wins, it returns that, else it returns ' '
function Three_Equal(A,B,C: Character) return Boolean is
begin
return (A=B) and (A=C);
end Three_Equal;
begin -- Find_Winner
for I in The_Range loop
if Three_Equal(Board(I,1), Board(I,2), Board(I,3)) then
return Board(I,1);
elsif Three_Equal(Board(1,I), Board(2,I), Board(3,I)) then
return Board(1,I);
end if;
end loop;
if Three_Equal(Board(1,1), Board(2,2), Board (3,3)) or
Three_Equal(Board(3,1), Board(2,2), Board (1,3)) then
return Board(2,2);
end if;
return ' ';
end Find_Winner;
procedure Do_Move(Board: in out Board_Type;
New_Char: Character; Computer_Move: Boolean) is
Done: Boolean := False;
C: Character;
use Ada.Text_IO;
procedure Do_C_Move(Board: in out Board_Type; New_Char: Character) is
Found: Boolean := False;
X,Y: The_Range;
begin
while not Found loop
X := Rand.Random(Gen);
Y := Rand.Random(Gen);
if (Board(X,Y) /= 'x') and (Board(X,Y) /= 'o') then
Found := True;
Board(X,Y) := New_Char;
end if;
end loop;
end Do_C_Move;
begin
if Computer_Move then
Do_C_Move(Board, New_Char);
else -- read move;
Put_Line("Choose your move, " & New_Char);
while not Done loop
Get(C);
for Row in The_Range loop
for Col in The_Range loop
if Board(Row, Col) = C then
Board(Row, Col) := New_Char;
Done := True;
end if;
end loop;
end loop;
end loop;
end if;
end Do_Move;
The_Board : Board_Type := (('1','2','3'), ('4','5','6'), ('7','8','9'));
Cnt_Moves: Natural := 0;
Players: array(0 .. 1) of Character := ('x', 'o'); -- 'x' begins
C_Player: array(0 .. 1) of Boolean := (False, False);
Reply: Character;
begin -- Tic_Tac_Toe
-- firstly, ask whether the computer shall take over either player
for I in Players'Range loop
Ada.Text_IO.Put_Line("Shall " & Players(I) &
" be run by the computer? (y=yes)");
Ada.Text_IO.Get(Reply);
if Reply='y' or Reply='Y' then
C_Player(I) := True;
Ada.Text_IO.Put_Line("Yes!");
else
Ada.Text_IO.Put_Line("No!");
end if;
end loop;
Rand.Reset(Gen); -- to initalize the random generator
-- now run the game
while (Find_Winner(The_Board) = ' ') and (Cnt_Moves < 9) loop
Show_Board(The_Board);
Do_Move(The_Board, Players(Cnt_Moves mod 2), C_Player(Cnt_Moves mod 2));
Cnt_Moves := Cnt_Moves + 1;
end loop;
Ada.Text_IO.Put_Line("This is the end!");
-- finally, output the outcome
Show_Board (The_Board);
if Find_Winner(The_Board) = ' ' then
Ada.Text_IO.Put_Line("Draw");
else
Ada.Text_IO.Put_Line("The winner is: " & Find_Winner(The_Board));
end if;
end Tic_Tac_Toe;
- Output:
> ./tic_tac_toe Shall x be run by the computer? (y=yes) y Yes! Shall o be run by the computer? (y=yes) n No! 123 456 789 1x3 456 789 Choose your move, o 5 1x3 4o6 789 1x3 xo6 789 Choose your move, o 6 1x3 xoo 789 1xx xoo 789 Choose your move, o 1 oxx xoo 789 oxx xoo 78x Choose your move, o 7 oxx xoo o8x This is the end! oxx xoo oxx Draw > ./tic_tac_toe Shall x be run by the computer? (y=yes) n No! Shall o be run by the computer? (y=yes) y Yes! 123 456 789 Choose your move, x 6 123 45x 789 123 45x o89 Choose your move, x 4 123 x5x o89 123 x5x o8o Choose your move, x 8 123 x5x oxo o23 x5x oxo Choose your move, x 5 This is the end! o23 xxx oxo The winner is: x
ALGOL W
The user can play O, X, both or neither. O goes first whether user or computer controlled.
begin
string(10) board;
% initialise the board %
procedure initBoard ; board := " 123456789";
% display the board %
procedure showBoard ;
begin
s_w := 0;
write( board(1//1), "|", board(2//1), "|", board(3//1) );
write( "-+-+-" );
write( board(4//1), "|", board(5//1), "|", board(6//1) );
write( "-+-+-" );
write( board(7//1), "|", board(8//1), "|", board(9//1) )
end showBoard ;
% returns true if board pos is free, false otherwise %
logical procedure freeSpace( integer value pos ) ;
( board(pos//1) >= "1" and board(pos//1) <= "9" );
% check for game over %
logical procedure gameOver ;
begin
logical noMoves;
noMoves := true;
for i := 1 until 9 do if noMoves then noMoves := not freeSpace( i );
noMoves
end gameOver ;
% makes the specified winning move or blocks it, if it will win %
logical procedure winOrBlock( integer value pos1, pos2, pos3
; string(1) value searchCharacter
; string(1) value playerCharacter
) ;
if board(pos1//1) = searchCharacter
and board(pos2//1) = searchCharacter
and freeSpace( pos3 )
then begin
board(pos3//1) := playerCharacter;
true
end
else if board(pos1//1) = searchCharacter
and freeSpace( pos2 )
and board(pos3//1) = searchCharacter
then begin
board(pos2//1) := playerCharacter;
true
end
else if freeSpace( pos1 )
and board(pos2//1) = searchCharacter
and board(pos3//1) = searchCharacter
then begin
board(pos1//1) := playerCharacter;
true
end
else begin
false
end winOrBlock ;
% makes a winning move or blocks a winning move, if there is one %
logical procedure makeOrBlockWinningMove( string(1) value searchCharacter
; string(1) value playerCharacter
) ;
( winOrBlock( 1, 2, 3, searchCharacter, playerCharacter )
or winOrBlock( 4, 5, 6, searchCharacter, playerCharacter )
or winOrBlock( 7, 8, 9, searchCharacter, playerCharacter )
or winOrBlock( 1, 4, 7, searchCharacter, playerCharacter )
or winOrBlock( 2, 5, 8, searchCharacter, playerCharacter )
or winOrBlock( 3, 6, 9, searchCharacter, playerCharacter )
or winOrBlock( 1, 5, 9, searchCharacter, playerCharacter )
or winOrBlock( 3, 5, 7, searchCharacter, playerCharacter )
) ;
% makes a move when there isn't an obvious winning/blocking move %
procedure move ( string(1) value playerCharacter ) ;
begin
logical moved;
moved := false;
% try for the centre, a corner or the midle of a line %
for pos := 5, 1, 3, 7, 9, 2, 4, 6, 8 do begin
if not moved and freeSpace( pos ) then begin
moved := true;
board(pos//1) := playerCharacter
end
end
end move ;
% gets a move from the user %
procedure userMove( string(1) value playerCharacter ) ;
begin
integer move;
while
begin
write( "Please enter the move for ", playerCharacter, " " );
read( move );
( move < 1 or move > 9 or not freeSpace( move ) )
end
do begin
write( "Invalid move" )
end;
board(move//1) := playerCharacter
end userMove ;
% returns true if the three board positions have the player character, %
% false otherwise %
logical procedure same( integer value pos1, pos2, pos3
; string(1) value playerCharacter
) ;
( board(pos1//1) = playerCharacter
and board(pos2//1) = playerCharacter
and board(pos3//1) = playerCharacter
);
% returns true if the player has made a winning move, false otherwise %
logical procedure playerHasWon( string(1) value playerCharacter ) ;
( same( 1, 2, 3, playerCharacter )
or same( 4, 5, 6, playerCharacter )
or same( 7, 8, 9, playerCharacter )
or same( 1, 4, 7, playerCharacter )
or same( 2, 5, 8, playerCharacter )
or same( 3, 6, 9, playerCharacter )
or same( 1, 5, 9, playerCharacter )
or same( 3, 5, 7, playerCharacter )
) ;
% takes a players turn - either automated or user input %
procedure turn ( string(1) value playerCharacter, otherCharacter
; logical value playerIsUser
) ;
begin
if playerIsUser then userMove( playerCharacter )
else begin
write( playerCharacter, " moves..." );
if not makeOrBlockWinningMove( playerCharacter, playerCharacter )
and not makeOrBlockWinningMove( otherCharacter, playerCharacter )
then move( playerCharacter )
end;
showBoard
end turn ;
% asks a question and returns true if the user inputs y/Y, %
% false otherwise %
logical procedure yes( string(32) value question ) ;
begin
string(1) answer;
write( question );
read( answer );
answer = "y" or answer = "Y"
end yes ;
% play the game %
while
begin
string(1) again;
string(32) gameResult;
logical oIsUser, xIsUser;
oIsUser := yes( "Do you want to play O? " );
xIsUser := yes( "Do you want to play X? " );
gameResult := "it's a draw";
initBoard;
showBoard;
while not gameOver and not playerHasWon( "O" ) and not playerHasWon( "X" ) do begin
turn( "O", "X", oIsUser );
if playerHasWon( "O" ) then gameResult := "O wins"
else if not gameOver then begin
turn( "X", "O", xIsUser );
if playerHasWon( "X" ) then gameResult := "X wins"
end
end ;
write( gameResult );
yes( "Play again? " )
end
do begin end
end.
- Output:
Do you want to play O? y Do you want to play X? n 1|2|3 -+-+- 4|5|6 -+-+- 7|8|9 Please enter the move for O 5 1|2|3 -+-+- 4|O|6 -+-+- 7|8|9 X moves... X|2|3 -+-+- 4|O|6 -+-+- 7|8|9 ...etc... Please enter the move for O 8 X|2|O -+-+- O|O|X -+-+- X|O|9 X moves... X|X|O -+-+- O|O|X -+-+- X|O|9 Please enter the move for O 9 X|X|O -+-+- O|O|X -+-+- X|O|O it's a draw Play again? n
AppleScript
property OMask : missing value
property XMask : missing value
property winningNumbers : {7, 56, 73, 84, 146, 273, 292, 448}
property difficulty : missing value
repeat
set OMask to 0
set XMask to 0
if button returned of (display dialog "Who should start?" buttons {"I shoud", "CPU"}) = "CPU" then set OMask to npcGet()
set difficulty to button returned of (display dialog "Please choose your difficulty" buttons {"Hard", "Normal"})
repeat
set XMask to XMask + 2 ^ (nGet() - 1)
if winnerForMask(XMask) or OMask + XMask = 511 then exit repeat
set OMask to npcGet()
if winnerForMask(OMask) or OMask + XMask = 511 then exit repeat
end repeat
if winnerForMask(OMask) then
set msg to "CPU Wins!"
else if winnerForMask(XMask) then
set msg to "You WON!!!"
else
set msg to "It's a draw"
end if
display dialog msg & return & return & drawGrid() & return & return & "Do you want to play again?"
end repeat
on nGet()
set theMessage to "It's your turn Player 1, please fill in the number for X" & return & return & drawGrid()
repeat
set value to text returned of (display dialog theMessage default answer "")
if (offset of value in "123456789") is not 0 then
if not positionIsUsed(value as integer) then exit repeat
end if
end repeat
return value as integer
end nGet
on npcGet()
--first get the free positions
set freeSpots to {}
repeat with s from 1 to 9
if not positionIsUsed(s) then set end of freeSpots to 2 ^ (s - 1)
end repeat
--second check if 1 move can make the CPU win
repeat with spot in freeSpots
if winnerForMask(OMask + spot) then return OMask + spot
end repeat
if difficulty is "Hard" and OMask is 0 then
if XMask = 1 or XMask = 4 then return 2
if XMask = 64 or XMask = 256 then return 128
end if
--third check if a user can make make it win (defensive) place it on position
repeat with spot in freeSpots
if winnerForMask(XMask + spot) then return OMask + spot
end repeat
--fourth check if CPU can win in two moves
repeat with spot1 in freeSpots
repeat with spot2 in freeSpots
if winnerForMask(OMask + spot1 + spot2) then return OMask + spot2
end repeat
end repeat
--fifth check if player can win in two moves
repeat with spot1 in freeSpots
repeat with spot2 in reverse of freeSpots
if winnerForMask(XMask + spot1 + spot2) then return OMask + spot1
end repeat
end repeat
--at last pick a random spot
if XMask + OMask = 0 and difficulty = "Hard" then return 1
return OMask + (some item of freeSpots)
end npcGet
on winnerForMask(mask)
repeat with winLine in winningNumbers
if BWAND(winLine, mask) = contents of winLine then return true
end repeat
return false
end winnerForMask
on drawGrid()
set grid to ""
repeat with o from 0 to 8
if BWAND(OMask, 2 ^ o) = 2 ^ o then
set grid to grid & "O"
else if BWAND(XMask, 2 ^ o) = 2 ^ o then
set grid to grid & "X"
else
set grid to grid & o + 1
end if
if o is in {2, 5} then set grid to grid & return
end repeat
return grid
end drawGrid
on positionIsUsed(pos)
return BWAND(OMask + XMask, 2 ^ (pos - 1)) = 2 ^ (pos - 1)
end positionIsUsed
on BWAND(n1, n2)
set theResult to 0
repeat with o from 0 to 8
if (n1 mod 2) = 1 and (n2 mod 2) = 1 then set theResult to theResult + 2 ^ o
set {n1, n2} to {n1 div 2, n2 div 2}
end repeat
return theResult as integer
end BWAND
AutoHotkey
This program uses a Gui with 9 buttons. Clicking on one will place an X there, disable the button, and cause the program to go somewhere. It plays logically, trying to win, trying to block, or playing randomly in that order.
Gui, Add, Button, x12 y12 w30 h30 vB1 gButtonHandler,
Gui, Add, Button, x52 y12 w30 h30 vB2 gButtonHandler,
Gui, Add, Button, x92 y12 w30 h30 vB3 gButtonHandler,
Gui, Add, Button, x12 y52 w30 h30 vB4 gButtonHandler,
Gui, Add, Button, x52 y52 w30 h30 vB5 gButtonHandler,
Gui, Add, Button, x92 y52 w30 h30 vB6 gButtonHandler,
Gui, Add, Button, x12 y92 w30 h30 vB7 gButtonHandler,
Gui, Add, Button, x52 y92 w30 h30 vB8 gButtonHandler,
Gui, Add, Button, x92 y92 w30 h30 vB9 gButtonHandler,
; Generated using SmartGUI Creator 4.0
Gui, Show, x127 y87 h150 w141, Tic-Tac-Toe
Winning_Moves := "123,456,789,147,258,369,159,357"
Return
ButtonHandler:
; Fired whenever the user clicks on an enabled button
Go(A_GuiControl,"X")
GoSub MyMove
Return
MyMove: ; Loops through winning moves. First attempts to win, then to block, then a random move
Went=0
Loop, parse, Winning_Moves,`,
{
Current_Set := A_LoopField
X:=O:=0
Loop, parse, Current_Set
{
GuiControlGet, Char,,Button%A_LoopField%
If ( Char = "O" )
O++
If ( Char = "X" )
X++
}
If ( O = 2 and X = 0 ) or ( X = 2 and O = 0 ){
Finish_Line(Current_Set)
Went = 1
Break ; out of the Winning_Moves Loop to ensure the computer goes only once
}
}
If (!Went)
GoSub RandomMove
Return
Go(Control,chr){
GuiControl,,%Control%, %chr%
GuiControl,Disable,%Control%
GoSub, CheckWin
}
CheckWin:
Loop, parse, Winning_Moves,`,
{
Current_Set := A_LoopField
X:=O:=0
Loop, parse, Current_Set
{
GuiControlGet, Char,,Button%A_LoopField%
If ( Char = "O" )
O++
If ( Char = "X" )
X++
}
If ( O = 3 ){
Msgbox O Wins!
GoSub DisableAll
Break
}
If ( X = 3 ){
MsgBox X Wins!
GoSub DisableAll
Break
}
}
return
DisableAll:
Loop, 9
GuiControl, Disable, Button%A_Index%
return
Finish_Line(Set){ ; Finish_Line is called when a line exists with 2 of the same character. It goes in the remaining spot, thereby blocking or winning.
Loop, parse, set
{
GuiControlGet, IsEnabled, Enabled, Button%A_LoopField%
Control=Button%A_LoopField%
If IsEnabled
Go(Control,"O")
}
}
RandomMove:
Loop{
Random, rnd, 1, 9
GuiControlGet, IsEnabled, Enabled, Button%rnd%
If IsEnabled
{
Control=Button%rnd%
Go(Control,"O")
Break
}
}
return
GuiClose:
ExitApp
AWK
# syntax: GAWK -f TIC-TAC-TOE.AWK
BEGIN {
move[12] = "3 7 4 6 8"; move[13] = "2 8 6 4 7"; move[14] = "7 3 2 8 6"
move[16] = "8 2 3 7 4"; move[17] = "4 6 8 2 3"; move[18] = "6 4 7 3 2"
move[19] = "8 2 3 7 4"; move[23] = "1 9 6 4 8"; move[24] = "1 9 3 7 8"
move[25] = "8 3 7 4 0"; move[26] = "3 7 1 9 8"; move[27] = "6 4 1 9 8"
move[28] = "1 9 7 3 4"; move[29] = "4 6 3 7 8"; move[35] = "7 4 6 8 2"
move[45] = "6 7 3 2 0"; move[56] = "4 7 3 2 8"; move[57] = "3 2 8 4 6"
move[58] = "2 3 7 4 6"; move[59] = "3 2 8 4 6"
split("7 4 1 8 5 2 9 6 3",rotate)
n = split("253 280 457 254 257 350 452 453 570 590",special)
i = 0
while (i < 9) { s[++i] = " " }
print("")
print("You move first, use the keypad:")
board = "\n7 * 8 * 9\n*********\n4 * 5 * 6\n*********\n1 * 2 * 3\n\n? "
printf(board)
}
state < 7 {
x = $0
if (s[x] != " ") {
printf("? ")
next
}
s[x] = "X"
++state
print("")
if (state > 1) {
for (i=0; i<r; ++i) { x = rotate[x] }
}
}
state == 1 {
for (r=0; x>2 && x!=5; ++r) { x = rotate[x] }
k = x
if (x == 5) { d = 1 } else { d = 5 }
}
state == 2 {
c = 5.5 * (k + x) - 4.5 * abs(k - x)
split(move[c],t)
d = t[1]
e = t[2]
f = t[3]
g = t[4]
h = t[5]
}
state == 3 {
k = x / 2.
c = c * 10
d = f
if (abs(c-350) == 100) {
if (x != 9) { d = 10 - x }
if (int(k) == k) { g = f }
h = 10 - g
if (x+0 == e+0) {
h = g
g = 9
}
}
else if (x+0 != e+0) {
d = e
state = 6
}
}
state == 4 {
if (x+0 == g+0) {
d = h
}
else {
d = g
state = 6
}
x = 6
for (i=1; i<=n; ++i) {
b = special[i]
if (b == 254) { x = 4 }
if (k+0 == abs(b-c-k)) { state = x }
}
}
state < 7 {
if (state != 5) {
for (i=0; i<4-r; ++i) { d = rotate[d] }
s[d] = "O"
}
for (b=7; b>0; b-=5) {
printf("%s * %s * %s\n",s[b++],s[b++],s[b])
if (b > 3) { print("*********") }
}
print("")
}
state < 5 {
printf("? ")
}
state == 5 {
printf("tie game")
state = 7
}
state == 6 {
printf("you lost")
state = 7
}
state == 7 {
printf(", play again? ")
++state
next
}
state == 8 {
if ($1 !~ /^[yY]$/) { exit(0) }
i = 0
while (i < 9) { s[++i] = " " }
printf(board)
state = 0
}
function abs(x) { if (x >= 0) { return x } else { return -x } }
Bash
Computer is X. Computer randomly goes first. Computer plays a good game, but not a perfect game. It will win when it can and draw when it can not.
It performs a depth-first scan of all following moves. It ignores dumb actions like not winning when either player can.
For each possible move it records if it will win, lose, or something else (like win and draw depending on opponent's move).
If there is a choice of best moves, it picks one at random.
I have not used simple bash code to:
- keep it under 100 lines;
- to demonstrate usefulness of bash integers;
- show-off ANSI ESC sequences;
- implement recursion in bash;
- demonstrate conditional and alternate execution using && and || with { ...; };
- show that you don't always need to use $ to refer to integer variables;
- encourage use of [[ ]] instead of [ ] for boolean expressions;
- provide examples of pattern matching; and
- encourage use of bash for more interesting tasks.
#!/bin/bash
declare -a B=( e e e e e e e e e ) # Board
function show(){ # show B - underline first 2 rows; highlight position; number empty positoins
local -i p POS=${1:-9}; local UL BOLD="\e[1m" GREEN="\e[32m" DIM="\e[2m" OFF="\e[m" ULC="\e[4m"
for p in 0 1 2 3 4 5 6 7 8; do
[[ p%3 -eq 0 ]] && printf " " # indent boards
UL=""; [[ p/3 -lt 2 ]] && UL=$ULC # underline first 2 rows
[[ p -eq POS ]] && printf "$BOLD$GREEN" # bold and colour for this position
[[ ${B[p]} = e ]] && printf "$UL$DIM%d$OFF" $p || printf "$UL%s$OFF" ${B[p]} # num or UL
{ [[ p%3 -lt 2 ]] && printf "$UL | $OFF"; } || printf "\n" # underline vertical bars or NL
done
};
function win(){ # win 'X' 3 return true if X wins after move in position 3
local ME=$1; local -i p=$2
[[ ${B[p/3*3]} = $ME && ${B[p/3*3+1]} = $ME && ${B[p/3*3+2]} = $ME ]] && return 0 # row
[[ ${B[p]} = $ME && ${B[(p+3)%9]} = $ME && ${B[(p+6)%9]} = $ME ]] && return 0 # col
[[ ${B[4]} != $ME ]] && return 1 # don't test diags
[[ p%4 -eq 0 && ${B[0]} = $ME && ${B[8]} = $ME ]] && return 0 # TL - BR diag
[[ p%4 -eq 2 || p -eq 4 ]] && [[ ${B[2]} = $ME && ${B[6]} = $ME ]] # TR - BL diag
};
function bestMove(){ # return best move or 9 if none possible
local ME=$1 OP=$2; local -i o s p
local -ia S=( -9 -9 -9 -9 -9 -9 -9 -9 -9 ) # score board
local -a SB # save board
[[ ${B[*]//[!e]} = "" ]] && return 9 # game over
SB=( ${B[*]} ) # save Board
for p in 0 1 2 3 4 5 6 7 8; do # for each board position
[[ ${B[p]} != e ]] && continue # skip occupied positions
B[p]=$ME # occupy position
win $ME $p && { S[p]=2; B=( ${SB[*]} ); return $p; } # ME wins so this is best move
bestMove $OP $ME; o=$? # what will opponent do
[[ o -le 8 ]] && { B[o]=$OP; win $OP $o; s=$?; } # opponent can make a legal move
S[p]=${s:-1} # save result of opponent move
B=( ${SB[*]} ) # restore board after each trial run
done
local -i best=-1; local -ia MOV=()
for p in 0 1 2 3 4 5 6 7 8; do # find all best moves
[[ S[p] -lt 0 ]] && continue # dont bother with occupied positions
[[ S[p] -eq S[best] ]] && { MOV+=(p); best=p; } # add this move to current list
[[ S[p] -gt S[best] ]] && { MOV=(p); best=p; } # a better move so scrap list and start again
done
return ${MOV[ RANDOM%${#MOV[*]} ]} # pick one at random
};
function getMove(){ # getMove from opponent
[[ $ME = X ]] && { bestMove $ME $OP; return $?; } # pick X move automatically
read -p "O move: " -n 1; printf "\n"; return $REPLY # get opponents move
};
function turn(){ # turn starts or continues a game. It is ME's turn
local -i p; local ME=$1 OP=$2
getMove; p=$?; [[ p -gt 8 ]] && { printf "Draw!\n"; show; return 1; } # no move so a draw
B[p]=$ME; printf "%s moves %d\n" $ME $p # mark board
win $ME $p && { printf "%s wins!\n" $ME; show $p; [[ $ME = X ]] && return 2; return 0; }
[[ ${B[*]//[!e]} = "" ]] && { printf "Draw!\n"; show; return 1; } # no move so a draw
show $p; turn $OP $ME # opponent moves
};
printf "Bic Bash Bow\n"
show; [[ RANDOM%2 -eq 0 ]] && { turn O X; exit $?; } || turn X O
- Output:
(nice ANSI formatting is not shown)
Bic Bash Bow 0 | 1 | 2 3 | 4 | 5 6 | 7 | 8 X moves 1 0 | X | 2 3 | 4 | 5 6 | 7 | 8 O move: 5 O moves 5 0 | X | 2 3 | 4 | O 6 | 7 | 8 X moves 2 0 | X | X 3 | 4 | O 6 | 7 | 8 O move: 0 O moves 0 O | X | X 3 | 4 | O 6 | 7 | 8 X moves 4 O | X | X 3 | X | O 6 | 7 | 8 O move: 6 O moves 6 O | X | X 3 | X | O O | 7 | 8 X moves 7 X wins! O | X | X 3 | X | O O | X | 8
BASIC
BASIC256
# basado en código de Antonio Rodrigo dos Santos Silva (gracias):
# http://statusgear.freeforums.net/thread/17/basic-256-tic-tac-toe
global playerturn$
global endGame$
global space$
global player1Score$
global player2Score$
global invalidMove$
global tecla$
global keyQ$
global keyW$
global keyE$
global keyA$
global keyS$
global keyD$
global keyZ$
global keyX$
global keyC$
global keySpace$
global keyEsc$
keyQ$ = 81
keyW$ = 87
keyE$ = 69
keyA$ = 65
keyS$ = 83
keyD$ = 68
keyZ$ = 90
keyX$ = 88
keyC$ = 67
keySpace$ = 32
keyEsc$ = 16777216
dim space$(9)
subroutine clearGameVars()
playerturn$ = 1
invalidMove$ = 0
endGame$ = 0
tecla$ = 0
for t = 0 to space$[?]-1
space$[t] = 0
next t
end subroutine
subroutine endGame()
cls
print "¡Hasta pronto!..."
end
end subroutine
subroutine printBoard()
print " " + space$[0]+" | "+space$[1]+" | "+space$[2]
print " " + "— + — + —"
print " " + space$[3]+" | "+space$[4]+" | "+space$[5]
print " " + "— + — + —"
print " " + space$[6]+" | "+space$[7]+" | "+space$[8]
print ""
end subroutine
subroutine changePlayer()
if playerturn$ = 1 then
playerturn$ = 2
else
playerturn$ = 1
end if
end subroutine
subroutine endMatchWithWinner()
cls
call printPlayerScore()
call printBoard()
endGame$ = 1
if playerturn$ = 1 then
player1Score$ += 1
else
player2Score$ += 1
end if
print "¡Jugador " + playerturn$ + " gana!" + chr(10)
print "Pulsa [SPACE] para jugar otra partida"
print "Pulsa [ESC] para dejar de jugar"
do
tecla$ = key
pause .01
if tecla$ = keySpace$ then call gamePlay()
if tecla$ = keyEsc$ then call endGame()
until false
end subroutine
subroutine endMatchWithoutWinner()
cls
call printPlayerScore()
call printBoard()
endGame$ = 1
print " Nadie ganó :( " + chr(10)
print " Pulsa [SPACE] para comenzar o [ESC] para salir. "
do
tecla$ = key
pause .01
if tecla$ = keySpace$ then call gamePlay()
if tecla$ = keyEsc$ then call endGame()
until false
end subroutine
subroutine printPlayerScore()
print "--------------------------------------------"
print " Jugador #1: " + player1Score$ + " pts"
print " Jugador #2: " + player2Score$ + " pts"
print "--------------------------------------------" + chr(10)
end subroutine
subroutine printPlayerMessage()
print "Jugador: " + playerturn$ + ", elige una casilla, por favor."
end subroutine
subroutine gamePlay()
call clearGameVars()
cls
call printPlayerScore()
call printBoard()
call printPlayerMessage()
while 0 = 0
invalidMove$ = 0
if endGame$ = 0 then
do
tecla$ = key
pause .01
validKeypressed$ = 0
if tecla$ = keyQ$ or tecla$ = keyW$ or tecla$ = keyE$ or tecla$ = keyA$ or tecla$ = keyS$ or tecla$ = keyD$ or tecla$ = keyZ$ or tecla$ = keyX$ or tecla$ = keyC$ then validKeypressed$ = 1
until validKeypressed$ = 1
endif
if tecla$ = keyQ$ then
if space$[0] = 0 then
space$[0] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyW$ then
if space$[1] = 0 then
space$[1] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyE$ then
if space$[2] = 0 then
space$[2] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyA$ then
if space$[3] = 0 then
space$[3] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyS$ then
if space$[4] = 0 then
space$[4] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyD$ then
if space$[5] = 0 then
space$[5] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyZ$ then
if space$[6] = 0 then
space$[6] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyX$ then
if space$[7] = 0 then
space$[7] = playerturn$
else
invalidMove$ = 1
endif
endif
if tecla$ = keyC$ then
if space$[8] = 0 then
space$[8] = playerturn$
else
invalidMove$ = 1
endif
endif
if invalidMove$ = 0 then
tecla$ = 0
if space$[0] = 1 and space$[1] = 1 and space$[2] = 1 then call endMatchWithWinner()
if space$[3] = 1 and space$[4] = 1 and space$[5] = 1 then call endMatchWithWinner()
if space$[6] = 1 and space$[7] = 1 and space$[8] = 1 then call endMatchWithWinner()
if space$[0] = 1 and space$[3] = 1 and space$[6] = 1 then call endMatchWithWinner()
if space$[1] = 1 and space$[4] = 1 and space$[7] = 1 then call endMatchWithWinner()
if space$[2] = 1 and space$[5] = 1 and space$[8] = 1 then call endMatchWithWinner()
if space$[0] = 1 and space$[4] = 1 and space$[8] = 1 then call endMatchWithWinner()
if space$[2] = 1 and space$[4] = 1 and space$[6] = 1 then call endMatchWithWinner()
if space$[0] = 2 and space$[1] = 2 and space$[2] = 2 then call endMatchWithWinner()
if space$[3] = 2 and space$[4] = 2 and space$[5] = 2 then call endMatchWithWinner()
if space$[6] = 2 and space$[7] = 2 and space$[8] = 2 then call endMatchWithWinner()
if space$[0] = 2 and space$[3] = 2 and space$[6] = 2 then call endMatchWithWinner()
if space$[1] = 2 and space$[4] = 2 and space$[7] = 2 then call endMatchWithWinner()
if space$[2] = 2 and space$[5] = 2 and space$[8] = 2 then call endMatchWithWinner()
if space$[0] = 2 and space$[4] = 2 and space$[8] = 2 then call endMatchWithWinner()
if space$[2] = 2 and space$[4] = 2 and space$[6] = 2 then call endMatchWithWinner()
if space$[0] <> 0 and space$[1] <> 0 and space$[2] <> 0 and space$[3] <> 0 and space$[4] <> 0 and space$[5] <> 0 and space$[6] <> 0 and space$[7] <> 0 and space$[8] <> 0 then call endMatchWithoutWinner()
call changePlayer()
cls
call printPlayerScore()
call printBoard()
call printPlayerMessage()
end if
end while
end subroutine
subroutine gameMenu()
cls
call clearGameVars()
player1Score$ = 0
player2Score$ = 0
print "================================================="
print "| TIC-TAC-TOE |"
print "=================================================" + chr(10)
print " Teclas para jugar:"
print "---------------------"
print " | q | w | e |"
print " | a | s | d |"
print " | z | x | c |" + chr(10)
print " Pulsa [SPACE] para comenzar o [ESC] para salir. "
do
tecla$ = key
pause .01
if tecla$ = keySpace$ then call gamePlay()
if tecla$ = keyEsc$ then call endGame()
until false
end subroutine
call gameMenu()
end
FreeBASIC
graphics mode
'About 400 lines of code, but it is a graphical (GUI ish) i.e. mouse driven.
'I have made provision for the player to beat the computer now and then.
Type box
As long x,y
As long wide,high,index
Dim As ulong colour
As String caption
Declare Sub show
Declare Sub NewCaption(s As String)
Declare Constructor
Declare Constructor(x As long,y As long,wide As long,_
high As long,index As long,colour As ulong,caption As String)
End Type
Constructor box
End Constructor
Constructor box(x As long,y As long,wide As long,_
high As long,index As long,colour As ulong,caption As String)
this.x=x
this.y=y
this.wide=wide
this.high=high
this.index=index
this.colour=colour
this.caption=caption
End Constructor
'ALL PROCEDURES:
Declare Function inside(B As box,px As long,py As long) As long
Declare Sub make_frame_image(im As ulong Pointer)
Declare Sub setup_grid(boxes() As box,cellsacross As long,cellsdown As long,xp As long,yp As long,w As long,h As long)
Declare Function all_clicked(b() As box) As long
Declare Sub OnCLICK(a() As box,b As box)
Declare Sub refresh_screen(b() As box,f1 As long=0,f2 As long=0)
Declare Function Get_Mouse_Events(boxes() As box) As long
Declare Sub thickline(x1 As long,y1 As long,x2 As long,y2 As long,thickness As Single,colour As ulong,im As Any Pointer=0)
Declare Sub lineto(x1 As long,y1 As long,x2 As long,y2 As long,l As long,th As Single,col As ulong,im As Any Pointer=0)
Declare Sub thickcircle(x As long,y As long,rad As long,th As Single,col As ulong,im As Any Pointer=0)
Declare Sub startup(b() As box)
Declare Sub get_computer_events(b() As box)
Declare Sub finish
'Macro used by more than one procedure
#macro incircle(cx,cy,radius,x,y)
(cx-x)*(cx-x) +(cy-y)*(cy-y)<= radius*radius
#endmacro
'=============== RUN ============================
Screen 19,32',1,16
Color ,Rgb(233,236,216) 'background colour
windowtitle string(100," ")+"Noughts and crosses"
'Globals:
Dim Shared As ulong Pointer frame
Dim Shared As long computer,player
Dim Shared As String msg1,msg2,message
message="In Play"
msg1="Computer Start"
msg2="Player Start"
'Custom Frame
frame=Imagecreate(800,600)
Dim As box boxes(0 To 9)
setup_grid(boxes(),3,3,175,85,150,150)
make_frame_image(frame)
Do
If player=0 And computer=0 Then
startup(boxes())
End If
If player Then
Get_Mouse_Events(boxes())
End If
If computer Then
get_computer_events(boxes())
End If
If all_clicked(boxes()) Then get_computer_events(boxes())
Loop Until Inkey=Chr(27)
finish
Sub box.show
Line(this.x,this.y)-(this.x+this.wide,this.y+this.high),this.colour,bf
Line(this.x,this.y)-(this.x+this.wide,this.y+this.high),Rgb(200,200,200),b
''Draw String(this.x+.5*this.wide-4*Len(this.caption),this.y+(.5*this.high-4)),this.caption,Rgb(0,0,0)
If this.index=0 Then
Draw String(this.x+.5*this.wide-4*Len(this.caption),this.y+.5*this.high-6),this.caption,Rgb(0,0,0)
End If
End Sub
Sub box.NewCaption(s As String)
Var cx=(this.x+this.x+this.wide)/2
Var cy=(this.y+this.y+this.high)/2
If s="X" Then
For k As long=20 To 0 Step -1
lineto(cx,cy,this.x,this.y,50,k,Rgb(50+10*k,5*k,0),frame)
lineto(cx,cy,this.x+this.wide,this.y+this.high,50,k,Rgb(50+10*k,5*k,0),frame)
lineto(cx,cy,this.x,this.y+this.high,50,k,Rgb(50+10*k,5*k,0),frame)
lineto(cx,cy,this.x+this.wide,this.y,50,k,Rgb(50+10*k,5*k,0),frame)
Next k
Else
For k As long=20 To 0 Step -1
thickcircle(cx,cy,40,k,Rgb(50+10*k,5*k,0),frame)
Next k
End If
End Sub
Sub get_computer_events(b() As box)
#define other(n) b(n).caption<>"0" And b(n).caption<>"C"
#define another(n) b(n).caption="0"
#define rr(f,l) (Rnd*((l)-(f))+(f))
Dim As long flag,i,k,Cwin,Pwin,NoWin
Static As long firstclick
var chance="001100"
dim as long ch
'horiz player finish
For x As long=1 To 3
If b(1+k).caption="0" And b(2+k).caption="0" And another((3+k)) Then b(3+k).Caption="0":Pwin=1:Goto fin
If b(2+k).caption="0" And b(3+k).caption="0" And another((1+k))Then b(1+k).Caption="0":Pwin=1=1:Goto fin
If b(1+k).caption="0" And b(3+k).caption="0" And another((2+k))Then b(2+k).Caption="0":Pwin=1:Goto fin
k=k+3
Next x
k=0
'vert player finish
For x As long=1 To 3
If b(1+k).caption="0" And b(4+k).caption="0" And another((7+k)) Then b(7+k).Caption="0":Pwin=1:Goto fin
If b(4+k).caption="0" And b(7+k).caption="0" And another((1+k))Then b(1+k).Caption="0":Pwin=1:Goto fin
If b(1+k).caption="0" And b(7+k).caption="0" And another((4+k))Then b(4+k).Caption="0":Pwin=1:Goto fin
k=k+1
Next x
k=0
'player finish main diag
If b(1+k).caption="0" And b(5+k).caption="0" And another((9+k)) Then b(9+k).Caption="0":Pwin=1:Goto fin
If b(1+k).caption="0" And b(9+k).caption="0" And another((5+k))Then b(5+k).Caption="0":Pwin=1:Goto fin
If b(5+k).caption="0" And b(9+k).caption="0" And another((1+k))Then b(1+k).Caption="0":Pwin=1:Goto fin
'player finish other diag
If b(7+k).caption="0" And b(5+k).caption="0" And another((3+k)) Then b(3+k).Caption="0":Pwin=1:Goto fin
If b(5+k).caption="0" And b(3+k).caption="0" And another((7+k))Then b(7+k).Caption="0":Pwin=1:Goto fin
If b(7+k).caption="0" And b(3+k).caption="0" And another((5+k))Then b(5+k).Caption="0":Pwin=1:Goto fin
'horiz computer finish
For x As long=1 To 3
If b(1+k).caption="C" And b(2+k).caption="C" And other((3+k)) Then b(3+k).Caption="C":Cwin=1:Goto fin
If b(2+k).caption="C" And b(3+k).caption="C" And other((1+k))Then b(1+k).Caption="C":Cwin=1:Goto fin
If b(1+k).caption="C" And b(3+k).caption="C" And other((2+k))Then b(2+k).Caption="C":Cwin=1:Goto fin
k=k+3
Next x
k=0
'vert computer finish
For x As long=1 To 3
If b(1+k).caption="C" And b(4+k).caption="C" And other((7+k)) Then b(7+k).Caption="C":Cwin=1:Goto fin
If b(4+k).caption="C" And b(7+k).caption="C" And other((1+k))Then b(1+k).Caption="C":Cwin=1:Goto fin
If b(1+k).caption="C" And b(7+k).caption="C" And other((4+k))Then b(4+k).Caption="C":Cwin=1:Goto fin
k=k+1
Next x
k=0
'computer finish main diag
If b(1+k).caption="C" And b(5+k).caption="C" And other((9+k)) Then b(9+k).Caption="C":Cwin=1:Goto fin
If b(1+k).caption="C" And b(9+k).caption="C" And other((5+k))Then b(5+k).Caption="C":Cwin=1:Goto fin
If b(5+k).caption="C" And b(9+k).caption="C" And other((1+k))Then b(1+k).Caption="C":Cwin=1:Goto fin
'computer finish other diag
If b(7+k).caption="C" And b(5+k).caption="C" And other((3+k)) Then b(3+k).Caption="C":Cwin=1:Goto fin
If b(5+k).caption="C" And b(3+k).caption="C" And other((7+k))Then b(7+k).Caption="C":Cwin=1:Goto fin
If b(7+k).caption="C" And b(3+k).caption="C" And other((5+k))Then b(5+k).Caption="C":Cwin=1:Goto fin
'block horizontals
For x As long=1 To 3
If b(1+k).caption="0" And b(2+k).caption="0" And other((3+k)) Then b(3+k).Caption="C":flag=1:Goto fin
If b(2+k).caption="0" And b(3+k).caption="0" And other((1+k))Then b(1+k).Caption="C":flag=1:Goto fin
If b(1+k).caption="0" And b(3+k).caption="0" And other((2+k))Then b(2+k).Caption="C":flag=1:Goto fin
k=k+3
Next x
k=0
'block verticals
For x As long=1 To 3
If b(1+k).caption="0" And b(4+k).caption="0" And other((7+k)) Then b(7+k).Caption="C":flag=1:Goto fin
If b(4+k).caption="0" And b(7+k).caption="0" And other((1+k))Then b(1+k).Caption="C":flag=1:Goto fin
If b(1+k).caption="0" And b(7+k).caption="0" And other((4+k))Then b(4+k).Caption="C":flag=1:Goto fin
k=k+1
Next x
k=0
'block main diag
If b(1+k).caption="0" And b(5+k).caption="0" And other((9+k)) Then b(9+k).Caption="C":flag=1:Goto fin
If b(1+k).caption="0" And b(9+k).caption="0" And other((5+k))Then b(5+k).Caption="C":flag=1:Goto fin
If b(5+k).caption="0" And b(9+k).caption="0" And other((1+k))Then b(1+k).Caption="C":flag=1:Goto fin
'block other diag
If b(7+k).caption="0" And b(5+k).caption="0" And other((3+k)) Then b(3+k).Caption="C":flag=1:Goto fin
If b(5+k).caption="0" And b(3+k).caption="0" And other((7+k))Then b(7+k).Caption="C":flag=1:Goto fin
If b(7+k).caption="0" And b(3+k).caption="0" And other((5+k))Then b(5+k).Caption="C":flag=1:Goto fin
If firstclick=0 Then
firstclick=1
var st="1379"
dim as long i=rr(0,3)
If Valint(b(5).caption)=0 and b(5).caption <> "C" Then b(st[i]-48).caption="C":Goto fin
End If
ch=rr(0,5)
if chance[ch]-48=1 then
If Valint(b(5).caption)<>0 Then b(5).caption="C":Goto fin
end if
If all_clicked(b()) Then Nowin=1:Goto fin
If flag=0 Then
Randomize
Do
i=rr(1,9)
If Valint(b(i).caption) <> 0 Then b(i).caption="C":Exit Do
Loop
End If
fin:
If Cwin=1 Or Pwin=1 Or NoWin=1 Then
Dim As long mx,my,mb
dim as integer x,y
screencontrol 0,x,y
for z as single=0 to 8*atn(1) step .001
dim as integer xx=x+100*cos(z)
dim as integer yy=y+100*sin(z)
screencontrol 100,xx,yy
next z
screencontrol 100,x,y
If Cwin=1 Then Message="You Loose"
If Pwin=1 Then Message="You WIN"
If Nowin=1 Then Message="DRAW"
cwin=0:k=0:pWin=0:Nowin=0:firstclick=0'i
Do
Getmouse mx,my,,mb
If inside(b(0),mx,my) And mb=1 Then finish
Var ic=incircle(500,55,20,mx,my)
If incircle(500,55,20,mx,my) And mb=1 Then Exit Do
refresh_screen(b(),ic)
Loop Until Inkey=chr(27)
For z As long=1 To Ubound(b)
b(z).caption=Str(b(z).index)
Next z
Imagedestroy frame
frame=Imagecreate(800,600)
make_frame_image(frame)
computer=0:player=0
Exit Sub
End If
player=1:computer=0
End Sub
Sub startup(b() As box)
message="In Play"
Dim As long mx,my,mb
Getmouse mx,my,,mb
For n As long=0 To Ubound(b)
If inside(b(n),mx,my) And mb=1 Then
If b(n).index=0 Then
finish
End If
End If
b(0).colour=Rgb(200,0,0)
Next n
Dim As long f1,f2
If incircle(80,230,10,mx,my) Then
f1=1:f2=0
If mb=1 Then computer=1:player=0
End If
If incircle(670,230,10,mx,my) Then
f1=0:f2=1
If mb=1 Then player=1:computer=0
End If
refresh_screen(b(),f1,f2)
End Sub
Sub thickcircle(x As long,y As long,rad As long,th As Single,col As ulong,im As Any Pointer=0)
Circle(x,y),rad+th/2,col
Circle(x,y),rad-th/2,col
Paint(x,y+rad),col,col
End Sub
Sub thickline(x1 As long,_
y1 As long,_
x2 As long,_
y2 As long,_
thickness As Single,_
colour As ulong,_
im As Any Pointer=0)
Dim p As ulong=Rgb(255, 255, 255)
If thickness<2 Then
Line(x1,y1)-(x2,y2),colour
Else
Dim As Double s,h,c
h=Sqr((x2-x1)^2+(y2-y1)^2)
If h=0 Then h=1e-6
s=(y1-y2)/h
c=(x2-x1)/h
For x As long=1 To 2
Line im,(x1+s*thickness/2,y1+c*thickness/2)-(x2+s*thickness/2,y2+c*thickness/2),p
Line im,(x1-s*thickness/2,y1-c*thickness/2)-(x2-s*thickness/2,y2-c*thickness/2),p
Line im,(x1+s*thickness/2,y1+c*thickness/2)-(x1-s*thickness/2,y1-c*thickness/2),p
Line im,(x2+s*thickness/2,y2+c*thickness/2)-(x2-s*thickness/2,y2-c*thickness/2),p
Paint im,((x1+x2)/2, (y1+y2)/2), p, p
p=colour
Next x
End If
End Sub
Sub lineto(x1 As long,y1 As long,x2 As long,y2 As long,l As long,th As Single,col As ulong,im As Any Pointer=0)
Dim As long diffx=x2-x1,diffy=y2-y1,ln=Sqr(diffx*diffx+diffy*diffy)
Dim As Single nx=diffx/ln,ny=diffy/ln
thickline(x1,y1,(x1+l*nx),(y1+l*ny),th,col,im)
End Sub
Function inside(B As box,px As long,py As long) As long
Return (px>B.x)*(px<(B.x+B.wide))*(py>B.y)*(py<(B.y+B.high))
End Function
Sub make_frame_image(im As ulong Pointer)
#macro map(a,b,x,d,c)
((d)-(c))*((x)-(a))/((b)-(a))+(c)
#endmacro
#macro logo(sx,sy,rad)
For k As Single=-rad/10 To rad/10 Step .5:Circle im,(sx,sy),rad+k,Rgb(15,118,155):Next
For k As Single=-rad/10 To rad/10 Step .5:Circle im,(sx+1.3*rad,sy+rad),rad+k,Rgb(230,193,78),2.,1.7:Next
For k As Single=-rad/10 To rad/10 Step .5:Circle im,(sx+2*1.3*rad,sy),rad+k,Rgb(21,3,0),3.25,3.05:Next
For k As Single=-rad/10 To rad/10 Step .5:Circle im,(sx+3*1.3*rad,sy+rad),rad+k,Rgb(26,143,76),2,1.8:Next
For k As Single=-rad/10 To rad/10 Step .5:Circle im,(sx+4*1.3*rad,sy),rad+k,Rgb(200,63,87),3.25,3.05:Next
#endmacro
For k As long=0 To 50
Var r=map(0,50,k,233,193-20)
Var g=map(0,50,k,236,153-20)
Var b=map(0,50,k,216,19-19)
Line im,(0+k,20+k)-(800-k,600-k),Rgb(r,g,b),b
Next k
For k As long=0 To 20
Var r=map(0,20,k,250,0)
Var g=map(0,20,k,250,0)
Var b=map(0,20,k,250,255)
Line im,(0,k)-(780,k),Rgb(r,g,b)',bf
Next k
logo(60,8,5)
logo(380,8,5)
logo(720,8,5)
End Sub
Sub setup_grid(boxes() As box,cellsacross As long,cellsdown As long,xp As long,yp As long,w As long,h As long)
Dim As long index
For y As long=yp To yp+h*(cellsdown-1) Step h
For x As long=xp To xp+w*(cellsacross-1) Step w
index=index+1
boxes(index)=Type<box>(x,y,w,h,index,Rgb(133,136,116),Str(index))
Next x
Next y
boxes(0)=Type<box>(780,-2,20,24,0,Rgb(200,0,0),"X")
End Sub
Function all_clicked(b() As box) As long
Dim As long sum
For z As long=1 To Ubound(b)
sum=sum+Valint(b(z).caption)
Next z
If sum<=0 Then Return -1
End Function
Sub OnCLICK(a() As box,b As box)
If b.caption="0" Then Exit Sub
If b.caption="C" Then Exit Sub
If b.caption <> "C" Then b.caption="0"
If b.index=0 Then finish
player=0:computer=1
End Sub
Sub refresh_screen(b() As box,f1 As long=0,f2 As long=0)
Screenlock:Cls
For n As long=0 To Ubound(b)
b(n).show 'draw boxes
If b(n).caption="0" Then b(n).NewCaption("X")
If b(n).caption="C" Then b(n).NewCaption("O")
Next n
Put(0,0),frame,trans
Draw String (390,50),message,Rgb(0,0,0)
If message <>"In Play" Then
Circle(500,55),20,Rgb(255,20,255),,,,f
If f1=-1 Then Circle(500,55),20,Rgb(202,200,200),,,,f
Draw String(480,50),"Click",Rgb(0,0,0)
End If
If computer=0 And player=0 Then
Draw String (60,200),msg1,Rgb(0,0,0)
Circle(80,230),10,Rgb(0,0,0)
Circle(80,230),5,Rgb(100,100,100),,,,f
If f1=1 Then Circle(80,230),10,Rgb(200,0,0),,,,f
Draw String (650,200),msg2,Rgb(0,0,0)
Circle(670,230),10,Rgb(0,0,0)
Circle(670,230),5,Rgb(100,100,100),,,,f
If f2=1 Then Circle(670,230),10,Rgb(200,0,0),,,,f
End If
Screenunlock:Sleep 1,1
End Sub
Function Get_Mouse_Events(boxes() As box) As long
Static released As long
Static pressed As long
Dim As long mousex,mousey,mousebutton ,x,y
Getmouse mousex,mousey,,mousebutton
Dim As box bar=Type<box>(0,0,780,50,0,0,"")
refresh_screen(boxes())
For n As long=0 To Ubound(boxes)
If inside(boxes(n),mousex,mousey) Then
If released Then
boxes(n).colour=Rgb(120,123,103)
If n=0 Then boxes(0).colour=Rgb(255,0,0)
End If
If mousebutton=1 Then
If released Then OnCLICK(boxes(),boxes(n))
Exit For
End If
Else
boxes(n).colour=Rgb(133,136,116)
If n=0 Then boxes(0).colour=Rgb(200,0,0)
End If
Next n
If mousebutton=0 Then released=1 Else released=0 'clean clicks
Return 0
End Function
Sub finish
Screenunlock
Imagedestroy frame
End
End Sub
text mode
Screenres 320,240,32
'global variables globales
Dim Shared As Integer b(3,3) 'tablero
Dim Shared As Integer mx, my, btns, ox, oy
'prueba para ganar posición
'3 victorias horizontales
Function TestWin(t As Integer) As Integer
Dim As Integer win = 0
If b(0,0)= t And b(1,0)= t And b(2,0)= t Then win = t
If b(0,1)= t And b(1,1)= t And b(2,1)= t Then win = t
If b(0,2)= t And b(1,2)= t And b(2,2)= t Then win = t
'3 en vertical gana
If b(0,0)= t And b(0,1)= t And b(0,2)= t Then win = t
If b(1,0)= t And b(1,1)= t And b(1,2)= t Then win = t
If b(2,0)= t And b(2,1)= t And b(2,2)= t Then win = t
'cruzada gana
If b(0,0)= t And b(1,1)= t And b(2,2)= t Then win = t
If b(2,0)= t And b(1,1)= t And b(0,2)= t Then win = t
Return win
End Function
Sub InicializarTablero()
For j As Integer = 0 To 2
For i As Integer = 0 To 2
b(i,j)=0
Next i
Next j
End Sub
Sub DibujaTablero()
Locate 1,1 : Print "+---+---+---+"
For j As Integer = 0 To 2
Print "|";
For i As Integer = 0 To 2
If b(i,j) = 0 Then Print " |";
If b(i,j) = 1 Then Print " x |";
If b(i,j) = 2 Then Print " o |";
Next i
Print !"\n+---+---+---+"
Next j
End Sub
Function MovimientoHumano() As Integer
DibujaTablero()
Print !"\n HAZ CLICK CON EL MOUSE"
Print "EN LA CASILLA QUE ELIJAS"
Dim As Integer opcion = -1
While opcion = -1
Getmouse mx,my,,btns
While btns <> 1 'esperar a pulsar botón
Getmouse mx,my,,btns
Wend
mx = (mx-4)\32
my = (my-4)\16
If mx >= 0 And mx < 3 And my >= 0 And my < 3 Then
If b(mx,my) = 0 Then opcion = mx+my*3 'Casilla vacía?
End If
While btns=1
Getmouse mx,my,,btns
Wend
Wend
Return opcion
End Function
Function MovimientoAleatorio() As Integer
Dim As Integer opcion, i, j
opcion = Int(Rnd(1)*9)
j = Int(opcion/3)
i = opcion - Int(opcion/3)*3
While b(i,j) <> 0 Or (opcion > 8 Or opcion < 0)
opcion = Int(Rnd(1)*9)
j = Int(opcion/3)
i = opcion - Int(opcion/3)*3
Wend
Return j*3+i
End Function
Function MovimientoInteligente(t As Integer) As Integer
Dim As Integer i, j, opcion, t2
opcion = -1 'opcion aún no seleccionada
'obtener la ficha t2 de los oponentes
If t = 1 Then t2 = 2 Else t2 = 1
'prueba para la casilla central
If b(1,1) = 0 Then opcion = 4
'prueba para ganar
If opcion = -1 Then
If b(0,0)= 0 And b(1,0)= t And b(2,0)= t Then opcion = 0
If b(0,0)= t And b(1,0)= 0 And b(2,0)= t Then opcion = 1
If b(0,0)= t And b(1,0)= t And b(2,0)= 0 Then opcion = 2
If b(0,1)= 0 And b(1,1)= t And b(2,1)= t Then opcion = 3
If b(0,1)= t And b(1,1)= 0 And b(2,1)= t Then opcion = 4
If b(0,1)= t And b(1,1)= t And b(2,1)= 0 Then opcion = 5
If b(0,2)= 0 And b(1,2)= t And b(2,2)= t Then opcion = 6
If b(0,2)= t And b(1,2)= 0 And b(2,2)= t Then opcion = 7
If b(0,2)= t And b(1,2)= t And b(2,2)= 0 Then opcion = 8
'3 bloques verticales
If b(0,0)= 0 And b(0,1)= t And b(0,2)= t Then opcion = 0
If b(0,0)= t And b(0,1)= 0 And b(0,2)= t Then opcion = 3
If b(0,0)= t And b(0,1)= t And b(0,2)= 0 Then opcion = 6
If b(1,0)= 0 And b(1,1)= t And b(1,2)= t Then opcion = 1
If b(1,0)= t And b(1,1)= 0 And b(1,2)= t Then opcion = 4
If b(1,0)= t And b(1,1)= t And b(1,2)= 0 Then opcion = 7
If b(2,0)= 0 And b(2,1)= t And b(2,2)= t Then opcion = 2
If b(2,0)= t And b(2,1)= 0 And b(2,2)= t Then opcion = 5
If b(2,0)= t And b(2,1)= t And b(2,2)= 0 Then opcion = 8
'bloques cruzados
If b(0,0)= 0 And b(1,1)= t And b(2,2)= t Then opcion = 0
If b(0,0)= t And b(1,1)= 0 And b(2,2)= t Then opcion = 4
If b(0,0)= t And b(1,1)= t And b(2,2)= 0 Then opcion = 8
If b(2,0)= 0 And b(1,1)= t And b(0,2)= t Then opcion = 2
If b(2,0)= t And b(1,1)= 0 And b(0,2)= t Then opcion = 4
If b(2,0)= t And b(1,1)= t And b(0,2)= 0 Then opcion = 6
End If
'prueba para bloques
If opcion = -1 Then
If b(0,0)= 0 And b(1,0)= t2 And b(2,0)= t2 Then opcion = 0
If b(0,0)= t2 And b(1,0)= 0 And b(2,0)= t2 Then opcion = 1
If b(0,0)= t2 And b(1,0)= t2 And b(2,0)= 0 Then opcion = 2
If b(0,1)= 0 And b(1,1)= t2 And b(2,1)= t2 Then opcion = 3
If b(0,1)= t2 And b(1,1)= 0 And b(2,1)= t2 Then opcion = 4
If b(0,1)= t2 And b(1,1)= t2 And b(2,1)= 0 Then opcion = 5
If b(0,2)= 0 And b(1,2)= t2 And b(2,2)= t2 Then opcion = 6
If b(0,2)= t2 And b(1,2)= 0 And b(2,2)= t2 Then opcion = 7
If b(0,2)= t2 And b(1,2)= t2 And b(2,2)= 0 Then opcion = 8
'3 bloques verticales
If b(0,0)= 0 And b(0,1)= t2 And b(0,2)= t2 Then opcion = 0
If b(0,0)= t2 And b(0,1)= 0 And b(0,2)= t2 Then opcion = 3
If b(0,0)= t2 And b(0,1)= t2 And b(0,2)= 0 Then opcion = 6
If b(1,0)= 0 And b(1,1)= t2 And b(1,2)= t2 Then opcion = 1
If b(1,0)= t2 And b(1,1)= 0 And b(1,2)= t2 Then opcion = 4
If b(1,0)= t2 And b(1,1)= t2 And b(1,2)= 0 Then opcion = 7
If b(2,0)= 0 And b(2,1)= t2 And b(2,2)= t2 Then opcion = 2
If b(2,0)= t2 And b(2,1)= 0 And b(2,2)= t2 Then opcion = 5
If b(2,0)= t2 And b(2,1)= t2 And b(2,2)= 0 Then opcion = 8
'bloques cruzados
If b(0,0)= 0 And b(1,1)= t2 And b(2,2)= t2 Then opcion = 0
If b(0,0)= t2 And b(1,1)= 0 And b(2,2)= t2 Then opcion = 4
If b(0,0)= t2 And b(1,1)= t2 And b(2,2)= 0 Then opcion = 8
If b(2,0)= 0 And b(1,1)= t2 And b(0,2)= t2 Then opcion = 2
If b(2,0)= t2 And b(1,1)= 0 And b(0,2)= t2 Then opcion = 4
If b(2,0)= t2 And b(1,1)= t2 And b(0,2)= 0 Then opcion = 6
End If
If opcion = -1 Then
If b(0,0) = 0 Then opcion = 0
If b(2,0) = 0 Then opcion = 2
If b(0,2) = 0 Then opcion = 6
If b(2,2) = 0 Then opcion = 8
End If
'no hay opción de hacer una elección al azar
If opcion = -1 Then
opcion = Int(Rnd(1)*9)
j = Int(opcion/3)
i = opcion - Int(opcion/3)*3
'encontrar una casilla vacía
While b(i,j) <> 0
opcion = Int(Rnd(1)*9)
j = Int(opcion/3)
i = opcion - Int(opcion/3)*3
Wend
End If
Return opcion
End Function
InicializarTablero()
DibujaTablero()
Dim As Integer resultado
Dim As Integer jugador = 1
Dim As Integer ContarMovimientos = 0
Dim As Integer ContarPartidas = 0
Dim As Integer movimiento = 0
Dim As Integer i, j
Do
'alternar jugadores
If jugador = 1 Then jugador = 2 Else jugador = 1
'selecciona tipo de movimiento para cada jugador
If jugador = 1 Then
movimiento = MovimientoHumano()
Else
movimiento = MovimientoInteligente(2)
End If
'print "movimiento ="; movimiento
'print "jugador ="; jugador
'convertir la elección a las coordenadas del tablero i,j
j = Int(movimiento/3)
i = movimiento - (j*3)
b(i,j) = jugador 'ingrese la ficha de jugador 1 o 2
resultado = TestWin(jugador) 'comprobar si el jugador ha ganado
DibujaTablero()
ContarMovimientos += 1
'=======================================================
'Comprobar final de partida y/o un resultado de victoria
'=======================================================
If ContarMovimientos = 9 Or resultado <> 0 Then
DibujaTablero()
If resultado = 0 Then Print !"\n EMPATE "
If resultado = 1 Then Print !"\n x GANA "
If resultado = 2 Then Print !"\n o GANA "
Print Space(28)
Print "PULSA BARRA ESPACIADORA PARA"
Print "OTRA PARTIDA, ESC PARA SALIR"
Sleep
Cls
InicializarTablero() 'reiniciar tablero
ContarMovimientos = 0
ContarPartidas += 1
End If
Loop Until Multikey(&H01)
End
Microsoft Small Basic
This game has a simple AI.
place1 = 1
place2 = 2
place3 = 3
place4 = 4
place5 = 5
place6 = 6
place7 = 7
place8 = 8
place9 = 9
symbol1 = "X"
symbol2 = "O"
reset:
TextWindow.Clear()
TextWindow.Write(place1 + " ")
TextWindow.Write(place2 + " ")
TextWindow.WriteLine(place3 + " ")
TextWindow.Write(place4 + " ")
TextWindow.Write(place5 + " ")
TextWindow.WriteLine(place6 + " ")
TextWindow.Write(place7 + " ")
TextWindow.Write(place8 + " ")
TextWindow.WriteLine(place9 + " ")
TextWindow.WriteLine("Where would you like to go to (choose a number from 1 to 9 and press enter)?")
n = TextWindow.Read()
If n = 1 then
If place1 = symbol1 or place1 = symbol2 then
Goto ai
Else
place1 = symbol1
EndIf
ElseIf n = 2 then
If place2 = symbol1 or place2 = symbol2 then
Goto ai
Else
place2 = symbol1
EndIf
ElseIf n = 3 then
If place3 = symbol1 or place3 = symbol2 then
Goto ai
Else
place3 = symbol1
EndIf
ElseIf n = 4 then
If place4 = symbol1 or place4 = symbol2 then
Goto ai
Else
place4 = symbol1
EndIf
ElseIf n = 5 then
If place5 = symbol1 or place5 = symbol2 then
Goto ai
Else
place5 = symbol1
EndIf
ElseIf n = 6 then
If place6 = symbol1 or place6 = symbol2 then
Goto ai
Else
place6 = symbol1
EndIf
ElseIf n = 7 then
If place8 = symbol1 or place7 = symbol2 then
Goto ai
Else
place7 = symbol1
EndIf
ElseIf n = 8 then
If place8 = symbol1 or place8 = symbol2 then
Goto ai
Else
place8 = symbol1
EndIf
ElseIf n = 9 then
If place9 = symbol1 or place9 = symbol2 then
Goto ai
Else
place9 = symbol1
EndIf
EndIf
Goto ai
ai:
n = Math.GetRandomNumber(9)
If n = 1 then
If place1 = symbol1 or place1 = symbol2 then
Goto ai
Else
place1 = symbol2
EndIf
ElseIf n = 2 then
If place2 = symbol1 or place2 = symbol2 then
Goto ai
Else
place2 = symbol2
EndIf
ElseIf n = 3 then
If place3 = symbol1 or place3 = symbol2 then
Goto ai
Else
place3 = symbol2
EndIf
ElseIf n = 4 then
If place4 = symbol1 or place4 = symbol2 then
Goto ai
Else
place4 = symbol2
EndIf
ElseIf n = 5 then
If place5 = symbol1 or place5 = symbol2 then
Goto ai
Else
place5 = symbol2
EndIf
ElseIf n = 6 then
If place6 = symbol1 or place6 = symbol2 then
Goto ai
Else
place6 = symbol2
EndIf
ElseIf n = 7 then
If place7 = symbol1 or place7 = symbol2 then
Goto ai
Else
place7 = symbol2
EndIf
ElseIf n = 8 then
If place8 = symbol1 or place8 = symbol2 then
Goto ai
Else
place8 = symbol2
EndIf
ElseIf n = 9 then
If place9 = symbol1 or place9 = symbol2 then
Goto ai
Else
place9 = symbol2
EndIf
EndIf
If place1 = symbol1 and place2 = symbol1 and place3 = symbol1 or place4 = symbol1 and place5 = symbol1 and place6 = symbol1 or place7 = symbol1 and place8 = symbol1 and place9 = symbol1 or place1 = symbol1 and place4 = symbol1 and place7 = symbol1 or place2 = symbol1 and place5 = symbol1 and place8 = symbol1 or place3 = symbol1 and place6 = symbol1 and place9 = symbol1 or place1 = symbol1 and place5 = symbol1 and place9 = symbol1 or place3 = symbol1 and place5 = symbol1 and place7 = symbol1 then
TextWindow.WriteLine("Player 1 (" + symbol1 + ") wins!")
ElseIf place1 = symbol2 and place2 = symbol2 and place3 = symbol2 or place4 = symbol2 and place5 = symbol2 and place6 = symbol2 or place7 = symbol2 and place8 = symbol2 and place9 = symbol2 or place1 = symbol2 and place4 = symbol2 and place7 = symbol2 or place2 = symbol2 and place5 = symbol2 and place8 = symbol2 or place3 = symbol2 and place6 = symbol2 and place9 = symbol2 or place1 = symbol2 and place5 = symbol2 and place8 = symbol2 or place3 = symbol2 and place5 = symbol2 and place7 = symbol2 then
TextWindow.WriteLine("Player 2 (" + symbol2 + ") wins!")
Else
Goto reset
EndIf
QuickBASIC
' Tic-tac-toe
DECLARE FUNCTION Win% (Piece AS STRING)
DECLARE FUNCTION SpacesFilled% ()
DECLARE SUB ClearBoard ()
DECLARE SUB DisplayNumberedBoard ()
DECLARE SUB DisplayPiecedBoard ()
DECLARE FUNCTION Evaluate% (Me AS STRING, Him AS STRING)
DIM SHARED Board(8) AS STRING * 1, BestMove AS INTEGER
DIM SHARED WinPos(7, 2) AS INTEGER
DIM SHARED MyPiece AS STRING, HisPiece AS STRING
FOR I = 0 TO 7
FOR J = 0 TO 2
READ WinPos(I, J)
NEXT J
NEXT I
' Winning positions
DATA 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 3, 6, 1, 4, 7, 2, 5, 8, 0, 4, 8, 2, 4, 6
MyWinsCnt = 0: HisWinsCnt = 0: DrawsCnt = 0
CompFirst = -1 ' It be reversed, so human goes first
CLS
PRINT
PRINT " TIC-TAC-TOE"
PRINT
PRINT "In this version, X always goes first."
PRINT "The board is numbered:"
DO
CompFirst = NOT CompFirst ' reverse who goes first
MovesCnt = 0
PRINT
DisplayNumberedBoard
PRINT
IF CompFirst THEN PRINT "I go first." ELSE PRINT "You go first. ";
ClearBoard
IF CompFirst THEN MyPiece = "X": HisPiece = "O" ELSE MyPiece = "O": HisPiece = "X"
' -1: human; 1: computer; 0: nobody
IF CompFirst THEN Mover = 1 ELSE Mover = -1
WHILE Mover <> 0
SELECT CASE Mover
CASE 1
IF MovesCnt = 0 THEN
BestMove = INT(RND * 9)
ELSEIF MovesCnt = 1 THEN
IF Board(4) <> " " THEN BestMove = INT(RND * 2) * 6 + INT(RND * 2) * 2 ELSE BestMove = 4
ELSE
T = Evaluate(MyPiece, HisPiece)
END IF
Board(BestMove) = MyPiece
MovesCnt = MovesCnt + 1
PRINT
CALL DisplayPiecedBoard
PRINT
IF Win(MyPiece) THEN
MyWinsCnt = MyWinsCnt + 1
PRINT "I win!"
Mover = 0
ELSEIF SpacesFilled THEN
DrawsCnt = DrawsCnt + 1
PRINT "It's a draw. Thank you."
Mover = 0
ELSE
Mover = -1
END IF
CASE -1
DO
INPUT "Where do you move? ", I
IF I < 1 OR I > 9 THEN
PRINT "Illegal! ";
ELSEIF Board(I - 1) <> " " THEN
PRINT "Place already occupied. ";
ELSE
EXIT DO
END IF
LOOP
Board(I - 1) = HisPiece
MovesCnt = MovesCnt + 1
PRINT
CALL DisplayPiecedBoard
PRINT
IF Win(HisPiece) THEN
HisWinsCnt = HisWinsCnt + 1
PRINT "You beat me! Good game."
Mover = 0
ELSEIF SpacesFilled THEN
DrawsCnt = DrawsCnt + 1
PRINT "It's a draw. Thank you."
Mover = 0
ELSE
Mover = 1
END IF
END SELECT
WEND
PRINT
INPUT "Another game (y/n)? ", Answ$
LOOP UNTIL UCASE$(Answ$) <> "Y"
PRINT
PRINT "Final score:"
PRINT "You won", HisWinsCnt; "game";
IF HisWinsCnt <> 1 THEN PRINT "s";
PRINT "."
PRINT "I won", MyWinsCnt; "game";
IF MyWinsCnt <> 1 THEN PRINT "s";
PRINT "."
PRINT "We tied", DrawsCnt; "game";
IF DrawsCnt <> 1 THEN PRINT "s";
PRINT "."
PRINT "See you later!"
END
SUB ClearBoard
FOR I = 0 TO 8: Board(I) = " ": NEXT I
END SUB
SUB DisplayNumberedBoard
FOR I = 0 TO 8 STEP 3
PRINT I + 1; "|"; I + 2; "|"; I + 3
IF I <> 6 THEN PRINT "---+---+---"
NEXT I
END SUB
SUB DisplayPiecedBoard
FOR I = 0 TO 8 STEP 3
PRINT " "; Board(I); " | "; Board(I + 1); " | "; Board(I + 2)
IF I <> 6 THEN PRINT "---+---+---"
NEXT I
END SUB
FUNCTION Evaluate% (Me AS STRING, Him AS STRING)
' Recursive algorithm
IF Win(Me) THEN Evaluate = 1: EXIT FUNCTION
IF Win(Him) THEN Evaluate = -1: EXIT FUNCTION
IF SpacesFilled THEN Evaluate = 0: EXIT FUNCTION
LoseFlag = 1
FOR I = 0 TO 8
IF Board(I) = " " THEN
Board(I) = Me ' Try the move.
V = Evaluate(Him, Me)
Board(I) = " " ' Restore the empty space.
IF V = -1 THEN BestMove = I: Evaluate = 1: EXIT FUNCTION
IF V = 0 THEN LoseFlag = 0: SafeMove = I
END IF
NEXT
BestMove = SafeMove
Evaluate = -LoseFlag
END FUNCTION
FUNCTION SpacesFilled%
FOR I = 0 TO 8
IF Board(I) = " " THEN SpacesFilled = 0: EXIT FUNCTION
NEXT I
SpacesFilled = -1
END FUNCTION
FUNCTION Win% (Piece AS STRING)
FOR I = 0 TO 7
IF Board(WinPos(I, 0)) = Piece AND Board(WinPos(I, 1)) = Piece AND Board(WinPos(I, 2)) = Piece THEN Win = -1: EXIT FUNCTION
NEXT I
Win = 0
END FUNCTION
RapidQ
' Tic-tac-toe
' Console application
DECLARE FUNCTION Win (Piece AS STRING) AS INTEGER
DECLARE FUNCTION SpacesFilled () AS INTEGER
DECLARE SUB ClearBoard ()
DECLARE SUB DisplayNumberedBoard ()
DECLARE SUB DisplayPiecedBoard ()
DECLARE FUNCTION Evaluate (Me AS STRING, Him AS STRING) AS INTEGER
DIM Board(8) AS STRING * 1, BestMove AS INTEGER
DIM WinPos(7, 2) AS INTEGER
DIM MyPiece AS STRING, HisPiece AS STRING
FOR I = 0 TO 7
FOR J = 0 TO 2
READ WinPos(I, J)
NEXT J
NEXT I
' Winning positions
DATA 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 3, 6, 1, 4, 7, 2, 5, 8, 0, 4, 8, 2, 4, 6
MyWinsCnt = 0: HisWinsCnt = 0: DrawsCnt = 0
CompFirst = -1 ' It be reversed, so human goes first
CLS
PRINT
PRINT " TIC-TAC-TOE"
PRINT
PRINT "In this version, X always goes first."
PRINT "The board is numbered:"
DO
CompFirst = NOT CompFirst ' reverse who goes first
MovesCnt = 0
PRINT
DisplayNumberedBoard
PRINT
PRINT IIF(CompFirst, "I go first.", "You go first.")
ClearBoard
IF CompFirst THEN MyPiece = "X": HisPiece = "O" ELSE MyPiece = "O": HisPiece = "X"
' -1: human; 1: computer; 0: nobody
Mover = IIF(CompFirst, 1, -1)
WHILE Mover <> 0
SELECT CASE Mover
CASE 1
IF MovesCnt = 0 THEN
BestMove = INT(RND * 9)
ELSEIF MovesCnt = 1 THEN
BestMove = IIF(Board(4) <> " ", INT(RND * 2) * 6 + INT(RND * 2) * 2, 4)
ELSE
T = Evaluate(MyPiece, HisPiece)
END IF
Board(BestMove) = MyPiece
INC(MovesCnt)
PRINT
CALL DisplayPiecedBoard
PRINT
IF Win(MyPiece) THEN
INC(MyWinsCnt)
PRINT "I win!"
Mover = 0
ELSEIF SpacesFilled THEN
INC(DrawsCnt)
PRINT "It's a draw. Thank you."
Mover = 0
ELSE
Mover = -1
END IF
CASE -1
DO
INPUT "Where do you move? ",I
IF I < 1 OR I > 9 THEN
PRINT "Illegal! ";
ELSEIF Board(I - 1) <> " " THEN
PRINT "Place already occupied. ";
ELSE
EXIT DO
END IF
LOOP
Board(I - 1) = HisPiece
INC(MovesCnt)
PRINT
CALL DisplayPiecedBoard
PRINT
IF Win(HisPiece) THEN
INC(HisWinsCnt)
PRINT "You beat me! Good game."
Mover = 0
ELSEIF SpacesFilled THEN
INC(DrawsCnt)
PRINT "It's a draw. Thank you."
Mover = 0
ELSE
Mover = 1
END IF
END SELECT
WEND
PRINT
INPUT "Another game (y/n)? ", Answ$
LOOP UNTIL UCASE$(Answ$) <> "Y"
PRINT
PRINT "Final score:"
PRINT "You won "; HisWinsCnt; " game"; IIF(HisWinsCnt <> 1, "s.", ".")
PRINT "I won "; MyWinsCnt; " game"; IIF(MyWinsCnt <> 1, "s.", ".")
PRINT "We tied "; DrawsCnt; " game"; IIF(DrawsCnt <> 1, "s.", ".")
PRINT "See you later!"
END
SUB ClearBoard
FOR I = 0 TO 8: Board(I) = " ": NEXT I
END SUB
SUB DisplayNumberedBoard
FOR I = 0 TO 8 STEP 3
PRINT " "; I + 1; " | "; I + 2; " | "; I + 3
IF I <> 6 THEN PRINT "---+---+---"
NEXT I
END SUB
SUB DisplayPiecedBoard
FOR I = 0 TO 8 STEP 3
PRINT " "; Board(I); " | "; Board(I + 1); " | "; Board(I + 2)
IF I <> 6 THEN PRINT "---+---+---"
NEXT I
END SUB
FUNCTION Evaluate (Me AS STRING, Him AS STRING)
' Recursive algorithm
DIM I AS INTEGER, SafeMove AS INTEGER, V AS INTEGER, LoseFlag AS INTEGER
IF Win(Me) THEN Evaluate = 1: EXIT FUNCTION
IF Win(Him) THEN Evaluate = -1: EXIT FUNCTION
IF SpacesFilled THEN Evaluate = 0: EXIT FUNCTION
LoseFlag = 1
I = 0
WHILE I <= 8
IF Board(I) = " " THEN
Board(I) = Me ' Try the move.
V = Evaluate(Him, Me)
Board(I) = " " ' Restore the empty space.
IF V = -1 THEN BestMove = I: Evaluate = 1: EXIT FUNCTION
IF V = 0 THEN LoseFlag = 0: SafeMove = I
END IF
INC(I)
WEND
BestMove = SafeMove
Evaluate = -LoseFlag
END FUNCTION
FUNCTION SpacesFilled
FOR I = 0 TO 8
IF Board(I) = " " THEN SpacesFilled = 0: EXIT FUNCTION
NEXT I
SpacesFilled = -1
END FUNCTION
FUNCTION Win (Piece AS STRING)
FOR I = 0 TO 7
IF Board(WinPos(I, 0)) = Piece AND Board(WinPos(I, 1)) = Piece AND Board(WinPos(I, 2)) = Piece THEN Win = -1: EXIT FUNCTION
NEXT I
Win = 0
END FUNCTION
Run BASIC
' ---------------------------
' TIC TAC TOE
' ---------------------------
winBox$ = "123 456 789 159 147 258 369 357"
boxPos$ = "123 231 456 564 789 897 159 591 357 753 132 465 798 174 285 396 159 471 582 693 147 258 369 195 375"
ai$ = "519628374"
ox$ = "OX"
[newGame]
for i = 1 to 9
box$(i) = ""
next i
goto [shoTic]
[loop]
for j = 1 to 2
tic$ = mid$(ox$,j,1)
for i = 1 to 25
b$ = word$(boxPos$,i," ")
b1 = val(mid$(b$,1,1))
b2 = val(mid$(b$,2,1))
b3 = val(mid$(b$,3,1))
if box$(b1) = tic$ AND box$(b2) = tic$ AND box$(b3) = "" then
box$(b3) = "O"
goto [shoTic]
end if
next i
next j
if box$(1) = "O" AND box$(5) = "X" and box$(9) = "X" then
if box$(3) = "" then
box$(3) = "O"
goto [shoTic]
end if
if box$(7) = "" then
box$(7) = "O"
goto [shoTic]
end if
end if
for i = 1 to 9
b1 = val(mid$(ai$,i,1))
if box$(b1) = "" then
box$(b1) = "O"
exit for
end if
next i
[shoTic]
cls
' ----------------------------------------
' show tic tac toe screen
' ----------------------------------------
html "<table border=1 width=300px height=225px><TR>"
for i = 1 to 9
html "<td align=center width=33%><h1>"
if box$(i) <> "" then
html box$(i)
else
button #box, " ";box$(i);" ", [doTic]
#box setkey(str$(i))
end if
if i mod 3 = 0 then html "</tr><tr>"
next i
html "</table>"
gosub [checkWin]
wait
[doTic]
box$(val(EventKey$)) = "X"
turn = 1
gosub [checkWin]
goto [loop]
' --- check for a winner ----------
[checkWin]
for i = 1 to 8
b$ = word$(winBox$,i," ")
b1 = val(mid$(b$,1,1))
b2 = val(mid$(b$,2,1))
b3 = val(mid$(b$,3,1))
if box$(b1) = "O" and box$(b2) = "O" and box$(b3) = "O" then
print "You Lose!"
goto [playAgain]
end if
if box$(b1) = "X" and box$(b2) = "X" and box$(b3) = "X" then
print "You Win!"
goto [playAgain]
end if
next i
moveCount = 0
for i = 1 to 9
if box$(i) <> "" then moveCount = moveCount + 1
next i
if moveCount = 9 then
print "Draw!"
goto [playAgain]
end if
RETURN
[playAgain]
input "Play again (y/n)";p$
if upper$(p$) = "Y" then goto [newGame]
end
Tiny BASIC
REM Tic-tac-toe for Tiny BASIC
REM
REM Released as public domain by Damian Gareth Walker, 2019
REM Created: 21-Sep-2019
REM --- Variables
REM A - first square in line examined
REM B - second square in line examined
REM C - third square in line examined
REM D - player whose pieces to count
REM E - number of DREM s pieces on a line
REM F - first square of line to examine
REM G - game winner
REM H - which side the human takes
REM I - increment for line to examine
REM L - line to examine
REM M - where to move (various uses)
REM N - piece found in a square
REM P - player currently playing
REM Q - square to examine
REM R-Z - contents of the board
REM --- Main Program
GOSUB 40
GOSUB 60
GOSUB 80
END
REM --- Subroutine to initialise the game
REM Outputs: H - Human play order
REM P - Whose turn it is
40 PRINT "Tic tac toe. Board positions are:"
PRINT " 1 2 3"
PRINT " 4 5 6"
PRINT " 7 8 9"
PRINT "Play first or second (1/2)?"
INPUT H
IF H<1 THEN GOTO 40
IF H>2 THEN GOTO 40
LET P=1
RETURN
REM --- Subroutine to take turns
REM Inputs: H - who is the human
REM P - whose turn it is
REM Outputs: G - who won the game
60 IF P=H THEN GOSUB 100
IF P<>H THEN GOSUB 120
GOSUB 200
IF G>0 THEN RETURN
LET P=3-P
IF R=0 THEN GOTO 60
IF S=0 THEN GOTO 60
IF T=0 THEN GOTO 60
IF U=0 THEN GOTO 60
IF V=0 THEN GOTO 60
IF W=0 THEN GOTO 60
IF X=0 THEN GOTO 60
IF Y=0 THEN GOTO 60
IF Z=0 THEN GOTO 60
RETURN
REM --- Victory
REM Inputs: H - which side was the human
REM P - player who won
80 IF G=H THEN PRINT "You win!"
IF G<>0 THEN IF G<>H THEN PRINT "Computer wins"
IF G=0 THEN PRINT "A draw"
RETURN
REM --- Subroutine to allow the player to move
REM Inputs: P - player number
REM Outputs: M - where the player wishes to move
100 PRINT "Move? "
INPUT Q
IF Q<1 THEN GOTO 100
IF Q>9 THEN GOTO 100
GOSUB 220
IF N<>0 THEN GOTO 100
LET M=Q
GOSUB 240
RETURN
REM --- Subroutine to make the computerREM s move
REM Inputs: P - player number
REM Outputs: M - the move chosen
120 LET M=0
LET D=3-H
GOSUB 145
IF M>0 THEN GOTO 135
LET D=H
GOSUB 145
IF M=0 THEN IF V=0 THEN LET M=5
IF M=0 THEN IF R=0 THEN LET M=1
IF M=0 THEN IF T=0 THEN LET M=3
IF M=0 THEN IF X=0 THEN LET M=7
IF M=0 THEN IF Z=0 THEN LET M=9
IF M=0 THEN IF S=0 THEN LET M=2
IF M=0 THEN IF U=0 THEN LET M=4
IF M=0 THEN IF Y=0 THEN LET M=8
IF M=0 THEN IF W=0 THEN LET M=6
135 GOSUB 240
PRINT "Computer move ",M
RETURN
REM --- Identify moves to win or avoid a loss
REM Inputs: D - player whose pieces weREM re counting
REM Changes: E - number of pieces on line being scanned
REM F - first square in winning line
REM I - increment of winning line
REM L - line being scanned (counter)
145 LET L=1
146 GOSUB 170
IF E<2 THEN GOTO 152
IF A=0 THEN LET M=F
IF B=0 THEN LET M=F+I
IF C=0 THEN LET M=F+I+I
IF M>0 THEN RETURN
152 LET L=L+1
IF L<9 THEN GOTO 146
RETURN
REM --- Count a playerREM s pieces on a line
REM Inputs: D - player whose pieces weREM re counting
REM L - line number
REM Changes: F - first square on the line
REM I - increment of the line
REM Q - individual squares to examine
REM Outputs: A - contents of first square
REM B - contents of second square
REM C - contents of third square
REM E - number of the playerREM s pieces
170 IF L>3 THEN GOTO 174
LET F=3*L-2
LET I=1
GOTO 180
174 IF L>6 THEN GOTO 178
LET F=L-3
LET I=3
GOTO 180
178 LET F=1+2*(L-7)
LET I=4-2*(L-7)
180 LET E=0
LET Q=F
GOSUB 220
LET A=N
IF N=D THEN LET E=E+1
LET Q=Q+I
GOSUB 220
LET B=N
IF N=D THEN LET E=E+1
LET Q=Q+I
GOSUB 220
LET C=N
IF N=D THEN LET E=E+1
RETURN
REM --- Subroutine to check for a win
REM Inputs: R-Z - board squares
REM Outputs: G - the winning player (0 for neither)
200 LET G=0
IF R>0 THEN IF R=S THEN IF S=T THEN LET G=R
IF U>0 THEN IF U=V THEN IF V=W THEN LET G=U
IF X>0 THEN IF X=Y THEN IF Y=Z THEN LET G=X
IF R>0 THEN IF R=U THEN IF U=X THEN LET G=R
IF S>0 THEN IF S=V THEN IF V=Y THEN LET G=S
IF T>0 THEN IF T=W THEN IF W=Z THEN LET G=T
IF R>0 THEN IF R=V THEN IF V=Z THEN LET G=R
IF T>0 THEN IF T=V THEN IF V=X THEN LET G=T
RETURN
REM --- Subroutine to see what piece is in a square
REM Inputs: Q - the square to check
REM R-Z - the contents of the squares
REM Outputs: N - the piece in that square
220 LET N=0
IF Q=1 THEN LET N=R
IF Q=2 THEN LET N=S
IF Q=3 THEN LET N=T
IF Q=4 THEN LET N=U
IF Q=5 THEN LET N=V
IF Q=6 THEN LET N=W
IF Q=7 THEN LET N=X
IF Q=8 THEN LET N=Y
IF Q=9 THEN LET N=Z
RETURN
REM --- Subroutine to put a piece in a square
REM Inputs: P - the player whose piece should be placed
REM M - the square to put the piece in
REM Changes: R-Z - the contents of the squares
240 IF M=1 THEN LET R=P
IF M=2 THEN LET S=P
IF M=3 THEN LET T=P
IF M=4 THEN LET U=P
IF M=5 THEN LET V=P
IF M=6 THEN LET W=P
IF M=7 THEN LET X=P
IF M=8 THEN LET Y=P
IF M=9 THEN LET Z=P
RETURN
VBA
Human play first with the "X". You must choose the row and the column you want to play...
Option Explicit
Private Lines(1 To 3, 1 To 3) As String
Private Nb As Byte, player As Byte
Private GameWin As Boolean, GameOver As Boolean
Sub Main_TicTacToe()
Dim p As String
InitLines
printLines Nb
Do
p = WhoPlay
Debug.Print p & " play"
If p = "Human" Then
Call HumanPlay
GameWin = IsWinner("X")
Else
Call ComputerPlay
GameWin = IsWinner("O")
End If
If Not GameWin Then GameOver = IsEnd
Loop Until GameWin Or GameOver
If Not GameOver Then
Debug.Print p & " Win !"
Else
Debug.Print "Game Over!"
End If
End Sub
Sub InitLines(Optional S As String)
Dim i As Byte, j As Byte
Nb = 0: player = 0
For i = LBound(Lines, 1) To UBound(Lines, 1)
For j = LBound(Lines, 2) To UBound(Lines, 2)
Lines(i, j) = "#"
Next j
Next i
End Sub
Sub printLines(Nb As Byte)
Dim i As Byte, j As Byte, strT As String
Debug.Print "Loop " & Nb
For i = LBound(Lines, 1) To UBound(Lines, 1)
For j = LBound(Lines, 2) To UBound(Lines, 2)
strT = strT & Lines(i, j)
Next j
Debug.Print strT
strT = vbNullString
Next i
End Sub
Function WhoPlay(Optional S As String) As String
If player = 0 Then
player = 1
WhoPlay = "Human"
Else
player = 0
WhoPlay = "Computer"
End If
End Function
Sub HumanPlay(Optional S As String)
Dim L As Byte, C As Byte, GoodPlay As Boolean
Do
L = Application.InputBox("Choose the row", "Numeric only", Type:=1)
If L > 0 And L < 4 Then
C = Application.InputBox("Choose the column", "Numeric only", Type:=1)
If C > 0 And C < 4 Then
If Lines(L, C) = "#" And Not Lines(L, C) = "X" And Not Lines(L, C) = "O" Then
Lines(L, C) = "X"
Nb = Nb + 1
printLines Nb
GoodPlay = True
End If
End If
End If
Loop Until GoodPlay
End Sub
Sub ComputerPlay(Optional S As String)
Dim L As Byte, C As Byte, GoodPlay As Boolean
Randomize Timer
Do
L = Int((Rnd * 3) + 1)
C = Int((Rnd * 3) + 1)
If Lines(L, C) = "#" And Not Lines(L, C) = "X" And Not Lines(L, C) = "O" Then
Lines(L, C) = "O"
Nb = Nb + 1
printLines Nb
GoodPlay = True
End If
Loop Until GoodPlay
End Sub
Function IsWinner(S As String) As Boolean
Dim i As Byte, j As Byte, Ch As String, strTL As String, strTC As String
Ch = String(UBound(Lines, 1), S)
'check lines & columns
For i = LBound(Lines, 1) To UBound(Lines, 1)
For j = LBound(Lines, 2) To UBound(Lines, 2)
strTL = strTL & Lines(i, j)
strTC = strTC & Lines(j, i)
Next j
If strTL = Ch Or strTC = Ch Then IsWinner = True: Exit For
strTL = vbNullString: strTC = vbNullString
Next i
'check diagonales
strTL = Lines(1, 1) & Lines(2, 2) & Lines(3, 3)
strTC = Lines(1, 3) & Lines(2, 2) & Lines(3, 1)
If strTL = Ch Or strTC = Ch Then IsWinner = True
End Function
Function IsEnd() As Boolean
Dim i As Byte, j As Byte
For i = LBound(Lines, 1) To UBound(Lines, 1)
For j = LBound(Lines, 2) To UBound(Lines, 2)
If Lines(i, j) = "#" Then Exit Function
Next j
Next i
IsEnd = True
End Function
- Output:
Loop 0 ### ### ### Human Play Loop 1 X## ### ### Computer Play Loop 2 X#O ### ### Human Play Loop 3 X#O #X# ### Computer Play Loop 4 XOO #X# ### Human Play Loop 5 XOO #X# ##X Human Win !
Yabasic
In the classic style.
5 REM Adaptation to Yabasic of the program published in Tim Hartnell's book "Artificial Intelligence: Concepts and Programs", with some minor modifications. 6/2018.
10 REM TICTAC
15 INPUT "English (0), Spanish (other key): " IDIOMA : IF NOT IDIOMA THEN RESTORE 2020 ELSE RESTORE 2010 END IF
20 GOSUB 1180: REM INICIALIZACION
30 REM *** REQUISITOS PREVIOS AL JUEGO ***
40 FOR J = 1 TO 9
50 A(J) = 32
60 NEXT J
70 FOR J = 1 TO 5
80 D(J) = 0
90 NEXT J
100 CCONTADOR = 0
110 R$ = ""
120 GOSUB 1070: REM IMPRESION DEL TABLERO
130 REM ** CICLO PRINCIPAL **
140 GOSUB 540: REM MOVIMIENTO DEL ORDENADOR
150 GOSUB 1070: REM IMPRESION DEL TABLERO
160 GOSUB 870: REM COMPRUEBA LA VICTORIA
170 IF R$ <> "" GOTO 240
180 GOSUB 980: REM SE ACEPTA EL MOVIMIENTO DE LA PERSONA
190 GOSUB 1070: REM IMPRESION DEL TABLERO
200 GOSUB 870: REM COMPRUEBA LA VICTORIA
210 IF R$ = "" GOTO 140
220 REM ** FIN DEL CICLO PRINCIPAL **
230 REM *****************************
240 REM FIN DEL JUEGO
250 GOSUB 1070: REM IMPRESION DEL TABLERO
260 PRINT: PRINT
270 IF R$ = "G" PRINT MENSAJE$(1): BANDERA = -1
280 IF R$ = "P" PRINT MENSAJE$(2): BANDERA = 1
290 IF R$ = "D" PRINT MENSAJE$(3): GOTO 430
300 REM ACTUALIZACION DE LA BASE DE DATOS
310 FOR B = 1 TO 5
320 FOR J = 2 TO 9
330 IF M(J) = D(B) GOSUB 370
340 NEXT J
350 NEXT B
360 GOTO 430
370 REM ** REORDENACION DE LOS ELEMENTOS DE LA MATRIZ M **
380 TEMP = M(J + BANDERA)
390 M(J + BANDERA) = M(J)
400 M(J) = TEMP
410 J = 9
420 RETURN
430 PRINT: PRINT
440 PRINT MENSAJE$(4)
450 PRINT: PRINT
460 FOR J = 1 TO 9
470 PRINT M(J), " ";
480 NEXT J
490 PRINT: PRINT
500 PRINT MENSAJE$(5)
510 INPUT A$
520 GOTO 30
530 REM ************************
540 REM MOVIMIENTO DEL ORDENADOR
550 P = ASC("O")
560 X = 0
570 J = 1
580 IF A(W(J)) = A(W(J + 1)) AND A(W(J + 2)) = 32 AND A(W(J)) = P X = W(J + 2): GOTO 750
590 IF A(W(J)) = A(W(J + 2)) AND A(W(J + 1)) = 32 AND A(W(J)) = P X = W(J + 1): GOTO 750
600 IF A(W(J + 1)) = A(W(J + 2)) AND A(W(J)) = 32 AND A(W(J + 1)) = P X = W(J): GOTO 750
610 IF J < 21 J = J + 3: GOTO 580
620 IF P = ASC("O") P = ASC("X"): GOTO 570
630 REM ** SI NO SE GANA SE BUSCA UN MOVIMIENTO DE BLOQUEO **
640 REM * ENTONCES SE USA LA SIGUIENTE SECCION *
650 J = 1
660 IF A(M(J)) = 32 X = M(J): GOTO 750
670 IF J < 10 J = J + 1: GOTO 660
680 H = 0
690 H = H + 1
700 X = INT(RAN(1) * 9): IF A(X) = 32 GOTO 750
710 IF H < 100 GOTO 690
720 R$ = "D": REM ES SIMPLEMENTE UN DIBUJO
730 RETURN
740 REM *********************
750 REM REALIZA EL MOVIMIENTO
760 A(X) = ASC("O")
770 CCONTADOR = CCONTADOR + 1
780 D(CCONTADOR) = X
790 BANDERA = 0
800 FOR J = 1 TO 9
810 IF A(J) = 32 BANDERA = 1
820 NEXT J
830 IF BANDERA = 0 AND R$ = "" R$ = "D"
840 REM SI TODAS LAS CASILLAS ESTAN LLENAS Y R$ ESTA VACIO, ENTONCES ES SIMPLEMENTE UN DIBUJO
850 RETURN
860 REM *********************
870 REM COMPRUEBA LA VICTORIA
880 J = 1
890 IF A(W(J)) = 32 J = J + 3
900 IF J > 23 RETURN
910 IF A(W(J)) = A(W(J + 1)) AND A(W(J)) = A(W(J + 2)) GOTO 940
920 IF J < 22 J = J + 3: GOTO 890
930 RETURN
940 IF A(W(J)) = ASC("O") R$ = "G": REM EL ORDENADOR GANA
950 IF A(W(J)) = ASC("X") R$ = "P": REM EL ORDENADOR PIERDE
960 RETURN
970 REM ************************
980 REM MOVIMIENTO DE LA PERSONA
990 PRINT: PRINT
1000 PRINT MENSAJE$(6)
1010 PRINT MENSAJE$(7); : INPUT MOVIMIENTO
1020 IF MOVIMIENTO < 1 OR MOVIMIENTO > 9 GOTO 1010
1030 IF A(MOVIMIENTO) <> 32 GOTO 1010
1040 A(MOVIMIENTO) = ASC("X")
1050 RETURN
1060 REM *********************
1070 REM IMPRESION DEL TABLERO
1080 CLEAR SCREEN
1090 PRINT: PRINT: PRINT
1100 PRINT " 1 : 2 : 3 ", CHR$(A(1)), " : ", CHR$(A(2)), " : ", CHR$(A(3))
1110 PRINT "----------- ------------"
1120 PRINT " 4 : 5 : 6 ", CHR$(A(4)), " : ", CHR$(A(5)), " : ", CHR$(A(6))
1130 PRINT "----------- ------------"
1140 PRINT " 7 : 8 : 9 ", CHR$(A(7)), " : ", CHR$(A(8)), " : ", CHR$(A(9))
1150 PRINT
1160 RETURN
1170 REM **************
1180 REM INICIALIZACION
1190 CLEAR SCREEN
1200 DIM A(9) : REM TABLERO
1210 DIM M(10) : REM ACCESO A LA BASE DE DATOS
1220 DIM W(24) : REM DATOS DE VICTORIA O BLOQUEO
1230 DIM D(5) : REM ACCESO AL MOVIMIENTO EN EL JUEGO ACTUAL
1235 DIM MENSAJE$(1) : READ M$ : N = TOKEN(M$,MENSAJE$(),",") : RESTORE
1240 REM DATOS DE VICTORIA O BLOQUEO
1250 FOR J = 1 TO 24
1260 READ W(J)
1270 NEXT J
1280 DATA 1, 2, 3, 4, 5, 6, 7, 8, 9
1290 DATA 1, 4, 7, 2, 5, 8, 3, 6, 9
1300 DATA 1, 5, 9, 3, 5, 7
1310 REM BASE INICIAL DE DATOS
1320 FOR J = 1 TO 10
1330 READ M(J)
1340 NEXT J
1350 DATA 2, 6, 8, 4, 7, 3, 1, 9, 5, 2
1360 RETURN
2000 REM MENSAJES EN ESPAÑOL
2010 DATA "YO GANO,TU GANAS,ES SIMPLEMENTE UN DIBUJO,ESTA ES MI PRIORIDAD ACTUALIZADA,PULSE LA TECLA <RETURN> PARA CONTINUAR,REALICE SU MOVIMIENTO,MOVIMIENTO: "
2020 DATA "I WIN,YOU WIN,IT'S JUST A DRAWING,THIS IS MY PRIORITY UPDATE,PRESS <RETURN> TO CONTINUE,TO MAKE YOUR MOVE,MOVEMENT: "
Batch File
This is just a game between two human players.
@echo off
setlocal enabledelayedexpansion
:newgame
set a1=1
set a2=2
set a3=3
set a4=4
set a5=5
set a6=6
set a7=7
set a8=8
set a9=9
set ll=X
set /a zz=0
:display1
cls
echo Player: %ll%
echo %a7%_%a8%_%a9%
echo %a4%_%a5%_%a6%
echo %a1%_%a2%_%a3%
set /p myt=Where would you like to go (choose a number from 1-9 and press enter)?
if !a%myt%! equ %myt% (
set a%myt%=%ll%
goto check
)
goto display1
:check
set /a zz=%zz%+1
if %zz% geq 9 goto newgame
if %a7%+%a8%+%a9% equ %ll%+%ll%+%ll% goto win
if %a4%+%a5%+%a6% equ %ll%+%ll%+%ll% goto win
if %a1%+%a2%+%a3% equ %ll%+%ll%+%ll% goto win
if %a7%+%a5%+%a3% equ %ll%+%ll%+%ll% goto win
if %a1%+%a5%+%a9% equ %ll%+%ll%+%ll% goto win
if %a7%+%a4%+%a1% equ %ll%+%ll%+%ll% goto win
if %a8%+%a5%+%a2% equ %ll%+%ll%+%ll% goto win
if %a9%+%a6%+%a3% equ %ll%+%ll%+%ll% goto win
goto %ll%
:X
set ll=O
goto display1
:O
set ll=X
goto display1
:win
echo %ll% wins!
pause
goto newgame
Advanced
This code makes a version of Tic Tac Toe with more features:
@ECHO OFF
:BEGIN
REM Skill level
set sl=
cls
echo Tic Tac Toe (Q to quit)
echo.
echo.
echo Pick your skill level (press a number)
echo.
echo (1) Children under 6
echo (2) Average Mental Case
echo (3) Oversized Ego
CHOICE /c:123q /n > nul
if errorlevel 4 goto end
if errorlevel 3 set sl=3
if errorlevel 3 goto layout
if errorlevel 2 set sl=2
if errorlevel 2 goto layout
set sl=1
:LAYOUT
REM Player turn ("x" or "o")
set pt=
REM Game winner ("x" or "o")
set gw=
REM No moves
set nm=
REM Set to one blank space after equal sign (check with cursor end)
set t1=
set t2=
set t3=
set t4=
set t5=
set t6=
set t7=
set t8=
set t9=
:UPDATE
cls
echo (S to set skill level) Tic Tac Toe (Q to quit)
echo.
echo You are the X player.
echo Press the number where you want to put an X.
echo.
echo Skill level %sl% 7 8 9
echo 4 5 6
echo 1 2 3
echo.
echo : :
echo %t1% : %t2% : %t3%
echo ....:...:....
echo %t4% : %t5% : %t6%
echo ....:...:....
echo %t7% : %t8% : %t9%
echo : :
if "%gw%"=="x" goto winx2
if "%gw%"=="o" goto wino2
if "%nm%"=="0" goto nomoves
:PLAYER
set pt=x
REM Layout is for keypad. Change CHOICE to "/c:123456789sq /n > nul"
REM for numbers to start at top left (also change user layout above).
CHOICE /c:789456123sq /n > nul
if errorlevel 11 goto end
if errorlevel 10 goto begin
if errorlevel 9 goto 9
if errorlevel 8 goto 8
if errorlevel 7 goto 7
if errorlevel 6 goto 6
if errorlevel 5 goto 5
if errorlevel 4 goto 4
if errorlevel 3 goto 3
if errorlevel 2 goto 2
goto 1
:1
REM Check if "x" or "o" already in square.
if "%t1%"=="x" goto player
if "%t1%"=="o" goto player
set t1=x
goto check
:2
if "%t2%"=="x" goto player
if "%t2%"=="o" goto player
set t2=x
goto check
:3
if "%t3%"=="x" goto player
if "%t3%"=="o" goto player
set t3=x
goto check
:4
if "%t4%"=="x" goto player
if "%t4%"=="o" goto player
set t4=x
goto check
:5
if "%t5%"=="x" goto player
if "%t5%"=="o" goto player
set t5=x
goto check
:6
if "%t6%"=="x" goto player
if "%t6%"=="o" goto player
set t6=x
goto check
:7
if "%t7%"=="x" goto player
if "%t7%"=="o" goto player
set t7=x
goto check
:8
if "%t8%"=="x" goto player
if "%t8%"=="o" goto player
set t8=x
goto check
:9
if "%t9%"=="x" goto player
if "%t9%"=="o" goto player
set t9=x
goto check
:COMPUTER
set pt=o
if "%sl%"=="1" goto skill1
REM (win corner to corner)
if "%t1%"=="o" if "%t3%"=="o" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t1%"=="o" if "%t9%"=="o" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t1%"=="o" if "%t7%"=="o" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t3%"=="o" if "%t7%"=="o" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t3%"=="o" if "%t9%"=="o" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t9%"=="o" if "%t7%"=="o" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
REM (win outside middle to outside middle)
if "%t2%"=="o" if "%t8%"=="o" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t4%"=="o" if "%t6%"=="o" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
REM (win all others)
if "%t1%"=="o" if "%t2%"=="o" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if "%t1%"=="o" if "%t5%"=="o" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t1%"=="o" if "%t4%"=="o" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t2%"=="o" if "%t5%"=="o" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%t3%"=="o" if "%t2%"=="o" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t3%"=="o" if "%t5%"=="o" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t3%"=="o" if "%t6%"=="o" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t4%"=="o" if "%t5%"=="o" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t6%"=="o" if "%t5%"=="o" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t7%"=="o" if "%t4%"=="o" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t7%"=="o" if "%t5%"=="o" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if "%t7%"=="o" if "%t8%"=="o" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t8%"=="o" if "%t5%"=="o" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t9%"=="o" if "%t8%"=="o" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t9%"=="o" if "%t5%"=="o" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t9%"=="o" if "%t6%"=="o" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
REM (block general attempts) -----------------------------------------------
if "%t1%"=="x" if "%t2%"=="x" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if "%t1%"=="x" if "%t5%"=="x" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t1%"=="x" if "%t4%"=="x" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t2%"=="x" if "%t5%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%t3%"=="x" if "%t2%"=="x" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t3%"=="x" if "%t5%"=="x" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t3%"=="x" if "%t6%"=="x" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t4%"=="x" if "%t5%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t6%"=="x" if "%t5%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t7%"=="x" if "%t4%"=="x" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t7%"=="x" if "%t5%"=="x" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if "%t7%"=="x" if "%t8%"=="x" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if "%t8%"=="x" if "%t5%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t9%"=="x" if "%t8%"=="x" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t9%"=="x" if "%t5%"=="x" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t9%"=="x" if "%t6%"=="x" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
REM (block obvious corner to corner)
if "%t1%"=="x" if "%t3%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t1%"=="x" if "%t9%"=="x" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t1%"=="x" if "%t7%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t3%"=="x" if "%t7%"=="x" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t3%"=="x" if "%t9%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t9%"=="x" if "%t7%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%sl%"=="2" goto skill2
REM (block sneaky corner to corner 2-4, 2-6, etc.)
if "%t2%"=="x" if "%t4%"=="x" if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if "%t2%"=="x" if "%t6%"=="x" if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if "%t8%"=="x" if "%t4%"=="x" if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if "%t8%"=="x" if "%t6%"=="x" if not "%t9%"=="x" if not "%t9%"=="o" goto c9
REM (block offset corner trap 1-8, 1-6, etc.)
if "%t1%"=="x" if "%t6%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%t1%"=="x" if "%t8%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t3%"=="x" if "%t8%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t3%"=="x" if "%t4%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%t9%"=="x" if "%t4%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t9%"=="x" if "%t2%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t7%"=="x" if "%t2%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t7%"=="x" if "%t6%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
:SKILL2
REM (block outside middle to outside middle)
if "%t2%"=="x" if "%t8%"=="x" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if "%t4%"=="x" if "%t6%"=="x" if not "%t5%"=="x" if not "%t5%"=="o" goto c5
REM (block 3 corner trap)
if "%t1%"=="x" if "%t9%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t3%"=="x" if "%t7%"=="x" if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if "%t1%"=="x" if "%t9%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t3%"=="x" if "%t7%"=="x" if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if "%t1%"=="x" if "%t9%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t3%"=="x" if "%t7%"=="x" if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if "%t1%"=="x" if "%t9%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
if "%t3%"=="x" if "%t7%"=="x" if not "%t8%"=="x" if not "%t8%"=="o" goto c8
:SKILL1
REM (just take a turn)
if not "%t5%"=="x" if not "%t5%"=="o" goto c5
if not "%t1%"=="x" if not "%t1%"=="o" goto c1
if not "%t3%"=="x" if not "%t3%"=="o" goto c3
if not "%t7%"=="x" if not "%t7%"=="o" goto c7
if not "%t9%"=="x" if not "%t9%"=="o" goto c9
if not "%t2%"=="x" if not "%t2%"=="o" goto c2
if not "%t4%"=="x" if not "%t4%"=="o" goto c4
if not "%t6%"=="x" if not "%t6%"=="o" goto c6
if not "%t8%"=="x" if not "%t8%"=="o" goto c8
set nm=0
goto update
:C1
set t1=o
goto check
:C2
set t2=o
goto check
:C3
set t3=o
goto check
:C4
set t4=o
goto check
:C5
set t5=o
goto check
:C6
set t6=o
goto check
:C7
set t7=o
goto check
:C8
set t8=o
goto check
:C9
set t9=o
goto check
:CHECK
if "%t1%"=="x" if "%t2%"=="x" if "%t3%"=="x" goto winx
if "%t4%"=="x" if "%t5%"=="x" if "%t6%"=="x" goto winx
if "%t7%"=="x" if "%t8%"=="x" if "%t9%"=="x" goto winx
if "%t1%"=="x" if "%t4%"=="x" if "%t7%"=="x" goto winx
if "%t2%"=="x" if "%t5%"=="x" if "%t8%"=="x" goto winx
if "%t3%"=="x" if "%t6%"=="x" if "%t9%"=="x" goto winx
if "%t1%"=="x" if "%t5%"=="x" if "%t9%"=="x" goto winx
if "%t3%"=="x" if "%t5%"=="x" if "%t7%"=="x" goto winx
if "%t1%"=="o" if "%t2%"=="o" if "%t3%"=="o" goto wino
if "%t4%"=="o" if "%t5%"=="o" if "%t6%"=="o" goto wino
if "%t7%"=="o" if "%t8%"=="o" if "%t9%"=="o" goto wino
if "%t1%"=="o" if "%t4%"=="o" if "%t7%"=="o" goto wino
if "%t2%"=="o" if "%t5%"=="o" if "%t8%"=="o" goto wino
if "%t3%"=="o" if "%t6%"=="o" if "%t9%"=="o" goto wino
if "%t1%"=="o" if "%t5%"=="o" if "%t9%"=="o" goto wino
if "%t3%"=="o" if "%t5%"=="o" if "%t7%"=="o" goto wino
if "%pt%"=="x" goto computer
if "%pt%"=="o" goto update
:WINX
set gw=x
goto update
:WINX2
echo You win!
echo Play again (Y,N)?
CHOICE /c:ynsq /n > nul
if errorlevel 4 goto end
if errorlevel 3 goto begin
if errorlevel 2 goto end
goto layout
:WINO
set gw=o
goto update
:WINO2
echo Sorry, You lose.
echo Play again (Y,N)?
CHOICE /c:ynsq /n > nul
if errorlevel 4 goto end
if errorlevel 3 goto begin
if errorlevel 2 goto end
goto layout
:NOMOVES
echo There are no more moves left!
echo Play again (Y,N)?
CHOICE /c:ynsq /n > nul
if errorlevel 4 goto end
if errorlevel 3 goto begin
if errorlevel 2 goto end
goto layout
:END
cls
echo Tic Tac Toe
echo.
REM Clear all variables (no spaces after equal sign).
set gw=
set nm=
set sl=
set pt=
set t1=
set t2=
set t3=
set t4=
set t5=
set t6=
set t7=
set t8=
set t9=
Befunge
Requires an intepreter with working support for numeric input, which unfortunately excludes most online implementations.
Plays reasonably well, but not perfectly, so can be beaten.
v123456789 --- >9 >48*,:55+\-0g,1v
>9>066+0p076+0p^ ^,," |"_v#%3:- <
:,,0537051v>:#,_$#^5#,5#+<>:#v_55+
74 1098709<^+55"---+---+---"0<v520
69 04560123 >:!#v_0\1v>$2-:6%v>803
6 +0g\66++0p^ $_>#% v#9:-1_ 6/5
5 vv5!/*88\%*28 ::g0_^>9/#v_ "I",
,,5v>5++0p82*/3-:*+\:^v,_@ >"uoY",
0+5<v0+66_v#!%2:_55v >:^:" win!"\
1-^ g >$>0" :evom ruoY">:#,_$v>p
\*8+ 65_^#!/*88g0** `0\!`9:::<&<^0
v >:!67+0g:!56+0g *+*+0" :evom "
>"yM">:#,_$ :. 1234+++, 789*+ \0^<
"a s't"98:*+>:#,_$@>365*+"ward"48*
- Output:
1 | 2 | 3 ---+---+--- 4 | 5 | 6 ---+---+--- 7 | 8 | 9 Your move: 1 X | 2 | 3 ---+---+--- 4 | 5 | 6 ---+---+--- 7 | 8 | 9 My move: 5 X | 2 | 3 ---+---+--- 4 | O | 6 ---+---+--- 7 | 8 | 9 Your move: 2 X | X | 3 ---+---+--- 4 | O | 6 ---+---+--- 7 | 8 | 9 My move: 3 X | X | O ---+---+--- 4 | O | 6 ---+---+--- 7 | 8 | 9 Your move:
C
Opening alternates between human and computer. Computer never loses.
#include <stdio.h>
#include <stdlib.h>
int b[3][3]; /* board. 0: blank; -1: computer; 1: human */
int check_winner()
{
int i;
for (i = 0; i < 3; i++) {
if (b[i][0] && b[i][1] == b[i][0] && b[i][2] == b[i][0])
return b[i][0];
if (b[0][i] && b[1][i] == b[0][i] && b[2][i] == b[0][i])
return b[0][i];
}
if (!b[1][1]) return 0;
if (b[1][1] == b[0][0] && b[2][2] == b[0][0]) return b[0][0];
if (b[1][1] == b[2][0] && b[0][2] == b[1][1]) return b[1][1];
return 0;
}
void showboard()
{
const char *t = "X O";
int i, j;
for (i = 0; i < 3; i++, putchar('\n'))
for (j = 0; j < 3; j++)
printf("%c ", t[ b[i][j] + 1 ]);
printf("-----\n");
}
#define for_ij for (i = 0; i < 3; i++) for (j = 0; j < 3; j++)
int best_i, best_j;
int test_move(int val, int depth)
{
int i, j, score;
int best = -1, changed = 0;
if ((score = check_winner())) return (score == val) ? 1 : -1;
for_ij {
if (b[i][j]) continue;
changed = b[i][j] = val;
score = -test_move(-val, depth + 1);
b[i][j] = 0;
if (score <= best) continue;
if (!depth) {
best_i = i;
best_j = j;
}
best = score;
}
return changed ? best : 0;
}
const char* game(int user)
{
int i, j, k, move, win = 0;
for_ij b[i][j] = 0;
printf("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n");
printf("You have O, I have X.\n\n");
for (k = 0; k < 9; k++, user = !user) {
while(user) {
printf("your move: ");
if (!scanf("%d", &move)) {
scanf("%*s");
continue;
}
if (--move < 0 || move >= 9) continue;
if (b[i = move / 3][j = move % 3]) continue;
b[i][j] = 1;
break;
}
if (!user) {
if (!k) { /* randomize if computer opens, less boring */
best_i = rand() % 3;
best_j = rand() % 3;
} else
test_move(-1, 0);
b[best_i][best_j] = -1;
printf("My move: %d\n", best_i * 3 + best_j + 1);
}
showboard();
if ((win = check_winner()))
return win == 1 ? "You win.\n\n": "I win.\n\n";
}
return "A draw.\n\n";
}
int main()
{
int first = 0;
while (1) printf("%s", game(first = !first));
return 0;
}
C#
This implementation is purposely wordy because Tic-Tac-Toe is often a starting level program.
It tries to show a number of C# code features while still keeping each function small and understandable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RosettaTicTacToe
{
class Program
{
/*================================================================
*Pieces (players and board)
*================================================================*/
static string[][] Players = new string[][] {
new string[] { "COMPUTER", "X" }, // computer player
new string[] { "HUMAN", "O" } // human player
};
const int Unplayed = -1;
const int Computer = 0;
const int Human = 1;
// GameBoard holds index into Players[] (0 or 1) or Unplayed (-1) if location not yet taken
static int[] GameBoard = new int[9];
static int[] corners = new int[] { 0, 2, 6, 8 };
static int[][] wins = new int[][] {
new int[] { 0, 1, 2 }, new int[] { 3, 4, 5 }, new int[] { 6, 7, 8 },
new int[] { 0, 3, 6 }, new int[] { 1, 4, 7 }, new int[] { 2, 5, 8 },
new int[] { 0, 4, 8 }, new int[] { 2, 4, 6 } };
/*================================================================
*Main Game Loop (this is what runs/controls the game)
*================================================================*/
static void Main(string[] args)
{
while (true)
{
Console.Clear();
Console.WriteLine("Welcome to Rosetta Code Tic-Tac-Toe for C#.");
initializeGameBoard();
displayGameBoard();
int currentPlayer = rnd.Next(0, 2); // current player represented by Players[] index of 0 or 1
Console.WriteLine("The first move goes to {0} who is playing {1}s.\n", playerName(currentPlayer), playerToken(currentPlayer));
while (true)
{
int thisMove = getMoveFor(currentPlayer);
if (thisMove == Unplayed)
{
Console.WriteLine("{0}, you've quit the game ... am I that good?", playerName(currentPlayer));
break;
}
playMove(thisMove, currentPlayer);
displayGameBoard();
if (isGameWon())
{
Console.WriteLine("{0} has won the game!", playerName(currentPlayer));
break;
}
else if (isGameTied())
{
Console.WriteLine("Cat game ... we have a tie.");
break;
}
currentPlayer = getNextPlayer(currentPlayer);
}
if (!playAgain())
return;
}
}
/*================================================================
*Move Logic
*================================================================*/
static int getMoveFor(int player)
{
if (player == Human)
return getManualMove(player);
else
{
//int selectedMove = getManualMove(player);
//int selectedMove = getRandomMove(player);
int selectedMove = getSemiRandomMove(player);
//int selectedMove = getBestMove(player);
Console.WriteLine("{0} selects position {1}.", playerName(player), selectedMove + 1);
return selectedMove;
}
}
static int getManualMove(int player)
{
while (true)
{
Console.Write("{0}, enter you move (number): ", playerName(player));
ConsoleKeyInfo keyInfo = Console.ReadKey();
Console.WriteLine(); // keep the display pretty
if (keyInfo.Key == ConsoleKey.Escape)
return Unplayed;
if (keyInfo.Key >= ConsoleKey.D1 && keyInfo.Key <= ConsoleKey.D9)
{
int move = keyInfo.KeyChar - '1'; // convert to between 0..8, a GameBoard index position.
if (GameBoard[move] == Unplayed)
return move;
else
Console.WriteLine("Spot {0} is already taken, please select again.", move + 1);
}
else
Console.WriteLine("Illegal move, please select again.\n");
}
}
static int getRandomMove(int player)
{
int movesLeft = GameBoard.Count(position => position == Unplayed);
int x = rnd.Next(0, movesLeft);
for (int i = 0; i < GameBoard.Length; i++) // walk board ...
{
if (GameBoard[i] == Unplayed && x < 0) // until we reach the unplayed move.
return i;
x--;
}
return Unplayed;
}
// plays random if no winning move or needed block.
static int getSemiRandomMove(int player)
{
int posToPlay;
if (checkForWinningMove(player, out posToPlay))
return posToPlay;
if (checkForBlockingMove(player, out posToPlay))
return posToPlay;
return getRandomMove(player);
}
// purposely not implemented (this is the thinking part).
static int getBestMove(int player)
{
return -1;
}
static bool checkForWinningMove(int player, out int posToPlay)
{
posToPlay = Unplayed;
foreach (var line in wins)
if (twoOfThreeMatchPlayer(player, line, out posToPlay))
return true;
return false;
}
static bool checkForBlockingMove(int player, out int posToPlay)
{
posToPlay = Unplayed;
foreach (var line in wins)
if (twoOfThreeMatchPlayer(getNextPlayer(player), line, out posToPlay))
return true;
return false;
}
static bool twoOfThreeMatchPlayer(int player, int[] line, out int posToPlay)
{
int cnt = 0;
posToPlay = int.MinValue;
foreach (int pos in line)
{
if (GameBoard[pos] == player)
cnt++;
else if (GameBoard[pos] == Unplayed)
posToPlay = pos;
}
return cnt == 2 && posToPlay >= 0;
}
static void playMove(int boardPosition, int player)
{
GameBoard[boardPosition] = player;
}
static bool isGameWon()
{
return wins.Any(line => takenBySamePlayer(line[0], line[1], line[2]));
}
static bool takenBySamePlayer(int a, int b, int c)
{
return GameBoard[a] != Unplayed && GameBoard[a] == GameBoard[b] && GameBoard[a] == GameBoard[c];
}
static bool isGameTied()
{
return !GameBoard.Any(spot => spot == Unplayed);
}
/*================================================================
*Misc Methods
*================================================================*/
static Random rnd = new Random();
static void initializeGameBoard()
{
for (int i = 0; i < GameBoard.Length; i++)
GameBoard[i] = Unplayed;
}
static string playerName(int player)
{
return Players[player][0];
}
static string playerToken(int player)
{
return Players[player][1];
}
static int getNextPlayer(int player)
{
return (player + 1) % 2;
}
static void displayGameBoard()
{
Console.WriteLine(" {0} | {1} | {2}", pieceAt(0), pieceAt(1), pieceAt(2));
Console.WriteLine("---|---|---");
Console.WriteLine(" {0} | {1} | {2}", pieceAt(3), pieceAt(4), pieceAt(5));
Console.WriteLine("---|---|---");
Console.WriteLine(" {0} | {1} | {2}", pieceAt(6), pieceAt(7), pieceAt(8));
Console.WriteLine();
}
static string pieceAt(int boardPosition)
{
if (GameBoard[boardPosition] == Unplayed)
return (boardPosition + 1).ToString(); // display 1..9 on board rather than 0..8
return playerToken(GameBoard[boardPosition]);
}
private static bool playAgain()
{
Console.WriteLine("\nDo you want to play again?");
return Console.ReadKey(false).Key == ConsoleKey.Y;
}
}
}
- Output:
Welcome to Rosetta Code Tic-Tac-Toe for C#. 1 | 2 | 3 ---|---|--- 4 | 5 | 6 ---|---|--- 7 | 8 | 9 The first move goes to HUMAN who is playing Os. HUMAN, enter you move (number): 5 1 | 2 | 3 ---|---|--- 4 | O | 6 ---|---|--- 7 | 8 | 9 COMPUTER selects position 7. 1 | 2 | 3 ---|---|--- 4 | O | 6 ---|---|--- X | 8 | 9 HUMAN, enter you move (number): 0 Illegal move, please select again. HUMAN, enter you move (number): 1 O | 2 | 3 ---|---|--- 4 | O | 6 ---|---|--- X | 8 | 9 COMPUTER selects position 9. O | 2 | 3 ---|---|--- 4 | O | 6 ---|---|--- X | 8 | X HUMAN, enter you move (number):
C++
#include <windows.h>
#include <iostream>
#include <string>
//--------------------------------------------------------------------------------------------------
using namespace std;
//--------------------------------------------------------------------------------------------------
enum players { Computer, Human, Draw, None };
const int iWin[8][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, { 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, { 0, 4, 8 }, { 2, 4, 6 } };
//--------------------------------------------------------------------------------------------------
class ttt
{
public:
ttt() { _p = rand() % 2; reset(); }
void play()
{
int res = Draw;
while( true )
{
drawGrid();
while( true )
{
if( _p ) getHumanMove();
else getComputerMove();
drawGrid();
res = checkVictory();
if( res != None ) break;
++_p %= 2;
}
if( res == Human ) cout << "CONGRATULATIONS HUMAN --- You won!";
else if( res == Computer ) cout << "NOT SO MUCH A SURPRISE --- I won!";
else cout << "It's a draw!";
cout << endl << endl;
string r;
cout << "Play again( Y / N )? "; cin >> r;
if( r != "Y" && r != "y" ) return;
++_p %= 2;
reset();
}
}
private:
void reset()
{
for( int x = 0; x < 9; x++ )
_field[x] = None;
}
void drawGrid()
{
system( "cls" );
COORD c = { 0, 2 };
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
cout << " 1 | 2 | 3 " << endl;
cout << "---+---+---" << endl;
cout << " 4 | 5 | 6 " << endl;
cout << "---+---+---" << endl;
cout << " 7 | 8 | 9 " << endl << endl << endl;
int f = 0;
for( int y = 0; y < 5; y += 2 )
for( int x = 1; x < 11; x += 4 )
{
if( _field[f] != None )
{
COORD c = { x, 2 + y };
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
string o = _field[f] == Computer ? "X" : "O";
cout << o;
}
f++;
}
c.Y = 9;
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
}
int checkVictory()
{
for( int i = 0; i < 8; i++ )
{
if( _field[iWin[i][0]] != None &&
_field[iWin[i][0]] == _field[iWin[i][1]] && _field[iWin[i][1]] == _field[iWin[i][2]] )
{
return _field[iWin[i][0]];
}
}
int i = 0;
for( int f = 0; f < 9; f++ )
{
if( _field[f] != None )
i++;
}
if( i == 9 ) return Draw;
return None;
}
void getHumanMove()
{
int m;
cout << "Enter your move ( 1 - 9 ) ";
while( true )
{
m = 0;
do
{ cin >> m; }
while( m < 1 && m > 9 );
if( _field[m - 1] != None )
cout << "Invalid move. Try again!" << endl;
else break;
}
_field[m - 1] = Human;
}
void getComputerMove()
{
int move = 0;
do{ move = rand() % 9; }
while( _field[move] != None );
for( int i = 0; i < 8; i++ )
{
int try1 = iWin[i][0], try2 = iWin[i][1], try3 = iWin[i][2];
if( _field[try1] != None && _field[try1] == _field[try2] && _field[try3] == None )
{
move = try3;
if( _field[try1] == Computer ) break;
}
if( _field[try1] != None && _field[try1] == _field[try3] && _field[try2] == None )
{
move = try2;
if( _field[try1] == Computer ) break;
}
if( _field[try2] != None && _field[try2] == _field[try3] && _field[try1] == None )
{
move = try1;
if( _field[try2] == Computer ) break;
}
}
_field[move] = Computer;
}
int _p;
int _field[9];
};
//--------------------------------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
srand( GetTickCount() );
ttt tic;
tic.play();
return 0;
}
//--------------------------------------------------------------------------------------------------
- Output:
Computer plays 'X' and human plays 'O'
1 | 2 | X ---+---+--- X | 5 | 6 ---+---+--- 7 | O | 9 Enter your move ( 1 - 9 )
Common Lisp
(defun generate-board ()
(loop repeat 9 collect nil))
(defparameter *straights* '((1 2 3) (4 5 6) (7 8 9) (1 4 7) (2 5 8) (3 6 9) (1 5 9) (3 5 7)))
(defparameter *current-player* 'x)
(defun get-board-elt (n board)
(nth (1- n) board))
(defun legal-p (n board)
(null (get-board-elt n board)))
(defun set-board-elt (n board symbol)
(if (legal-p n board)
(setf (nth (1- n) board) symbol)
(progn (format t "Illegal move. Try again.~&")
(set-board-elt (read) board symbol))))
(defun list-legal-moves (board)
(loop for i from 1 to (length board)
when (legal-p i board)
collect i))
(defun get-random-element (lst)
(nth (random (length lst)) lst))
(defun multi-non-nil-eq (lst)
(and (notany #'null lst)
(notany #'null (mapcar #'(lambda (x) (eq (car lst) x)) lst))
(car lst)))
(defun elements-of-straights (board)
(loop for i in *straights*
collect (loop for j from 0 to 2
collect (get-board-elt (nth j i) board))))
(defun find-winner (board)
(car (remove-if #'null (mapcar #'multi-non-nil-eq (elements-of-straights board)))))
(defun set-player (mark)
(format t "Shall a computer play as ~a? (y/n)~&" mark)
(let ((response (read)))
(cond ((equalp response 'y) t)
((equalp response 'n) nil)
(t (format t "Come again?~&")
(set-player mark)))))
(defun player-move (board symbol)
(format t "~%Player ~a, please input your move.~&" symbol)
(set-board-elt (read) board symbol)
(format t "~%"))
(defun computer-move (board symbol)
(let ((move (get-random-element (list-legal-moves board))))
(set-board-elt move board symbol)
(format t "~%computer selects ~a~%~%" move)))
(defun computer-move-p (current-player autoplay-x-p autoplay-o-p)
(if (eq current-player 'x)
autoplay-x-p
autoplay-o-p))
(defun perform-turn (current-player board autoplay-x-p autoplay-o-p)
(if (computer-move-p current-player autoplay-x-p autoplay-o-p)
(computer-move board current-player)
(player-move board current-player)))
(defun switch-player ()
(if (eq *current-player* 'x)
(setf *current-player* 'o)
(setf *current-player* 'x)))
(defun display-board (board)
(loop for i downfrom 2 to 0
do (loop for j from 1 to 3
initially (format t "|")
do (format t "~a|" (or (get-board-elt (+ (* 3 i) j) board) (+ (* 3 i) j)))
finally (format t "~&"))))
(defun tic-tac-toe ()
(setf *current-player* 'x)
(let ((board (generate-board))
(autoplay-x-p (set-player 'x))
(autoplay-o-p (set-player 'o)))
(format t "~%")
(loop until (or (find-winner board) (null (list-legal-moves board)))
do (display-board board)
do (perform-turn *current-player* board autoplay-x-p autoplay-o-p)
do (switch-player)
finally (if (find-winner board)
(format t "The winner is ~a!" (find-winner board))
(format t "It's a tie.")))))
- Output:
CL-USER> (tic-tac-toe) Shall a computer play as X? (y/n) n Shall a computer play as O? (y/n) y |7|8|9| |4|5|6| |1|2|3| Player X, please input your move. 5 |7|8|9| |4|X|6| |1|2|3| computer selects 8 |7|O|9| |4|X|6| |1|2|3| Player X, please input your move.
D
import std.stdio, std.string, std.algorithm, std.conv, std.random,
std.ascii, std.array, std.range, std.math;
struct GameBoard {
dchar[9] board = "123456789";
enum : dchar { human = 'X', computer = 'O' }
enum Game { going, humanWins, computerWins, draw }
const pure nothrow @safe @nogc invariant() {
int nHuman = 0, nComputer = 0;
foreach (immutable i, immutable c; board)
if (c.isDigit)
assert(i == c - '1'); // In correct position?
else {
assert(c == human || c == computer);
(c == human ? nHuman : nComputer)++;
}
assert(abs(nHuman - nComputer) <= 1);
}
string toString() const pure {
return format("%(%-(%s|%)\n-+-+-\n%)", board[].chunks(3));
}
bool isAvailable(in int i) const pure nothrow @safe @nogc {
return i >= 0 && i < 9 && board[i].isDigit;
}
auto availablePositions() const pure nothrow @safe /*@nogc*/ {
return 9.iota.filter!(i => isAvailable(i));
}
Game winner() const pure nothrow @safe /*@nogc*/ {
static immutable wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]];
foreach (immutable win; wins) {
immutable bw0 = board[win[0]];
if (bw0.isDigit)
continue; // Nobody wins on this one.
if (bw0 == board[win[1]] && bw0 == board[win[2]])
return bw0 == GameBoard.human ?
Game.humanWins :
Game.computerWins;
}
return availablePositions.empty ? Game.draw: Game.going;
}
bool isFinished() const pure nothrow @safe /*@nogc*/ {
return winner != Game.going;
}
int computerMove() const // Random move.
out(res) {
assert(res >= 0 && res < 9 && isAvailable(res));
} body {
// return availablePositions.array.choice;
return availablePositions.array[uniform(0, $)];
}
}
GameBoard playGame() {
GameBoard board;
bool playsHuman = true;
while (!board.isFinished) {
board.writeln;
int move;
if (playsHuman) {
do {
writef("Your move (available moves: %s)? ",
board.availablePositions.map!q{ a + 1 });
readf("%d\n", &move);
move--; // Zero based indexing.
if (move < 0)
return board;
} while (!board.isAvailable(move));
} else
move = board.computerMove;
assert(board.isAvailable(move));
writefln("\n%s chose %d", playsHuman ? "You" : "I", move + 1);
board.board[move] = playsHuman ? GameBoard.human :
GameBoard.computer;
playsHuman = !playsHuman; // Switch player.
}
return board;
}
void main() {
"Tic-tac-toe game player.\n".writeln;
immutable outcome = playGame.winner;
final switch (outcome) {
case GameBoard.Game.going:
"Game stopped.".writeln;
break;
case GameBoard.Game.humanWins:
"\nYou win!".writeln;
break;
case GameBoard.Game.computerWins:
"\nI win.".writeln;
break;
case GameBoard.Game.draw:
"\nDraw".writeln;
break;
}
}
- Output:
Tic-tac-toe game player. 1|2|3 -+-+- 4|5|6 -+-+- 7|8|9 Your move (available moves: [1, 2, 3, 4, 5, 6, 7, 8, 9])? 1 You chose 1 X|2|3 -+-+- 4|5|6 -+-+- 7|8|9 I chose 2 X|O|3 -+-+- 4|5|6 -+-+- 7|8|9 Your move (available moves: [3, 4, 5, 6, 7, 8, 9])? 5 You chose 5 X|O|3 -+-+- 4|X|6 -+-+- 7|8|9 I chose 3 X|O|O -+-+- 4|X|6 -+-+- 7|8|9 Your move (available moves: [4, 6, 7, 8, 9])? 9 You chose 9 You win!
Delphi
This is a GUI, event driven version of the game. The game board is a "TStringGrid" component, which has a grid that can hold and display characters. In this case, the grid displays Xs and Ox using a large font.
When the user clicks on the grid, this generates an event that is handled by the program. The mouse click coordinates are converted to a cell coordinates and a large X is placed in the box. At this point program tests if this resulted in a win. Next it calculate the computer's response and it checks to see if the computer won. The process is repeated until there is a winner or a draw.
{Array contain possiple winning lines}
type TWinLine = array [0..3-1] of TPoint;
var WinLines: array [0..8-1] of TWinLine = (
((X:0; Y:0), (X:1; Y:0), (X:2; Y:0)),
((X:0; Y:1), (X:1; Y:1), (X:2; Y:1)),
((X:0; Y:2), (X:1; Y:2), (X:2; Y:2)),
((X:0; Y:0), (X:0; Y:1), (X:0; Y:2)),
((X:1; Y:0), (X:1; Y:1), (X:1; Y:2)),
((X:2; Y:0), (X:2; Y:1), (X:2; Y:2)),
((X:0; Y:0), (X:1; Y:1), (X:2; Y:2)),
((X:2; Y:0), (X:1; Y:1), (X:0; Y:2))
);
{Array containing all characters in a line}
type TCellArray = array [0..3-1] of char;
var WinLineInx: integer;
var GameOver: boolean;
procedure ClearGrid;
{Clear TTT Grid by setting all cells to a space}
var X,Y: integer;
begin
with TicTacToeDlg do
begin
for Y:=0 to GameGrid.RowCount-1 do
for X:=0 to GameGrid.ColCount-1 do GameGrid.Cells[X,Y]:=' ';
WinLineInx:=-1;
Status1Dis.Caption:='';
GameOver:=False;
end;
end;
function FirstEmptyCell(var P: TPoint): boolean;
{Find first empty grid i.e. the one containing a space}
{Returns false if there are no empty cells (a tie game)}
var X,Y: integer;
begin
Result:=True;
with TicTacToeDlg do
begin
{Iterate through all cells in array}
for Y:=0 to GameGrid.RowCount-1 do
for X:=0 to GameGrid.ColCount-1 do
if GameGrid.Cells[X,Y]=' ' then
begin
P.X:=X; P.Y:=Y;
Exit;
end;
end;
Result:=False;
end;
procedure GetLineChars(Inx: integer; var CA: TCellArray);
{Get all the characters in a specific win line}
var P1,P2,P3: TPoint;
begin
with TicTacToeDlg do
begin
{Get cell position of specific win line}
P1:=WinLines[Inx,0];
P2:=WinLines[Inx,1];
P3:=WinLines[Inx,2];
{Get the characters from each and put in array}
CA[0]:=GameGrid.Cells[P1.X,P1.Y][1];
CA[1]:=GameGrid.Cells[P2.X,P2.Y][1];
CA[2]:=GameGrid.Cells[P3.X,P3.Y][1];
end;
end;
function IsWinner(var Inx: integer; var C: char): boolean;
{Test if the specified line is a winner}
{Return index of line and the char of winner}
var I,J: integer;
var CA: TCellArray;
begin
with TicTacToeDlg do
begin
Result:=False;
{Go through all winning patterns}
for J:=0 to High(WinLines) do
begin
{Get one winning pattern}
GetLineChars(J,CA);
{Look for line that has the same char in all three places}
if (CA[0]<>' ') and (CA[0]=CA[1]) and (CA[0]=CA[2]) then
begin
Result:=True;
Inx:=J;
C:=CA[0];
end;
end;
end;
end;
procedure DrawWinLine(Inx: integer);
{Draw line through winning squares}
var Line: TWinLine;
var C1,C2: TPoint;
var P1,P2: TPoint;
var W2,H2: integer;
begin
with TicTacToeDlg do
begin
{Offset to center of cell}
W2:=GameGrid.ColWidths[0] div 2;
H2:=GameGrid.RowHeights[0] div 2;
{Get winning pattern of lines}
Line:=WinLines[Inx];
{Get beginning and ending cell of win}
C1:=Line[0]; C2:=Line[2];
{Convert to screen coordinates}
P1.X:=C1.X * GameGrid.ColWidths[0] + W2;
P1.Y:=C1.Y * GameGrid.RowHeights[0] + H2;
P2.X:=C2.X * GameGrid.ColWidths[0] + W2;
P2.Y:=C2.Y * GameGrid.RowHeights[0] + H2;
{Set line attributes}
GameGrid.Canvas.Pen.Color:=clRed;
GameGrid.Canvas.Pen.Width:=5;
{Draw line}
GameGrid.Canvas.MoveTo(P1.X,P1.Y);
GameGrid.Canvas.LineTo(P2.X,P2.Y);
end;
end;
procedure DoBestComputerMove;
{Analyze game board and execute the best computer move}
var I,J,Inx: integer;
var CA: TCellArray;
var P: TPoint;
function UrgentMove(CA: TCellArray; var EmptyInx: integer): boolean;
{Test row, column or diagonal for an urgent move}
{This would be either an immediate win or immediate loss}
{Returns True if there is an urgent move and the index of location to respond}
var I: integer;
var OCnt,XCnt,EmptyCnt: integer;
begin
Result:=False;
OCnt:=0; XCnt:=0;
EmptyCnt:=0; EmptyInx:=-1;
{Count number of Xs, Os or Spaces in line}
for I:=0 to High(CA) do
begin
case CA[I] of
'O': Inc(OCnt);
'X': Inc(XCnt);
' ':
begin
Inc(EmptyCnt);
if EmptyCnt=1 then EmptyInx:=I;
end;
end;
end;
{Look for pattern of one empty and two Xs or two Os}
{Which means it's one move away from a win}
Result:=(EmptyCnt=1) and ((OCnt=2) or (XCnt=2));
end;
begin
with TicTacToeDlg do
begin
{Look for urgent moves in all patterns of wins}
for J:=0 to High(WinLines) do
begin
{Get a winning pattern of chars}
GetLineChars(J,CA);
if UrgentMove(CA,Inx) then
begin
{Urgent move found - take it}
P:=WinLines[J,Inx];
GameGrid.Cells[P.X,P.Y]:='O';
exit;
end;
end;
{No urgent moves, so use first empty}
{If there is no empty, the game is stalemated}
if FirstEmptyCell(P) then GameGrid.Cells[P.X,P.Y]:='O';
end;
end;
function TestWin: boolean;
{Test if last move resulted in win/draw}
var Inx: integer; var C: char;
var P: TPoint;
var S: string;
begin
Result:=True;
{Test if somebody won}
if IsWinner(Inx,C) then
begin
WinLineInx:=Inx;
TicTacToeDlg.GameGrid.Invalidate;
if C='O' then S:=' Computer Won!!' else S:=' You Won!!';
TicTacToeDlg.Status1Dis.Caption:=S;
GameOver:=True;
exit;
end;
{Test if game is a draw}
if not FirstEmptyCell(P) then
begin
TicTacToeDlg.Status1Dis.Caption:=' Game is Draw.';
GameOver:=True;
exit;
end;
Result:=False;
end;
procedure PlayTicTacToe;
{Show TicTacToe dialog box}
begin
ClearGrid;
TicTacToeDlg.ShowModal
end;
procedure TTicTacToeDlg.GameGridMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
{Handle user click on TicTacToe grid}
var Row,Col: integer;
begin
with TicTacToeDlg do
begin
if GameOver then exit;;
{Convert Mouse X,Y to Grid row and column}
GameGrid.MouseToCell(X,Y,Col,Row);
{Mark user's selection by placing X in the cell}
if GameGrid.Cells[Col,Row]=' 'then
GameGrid.Cells[Col,Row]:='X';
{Did this result in a win? }
if TestWin then exit;
{Get computer's response}
DoBestComputerMove;
{Did computer win}
TestWin;
end;
end;
procedure TTicTacToeDlg.ReplayBtnClick(Sender: TObject);
begin
ClearGrid;
end;
procedure TTicTacToeDlg.GameGridDrawCell(Sender: TObject; ACol,
ARow: Integer; Rect: TRect; State: TGridDrawState);
{Draw winner line on top of Xs and Os}
begin
if WinLineInx>=0 then DrawWinLine(WinLineInx);
end;
- Output:
Elapsed Time: 19.126 Sec.
EasyLang
It uses minimax with alpha-beta pruning. Therefore, the computer never loses.
len f[] 9
state = 0
textsize 14
#
proc init . .
linewidth 2
clear
color 666
move 34 96
line 34 20
move 62 96
line 62 20
move 10 72
line 86 72
move 10 44
line 86 44
linewidth 2.5
for i = 1 to 9
f[i] = 0
.
if state = 1
timer 0.2
.
.
proc draw ind . .
c = (ind - 1) mod 3
r = (ind - 1) div 3
x = c * 28 + 20
y = r * 28 + 30
if f[ind] = 4
color 900
move x - 7 y - 7
line x + 7 y + 7
move x + 7 y - 7
line x - 7 y + 7
elif f[ind] = 1
color 009
move x y
circle 10
color -2
circle 7.5
.
.
proc sum3 a d . st .
for i = 1 to 3
s += f[a]
a += d
.
if s = 3
st = -1
elif s = 12
st = 1
.
.
proc rate . res done .
res = 0
for i = 1 step 3 to 7
sum3 i 1 res
.
for i = 1 to 3
sum3 i 3 res
.
sum3 1 4 res
sum3 3 2 res
cnt = 1
for i = 1 to 9
if f[i] = 0
cnt += 1
.
.
res *= cnt
done = 1
if res = 0 and cnt > 1
done = 0
.
.
proc minmax player alpha beta . rval rmov .
rate rval done
if done = 1
if player = 1
rval = -rval
.
else
rval = alpha
start = random 9
mov = start
repeat
if f[mov] = 0
f[mov] = player
minmax (5 - player) (-beta) (-rval) val h
val = -val
f[mov] = 0
if val > rval
rval = val
rmov = mov
.
.
mov = mov mod 9 + 1
until mov = start or rval >= beta
.
.
.
proc show_result val . .
color 555
move 16 4
if val < 0
# this never happens
text "You won"
elif val > 0
text "You lost"
else
text "Tie"
.
state += 2
.
proc computer . .
minmax 4 -11 11 val mov
f[mov] = 4
draw mov
rate val done
state = 0
if done = 1
show_result val
.
.
proc human . .
mov = floor ((mouse_x - 6) / 28) + 3 * floor ((mouse_y - 16) / 28) + 1
if f[mov] = 0
f[mov] = 1
draw mov
state = 1
timer 0.5
.
.
on timer
rate val done
if done = 1
show_result val
else
computer
.
.
on mouse_down
if state = 0
if mouse_x > 6 and mouse_x < 90 and mouse_y > 16
human
.
elif state >= 2
state -= 2
init
.
.
init
Erlang
The program will randomly chose if the computer ("X") or the user ("O") starts. The computer look ahead is only one level. Perhaps the computer might lose?
-module(tic_tac_toe).
-export( [task/0] ).
task() -> io:fwrite( "Result: ~p.~n", [turn(player(random:uniform()), board())] ).
board() -> [{X, erlang:integer_to_list(X)} || X <- lists:seq(1, 9)].
board_tuples( Selections, Board ) -> [lists:keyfind(X, 1, Board) || X <- Selections].
computer_move( Player, Board ) ->
[N | _T] = lists:flatten( [X(Player, Board) || X <- [fun computer_move_win/2, fun computer_move_block/2, fun computer_move_middle/2, fun computer_move_random/2]] ),
N.
computer_move_block( Player, Board ) -> computer_move_two_same_player( player(false, Player), Board ).
computer_move_middle( _Player, Board ) ->
{5, Y} = lists:keyfind( 5, 1, Board ),
computer_move_middle( is_empty(Y) ).
computer_move_middle( true ) -> [5];
computer_move_middle( false ) -> [].
computer_move_random( _Player, Board ) ->
Ns = [X || {X, Y} <- Board, is_empty(Y)],
[lists:nth( random:uniform(erlang:length(Ns)), Ns )].
computer_move_two_same_player( Player, Board ) ->
Selections = [X || X <- three_in_row_all(), is_two_same_player(Player, X, Board)],
computer_move_two_same_player( Player, Board, Selections ).
computer_move_two_same_player( _Player, _Board, [] ) -> [];
computer_move_two_same_player( _Player, Board, [Selection | _T] ) -> [X || {X, Y} <- board_tuples(Selection, Board), is_empty(Y)].
computer_move_win( Player, Board ) -> computer_move_two_same_player( Player, Board ).
is_empty( Square ) -> Square =< "9". % Do not use < "10".
is_finished( Board ) -> is_full( Board ) orelse is_three_in_row( Board ).
is_full( Board ) -> [] =:= [X || {X, Y} <- Board, is_empty(Y)].
is_three_in_row( Board ) ->
Fun = fun(Selections) -> is_three_in_row_same_player( board_tuples(Selections, Board) ) end,
lists:any( Fun, three_in_row_all() ).
is_three_in_row_same_player( Selected ) -> three_in_row_player( Selected ) =/= no_player.
is_two_same_player( Player, Selections, Board ) -> is_two_same_player( Player, [{X, Y} || {X, Y} <- board_tuples(Selections, Board), not is_empty(Y)] ).
is_two_same_player( Player, [{_X, Player}, {_Y, Player}] ) -> true;
is_two_same_player( _Player, _Selected ) -> false.
player( Random ) when Random > 0.5 -> "O";
player( _Random ) -> "X".
player( true, _Player ) -> finished;
player( false, "X" ) -> "O";
player( false, "O" ) -> "X".
result( Board ) -> result( is_full(Board), Board ).
result( true, _Board ) -> draw;
result( false, Board ) ->
[Winners] = [Selections || Selections <- three_in_row_all(), three_in_row_player(board_tuples(Selections, Board)) =/= no_player],
"Winner is " ++ three_in_row_player( board_tuples(Winners, Board) ).
three_in_row_all() -> three_in_row_horisontal() ++ three_in_row_vertical() ++ three_in_row_diagonal().
three_in_row_diagonal() -> [[1,5,9], [3,5,7]].
three_in_row_horisontal() -> [[1,2,3], [4,5,6], [7,8,9]].
three_in_row_vertical() -> [[1,4,7], [2,5,8], [3,6,9]].
three_in_row_player( [{_X, Player}, {_Y, Player}, {_Z, Player}] ) -> three_in_row_player( not is_empty(Player), Player );
three_in_row_player( _Selected ) -> no_player.
three_in_row_player( true, Player ) -> Player;
three_in_row_player( false, _Player ) -> no_player.
turn( finished, Board ) -> result( Board );
turn( "X"=Player, Board ) ->
N = computer_move( Player, Board ),
io:fwrite( "Computer, ~p, selected ~p~n", [Player, N] ),
New_board = [{N, Player} | lists:keydelete(N, 1, Board)],
turn( player(is_finished(New_board), Player), New_board );
turn( "O"=Player, Board ) ->
[turn_board_write_horisontal(X, Board) || X <- three_in_row_horisontal()],
Ns = [X || {X, Y} <- Board, is_empty(Y)],
Prompt = lists:flatten( io_lib:format("Player, ~p, select one of ~p: ", [Player, Ns]) ),
N = turn_next_move( Prompt, Ns ),
New_board = [{N, Player} | lists:keydelete(N, 1, Board)],
turn( player(is_finished(New_board), Player), New_board ).
turn_board_write_horisontal( Selections, Board ) ->
Tuples = [lists:keyfind(X, 1, Board) || X <- Selections],
[io:fwrite( "~p ", [Y]) || {_X, Y} <- Tuples],
io:fwrite( "~n" ).
turn_next_move( Prompt, Ns ) ->
{ok,[N]} = io:fread( Prompt, "~d" ),
turn_next_move_ok( lists:member(N, Ns), Prompt, Ns, N ).
turn_next_move_ok( true, _Prompt, _Ns, N ) -> N;
turn_next_move_ok( false, Prompt, Ns, _N ) -> turn_next_move( Prompt, Ns ).
- Output:
96> tic_tac_toe:task(). "1" "2" "3" "4" "5" "6" "7" "8" "9" Player, "O", select one of [1,2,3,4,5,6,7,8,9]: 5 Computer, "X", selected 2 "1" "X" "3" "4" "O" "6" "7" "8" "9" Player, "O", select one of [1,3,4,6,7,8,9]: 1 Computer, "X", selected 9 "O" "X" "3" "4" "O" "6" "7" "8" "X" Player, "O", select one of [3,4,6,7,8]: 3 Computer, "X", selected 7 "O" "X" "O" "4" "O" "6" "X" "8" "X" Player, "O", select one of [4,6,8]: 8 Computer, "X", selected 6 "O" "X" "O" "4" "O" "X" "X" "O" "X" Player, "O", select one of [4]: 4 Result: draw.
ERRE
Taken from ERRE distribution disk: comments and messages are in Italian.
!-------------------------------------------- ! TRIS.R : gioca a tris contro l'operatore !-------------------------------------------- PROGRAM TRIS DIM TRIS%[9],T1%[9],PIECES$[3] !$SEGMENT=$B800 !$INCLUDE="PC.LIB" PROCEDURE DELAY(COUNT%) FOR Z%=1 TO COUNT DO END FOR END PROCEDURE PROCEDURE SET_BOARD ! ! Disegna lo schema del gioco ! CLS BLOAD("TRIS.BLD",0) !$KEY END PROCEDURE PROCEDURE PUT_PIECES ! ! Pone i pezzi sulla scacchiera ! Z%=0 FOR ROW%=6 TO 12 STEP 3 DO ! posizioni assolute sullo schermo FOR COL%=32 TO 48 STEP 8 DO LOCATE(ROW%+1,COL%+1) Z%=Z%+1 PRINT(PIECES$[TRIS%[Z%]]) END FOR END FOR END PROCEDURE PROCEDURE COMPUTE_MOVE(A%) CASE A% OF 2-> C1%=C1%+1 END -> 4-> C2%=C2%+1 END -> 8-> S1%=TRUE S2%=TRUE END -> 3-> N1%=N1%+1 END -> 9-> N2%=N2%+1 END -> 27-> S1%=FALSE S2%=FALSE END -> END CASE END PROCEDURE PROCEDURE PREPAREMOVE(T1%[],I%->M%) ! ! Prepara la mossa del calcolatore ! T1%[I%]=2 C1%=0 C2%=0 N1%=0 N2%=0 FOR K%=0 TO 2 DO COMPUTE_MOVE(T1%[3*K%+1]*T1%[3*K%+2]*T1%[3*K%+3]) COMPUTE_MOVE(T1%[K%+1]*T1%[K%+4]*T1%[K%+7]) END FOR COMPUTE_MOVE(T1%[1]*T1%[5]*T1%[9]) COMPUTE_MOVE(T1%[3]*T1%[5]*T1%[7]) M%=-63*N2%+31*C2%-15*N1%+7*C1% END PROCEDURE PROCEDURE COMPUTER_MOVE ! ! Coordina le mosse del calcolatore ! MAXSCORE%=-1000 FOR I%=1 TO 9 DO IF TRIS%[I%]=1 THEN PREPAREMOVE(TRIS%[],I%->MV%) EXIT IF S2% AND NOT S1% IF S1% AND S2% THEN TRIS%[I%]=2 DIARY$=DIARY$+"c"+MID$(STR$(I%),2)+"*" PUT_PIECES EXIT END IF IF MV%=0 THEN MOVE%=I% EXIT END IF IF MV%>MAXSCORE% THEN MOVE%=I% MAXSCORE%=MV% END IF END IF END FOR IF NOT S2% THEN TRIS%[MOVE%]=2 DIARY$=DIARY$+"c"+MID$(STR$(MOVE%),2)+";" PUT_PIECES NMOVE%=NMOVE%-1 S1%=(NMOVE%=0) END IF END PROCEDURE PROCEDURE PLAYER_MOVE ! ! Gioca l'avversario umano usando i tasti cursore per lo spostamento ! LOCATE(19,13) PRINT("Tocca a te .... ") REPEAT ROW%=7 COL%=32 LOCATE(ROW%+1,COL%+1) PRINT("Ü") REPEAT GET(B$) IF LEN(B$)=2 THEN CASE ASC(RIGHT$(B$,1)+CHR$(0)) OF 77-> ! codice tastiera per CRSR => LOCATE(ROW%+1,COL%+1) PRINT(" ") COL%=-(COL%+8)*(COL%<=40)-32*(COL%>40) LOCATE(ROW%+1,COL%+1) PRINT("Ü") END -> 75-> ! codice tastiera per CRSR <= LOCATE(ROW%+1,COL%+1) PRINT(" ") COL%=-(COL%-8)*(COL%>=40)-48*(COL%<40) LOCATE(ROW%+1,COL%+1) PRINT("Ü") END -> 80-> ! codice tastiera per CRSR DOWN LOCATE(ROW%+1,COL%+1) PRINT(" ") ROW%=-(ROW%+3)*(ROW%<=10)-7*(ROW%>10) LOCATE(ROW%+1,COL%+1) PRINT("Ü") END -> 72-> ! codice tastiera per CRSR UP LOCATE(ROW%+1,COL%+1) PRINT(" ") ROW%=-(ROW%-3)*(ROW%>=10)-13*(ROW%<10) LOCATE(ROW%+1,COL%+1) PRINT("Ü") END -> END CASE END IF UNTIL B$=CHR$(13) MM%=ROW%+COL%/8-10 ! da coordinate schermo a coordinate scacchiera UNTIL TRIS%[MM%]=1 TRIS%[MM%]=3 LOCATE(ROW%+1,COL%+1) PRINT(" ") DIARY$=DIARY$+"p"+MID$(STR$(MM%),2)+";" PUT_PIECES NMOVE%=NMOVE%-1 S1%=(NMOVE%=0) LOCATE(19,13) PRINT(STRING$(45," ")) END PROCEDURE BEGIN DATA(" ","+","o") SET_BOARD REPEAT S1%=FALSE S2%=FALSE ! determinano lo stato della partita NMOVE%=9 FOR Z%=1 TO 3 DO READ(PIECES$[Z%]) END FOR FOR Z%=1 TO 9 DO TRIS%[Z%]=1 END FOR LOCATE(19,13) PRINT("Giochi per primo ?") REPEAT GET(A$) UNTIL A$="S" OR A$="s" OR A$="N" OR A$="n" PUT_PIECES FOR INDICE%=1 TO 9 DO IF A$="s" OR A$="S" THEN PLAYER_MOVE EXIT IF S1% OR S2% COMPUTER_MOVE EXIT IF S1% OR S2% ELSE COMPUTER_MOVE EXIT IF S1% OR S2% PLAYER_MOVE EXIT IF S1% OR S2% END IF END FOR LOCATE(19,13) CASE TRUE OF (S1% AND NOT S2%)-> PRINT("E' finita pari !!! ") END -> (S2% AND NOT S1%)-> PRINT("HAI VINTO !!! ") END -> (S1% AND S2%)-> PRINT("HO VINTO IO !!! ") END -> END CASE DELAY(500) LOCATE(19,13) PRINT(DIARY$) DELAY(500) LOCATE(19,13) PRINT("Vuoi giocare ancora ? ") REPEAT GET(A$) UNTIL A$="S" OR A$="s" OR A$="N" OR A$="n" UNTIL A$="N" OR A$="n" END PROGRAM
- Output:
A game example
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ ░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░▒▒▓▓ ▓▓▒▒░░ ░░▒▒▓▓ ┌───────────────┐ ╔═══════╤═══════╤═══════╗ ┌────────────┐ ▓▓▒▒░░ ░░▒▒▓▓ │───── TRIS ────│ ║ + │ o │ + ║ │Il calcola- │ ▓▓▒▒░░ ░░▒▒▓▓ │ │ ║ │ │ ║ │tore non può│ ▓▓▒▒░░ ░░▒▒▓▓ │Si gioca contro│ ╟───────┼───────┼───────╢ │perdere e │ ▓▓▒▒░░ ░░▒▒▓▓ │il calcolatore.│ ║ + │ o │ o ║ │perciò il │ ▓▓▒▒░░ ░░▒▒▓▓ │Per muoversi │ ║ │ │ ║ │giocatore │ ▓▓▒▒░░ ░░▒▒▓▓ │usare i tasti │ ╟───────┼───────┼───────╢ │può al mas- │ ▓▓▒▒░░ ░░▒▒▓▓ │cursore e ◄──┘ │ ║ o │ + │ o ║ │simo pareg- │ ▓▓▒▒░░ ░░▒▒▓▓ │per confermare.│ ║ │ │ ║ │giare. │ ▓▓▒▒░░ ░░▒▒▓▓ └───────────────┘ ╚═══════╧═══════╧═══════╝ └────────────┘ ▓▓▒▒░░ ░░▒▒▓▓ ▓▓▒▒░░ ░░▒▒▓▓ ▓▓▒▒░░ ░░▒▒▓▓ ┌────────────────────────────────────────────────────────────┐ ▓▓▒▒░░ ░░▒▒▓▓ │ Vuoi giocare ancora ? │ ▓▓▒▒░░ ░░▒▒▓▓ └────────────────────────────────────────────────────────────┘ ▓▓▒▒░░ ░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Euphoria
No computer AI
include std/console.e
include std/text.e
include std/search.e
include std/sequence.e
sequence board
sequence players = {"X","O"}
function DisplayBoard()
for i = 1 to 3 do
for j = 1 to 3 do
printf(1,"%s",board[i][j])
if j < 3 then
printf(1,"%s","|")
end if
end for
if i < 3 then
puts(1,"\n-----\n")
else
puts(1,"\n\n")
end if
end for
return 1
end function
function CheckWinner()
sequence temp = board
for a = 1 to 2 do
for i = 1 to 3 do
if equal({"X","X","X"},temp[i]) then
puts(1,"X wins\n\n")
return 1
elsif equal({"O","O","O"},temp[i]) then
puts(1,"O wins\n\n")
return 1
end if
end for
temp = columnize(board)
end for
if equal({"X","X","X"},{board[1][1],board[2][2],board[3][3]}) or
equal({"X","X","X"},{board[1][3],board[2][2],board[3][1]}) then
puts(1,"X wins\n")
return 1
elsif equal({"O","O","O"},{board[1][1],board[2][2],board[3][3]}) or
equal({"O","O","O"},{board[1][3],board[2][2],board[3][1]}) then
puts(1,"O wins\n")
return 1
end if
if moves = 9 then
puts(1,"Draw\n\n")
return 1
end if
return 0
end function
integer turn, row, column, moves
sequence replay
while 1 do
board = repeat(repeat(" ",3),3)
DisplayBoard()
turn = rand(2)
moves = 0
while 1 do
while 1 do
printf(1,"%s's turn\n",players[turn])
row = prompt_number("Enter row: ",{})
column = prompt_number("Enter column: ",{})
if match(board[row][column]," ") then
board[row][column] = players[turn]
moves += 1
exit
else
puts(1,"Space already taken - pick again\n")
end if
end while
DisplayBoard()
if CheckWinner() then
exit
end if
if turn = 1 then
turn = 2
else
turn = 1
end if
end while
replay = lower(prompt_string("Play again (y/n)?\n\n"))
if match(replay,"n") then
exit
end if
end while
- Output:
| | ----- | | ----- | | O's turn Enter row: 1 Enter column: 1 O| | ----- | | ----- | | X's turn Enter row: 3 Enter column: 3 O| | ----- | | ----- | |X O's turn Enter row: 3 Enter column: 1 O| | ----- | | ----- O| |X X's turn Enter row: 2 Enter column: 1 O| | ----- X| | ----- O| |X O's turn Enter row: 2 Enter column: 2 O| | ----- X|O| ----- O| |X X's turn Enter row: 1 Enter column: 3 O| |X ----- X|O| ----- O| |X O's turn Enter row: 1 Enter column: 2 O|O|X ----- X|O| ----- O| |X X's turn Enter row: 3 Enter column: 2 O|O|X ----- X|O| ----- O|X|X O's turn Enter row: 2 Enter column: 3 O|O|X ----- X|O|O ----- O|X|X Draw Play again (y/n)?
F#
A purely-functional solution with a naive (but perfect) computer player implementation. The first move is played randomly by the computer.
type Brick =
| Empty
| Computer
| User
let brickToString = function
| Empty -> ' '
| Computer -> 'X'
| User -> 'O'
// y -> x -> Brick
type Board = Map<int, Map<int, Brick> >
let emptyBoard =
let emptyRow = Map.ofList [0,Empty; 1,Empty; 2,Empty]
Map.ofList [0,emptyRow; 1,emptyRow; 2,emptyRow]
let get (b:Board) (x,y) = b.[y].[x]
let set player (b:Board) (x,y) : Board =
let row = b.[y].Add(x, player)
b.Add(y, row)
let winningPositions =
[for x in [0..2] -> x,x] // first diagonal
::[for x in [0..2] -> 2-x,x] // second diagonal
::[for y in [0..2] do
yield! [[for x in [0..2]->(y,x)]; // columns
[for x in [0..2] -> (x,y)]]] // rows
let hasWon player board =
List.exists
(fun ps -> List.forall (fun pos -> get board pos = player) ps)
winningPositions
let freeSpace board =
[for x in 0..2 do
for y in 0..2 do
if get board (x,y) = Empty then yield x,y]
type Evaluation =
| Win
| Draw
| Lose
let rec evaluate board move =
let b2 = set Computer board move
if hasWon Computer b2 then Win
else
match freeSpace b2 with
| [] -> Draw
| userChoices ->
let b3s = List.map (set User b2) userChoices
if List.exists (hasWon User) b3s then Lose
elif List.exists (fun b3 -> bestOutcome b3 = Lose) b3s
then Lose
elif List.exists (fun b3 -> bestOutcome b3 = Draw) b3s
then Draw
else Win
and findBestChoice b =
match freeSpace b with
| [] -> ((-1,-1), Draw)
| choices ->
match List.tryFind (fun c -> evaluate b c = Win) choices with
| Some c -> (c, Win)
| None -> match List.tryFind (fun c -> evaluate b c = Draw) choices with
| Some c -> (c, Draw)
| None -> (List.head choices, Lose)
and bestOutcome b = snd (findBestChoice b)
let bestChoice b = fst (findBestChoice b)
let computerPlay b = set Computer b (bestChoice b)
let printBoard b =
printfn " | A | B | C |"
printfn "---+---+---+---+"
for y in 0..2 do
printfn " %d | %c | %c | %c |"
(3-y)
(get b (0,y) |> brickToString)
(get b (1,y) |> brickToString)
(get b (2,y) |> brickToString)
printfn "---+---+---+---+"
let rec userPlay b =
printfn "Which field do you play? (format: a1)"
let input = System.Console.ReadLine()
if input.Length <> 2
|| input.[0] < 'a' || input.[0] > 'c'
|| input.[1] < '1' || input.[1] > '3' then
printfn "illegal input"
userPlay b
else
let x = int(input.[0]) - int('a')
let y = 2 - int(input.[1]) + int('1')
if get b (x,y) <> Empty then
printfn "Field is not free."
userPlay b
else
set User b (x,y)
let rec gameLoop b player =
if freeSpace b = [] then
printfn "Game over. Draw."
elif player = Computer then
printfn "Computer plays..."
let b2 = computerPlay b
printBoard b2
if hasWon Computer b2 then
printfn "Game over. I have won."
else
gameLoop b2 User
elif player = User then
let b2 = userPlay b
printBoard b2
if hasWon User b2 then
printfn "Game over. You have won."
else
gameLoop b2 Computer
// randomly choose an element of a list
let choose =
let rnd = new System.Random()
fun (xs:_ list) -> xs.[rnd.Next(xs.Length)]
// play first brick randomly
printfn "Computer starts."
let b = set Computer emptyBoard (choose (freeSpace emptyBoard))
printBoard b
gameLoop b User
Example game:
Computer starts. | A | B | C | ---+---+---+---+ 3 | | | X | ---+---+---+---+ 2 | | | | ---+---+---+---+ 1 | | | | ---+---+---+---+ Which field do you play? (format: a1) a1 | A | B | C | ---+---+---+---+ 3 | | | X | ---+---+---+---+ 2 | | | | ---+---+---+---+ 1 | O | | | ---+---+---+---+ Computer plays... | A | B | C | ---+---+---+---+ 3 | X | | X | ---+---+---+---+ 2 | | | | ---+---+---+---+ 1 | O | | | ---+---+---+---+ Which field do you play? (format: a1) b3 | A | B | C | ---+---+---+---+ 3 | X | O | X | ---+---+---+---+ 2 | | | | ---+---+---+---+ 1 | O | | | ---+---+---+---+ Computer plays... | A | B | C | ---+---+---+---+ 3 | X | O | X | ---+---+---+---+ 2 | | | | ---+---+---+---+ 1 | O | | X | ---+---+---+---+ Which field do you play? (format: a1) c2 | A | B | C | ---+---+---+---+ 3 | X | O | X | ---+---+---+---+ 2 | | | O | ---+---+---+---+ 1 | O | | X | ---+---+---+---+ Computer plays... | A | B | C | ---+---+---+---+ 3 | X | O | X | ---+---+---+---+ 2 | | X | O | ---+---+---+---+ 1 | O | | X | ---+---+---+---+ Game over. I have won.
Forth
Game between two humans.
create board 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,