Tic-tac-toe: Difference between revisions
Content deleted Content added
Tags: Mobile edit Mobile web edit |
|||
(341 intermediate revisions by 91 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Games}} |
{{task|Games}} |
||
[[Category:Games]] |
|||
Play a game of [[wp:Tic-tac-toe|tic-tac-toe]]. Ensure that legal moves are played and that a winning position is notified. |
|||
[[File:Tic_tac_toe.jpg|500px||right]] |
|||
;Task: |
|||
Play a game of [[wp:Tic-tac-toe|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 |
|||
::* ''<big>X</big>s and <big>O</big>s'' |
|||
;See also |
|||
* [http://mathworld.wolfram.com/Tic-Tac-Toe.html MathWorld™, Tic-Tac-Toe game]. |
|||
* [https://en.wikipedia.org/wiki/Tic-tac-toe Wikipedia tic-tac-toe]. |
|||
<br><br> |
|||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="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")</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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] |
|||
</pre> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO, Ada.Numerics.Discrete_Random; |
||
-- can play human-human, human-computer, computer-human or computer-computer |
-- can play human-human, human-computer, computer-human or computer-computer |
||
-- the computer isn't very clever: it just chooses a legal random move |
-- the computer isn't very clever: it just chooses a legal random move |
||
Line 126: | Line 281: | ||
Ada.Text_IO.Put_Line("The winner is: " & Find_Winner(The_Board)); |
Ada.Text_IO.Put_Line("The winner is: " & Find_Winner(The_Board)); |
||
end if; |
end if; |
||
end Tic_Tac_Toe;</ |
end Tic_Tac_Toe;</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre> |
<pre> |
||
> ./tic_tac_toe |
> ./tic_tac_toe |
||
Line 240: | Line 394: | ||
</pre> |
</pre> |
||
=={{header|ALGOL W}}== |
|||
The user can play O, X, both or neither. O goes first whether user or computer controlled. |
|||
<syntaxhighlight lang="algolw">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.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|AppleScript}}== |
|||
<syntaxhighlight lang="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</syntaxhighlight> |
|||
=={{header|AutoHotkey}}== |
=={{header|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. |
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. |
It plays logically, trying to win, trying to block, or playing randomly in that order. |
||
< |
<syntaxhighlight lang="autohotkey">Gui, Add, Button, x12 y12 w30 h30 vB1 gButtonHandler, |
||
Gui, Add, Button, x52 y12 w30 h30 vB2 gButtonHandler, |
Gui, Add, Button, x52 y12 w30 h30 vB2 gButtonHandler, |
||
Gui, Add, Button, x92 y12 w30 h30 vB3 gButtonHandler, |
Gui, Add, Button, x92 y12 w30 h30 vB3 gButtonHandler, |
||
Line 350: | Line 844: | ||
GuiClose: |
GuiClose: |
||
ExitApp |
ExitApp |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|AWK}}== |
|||
<syntaxhighlight lang="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 } } |
|||
</syntaxhighlight> |
|||
=={{header|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. |
|||
<syntaxhighlight lang="bash"> |
|||
#!/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 |
|||
</syntaxhighlight> |
|||
{{out}} (nice ANSI formatting is not shown) |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|BASIC}}== |
|||
==={{header|BASIC256}}=== |
|||
<syntaxhighlight lang="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 |
|||
</syntaxhighlight> |
|||
==={{header|FreeBASIC}}=== |
|||
====graphics mode==== |
|||
<syntaxhighlight lang="freebasic"> |
|||
'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 |
|||
</syntaxhighlight> |
|||
====text mode==== |
|||
<syntaxhighlight lang="freebasic"> |
|||
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 |
|||
</syntaxhighlight> |
|||
==={{header|Microsoft Small Basic}}=== |
|||
This game has a simple AI. |
|||
<syntaxhighlight lang="smallbasic">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</syntaxhighlight> |
|||
==={{header|QuickBASIC}}=== |
|||
{{works with|QuickBasic|4.5}} |
|||
<syntaxhighlight lang="qbasic"> |
|||
' 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 |
|||
</syntaxhighlight> |
|||
==={{header|RapidQ}}=== |
|||
{{trans|QuickBASIC}} |
|||
<syntaxhighlight lang="basic"> |
|||
' 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 |
|||
</syntaxhighlight> |
|||
==={{header|Run BASIC}}=== |
|||
<syntaxhighlight lang="runbasic">' --------------------------- |
|||
' 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</syntaxhighlight> |
|||
==={{Header|Tiny BASIC}}=== |
|||
<syntaxhighlight lang="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 </syntaxhighlight> |
|||
==={{header|VBA}}=== |
|||
Human play first with the "X". You must choose the row and the column you want to play... |
|||
<syntaxhighlight lang="vb"> |
|||
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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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 !</pre> |
|||
==={{header|Yabasic}}=== |
|||
In the classic style. |
|||
<syntaxhighlight lang="yabasic">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: " |
|||
</syntaxhighlight> |
|||
=={{header|Batch File}}== |
|||
This is just a game between two human players. |
|||
<syntaxhighlight lang="dos">@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 |
|||
</syntaxhighlight> |
|||
===Advanced=== |
|||
This code makes a version of Tic Tac Toe with more features: |
|||
<syntaxhighlight lang="dos">@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=</syntaxhighlight> |
|||
=={{header|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. |
|||
<syntaxhighlight lang="befunge">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* |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> 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: |
|||
</pre> |
|||
=={{header|C}}== |
=={{header|C}}== |
||
Opening alternates between human and computer. Computer never loses. |
Opening alternates between human and computer. Computer never loses. |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
#include <stdlib.h> |
#include <stdlib.h> |
||
Line 378: | Line 3,597: | ||
void showboard() |
void showboard() |
||
{ |
{ |
||
char *t = "X O"; |
const char *t = "X O"; |
||
int i, j; |
int i, j; |
||
for (i = 0; i < 3; i++, putchar('\n')) |
for (i = 0; i < 3; i++, putchar('\n')) |
||
Line 413: | Line 3,632: | ||
} |
} |
||
char* game(int user) |
const char* game(int user) |
||
{ |
{ |
||
int i, j, k, move, win = 0; |
int i, j, k, move, win = 0; |
||
Line 454: | Line 3,673: | ||
{ |
{ |
||
int first = 0; |
int first = 0; |
||
while (1) printf(game(first = !first)); |
while (1) printf("%s", game(first = !first)); |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
=={{header| |
=={{header|C sharp}}== |
||
This implementation is purposely wordy because Tic-Tac-Toe is often a starting level program.<br/> |
|||
<lang d>import std.stdio, std.string, std.algorithm, std.conv, std.random; |
|||
It tries to show a number of C# code features while still keeping each function small and understandable. |
|||
<syntaxhighlight lang="csharp">using System; |
|||
struct Board { |
|||
using System.Collections.Generic; |
|||
int[9] board; |
|||
using System.Linq; |
|||
using System.Text; |
|||
namespace RosettaTicTacToe |
|||
string positionString(int pos) |
|||
{ |
|||
in { |
|||
class Program |
|||
assert(pos >= 0); |
|||
{ |
|||
assert(pos < 9); |
|||
/*================================================================ |
|||
*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; |
|||
} |
|||
body { |
|||
if(board[pos]) |
|||
return board[pos] == 1 ? "X" : "O"; |
|||
return to!string(pos + 1); |
|||
} |
} |
||
/*================================================================ |
|||
string toString() { |
|||
*Move Logic |
|||
string lineSeparator = "-+-+-\n"; |
|||
*================================================================*/ |
|||
string row(int start) { |
|||
static int getMoveFor(int player) |
|||
return format("%s|%s|%s\n", |
|||
{ |
|||
positionString(start + 0), |
|||
if (player == Human) |
|||
return getManualMove(player); |
|||
else |
|||
{ |
|||
//int selectedMove = getManualMove(player); |
|||
string ret; |
|||
//int selectedMove = getRandomMove(player); |
|||
int selectedMove = getSemiRandomMove(player); |
|||
//int selectedMove = getBestMove(player); |
|||
Console.WriteLine("{0} selects position {1}.", playerName(player), selectedMove + 1); |
|||
ret ~= row(3); |
|||
return selectedMove; |
|||
} |
|||
return ret; |
|||
} |
} |
||
int |
static int getManualMove(int player) |
||
{ |
|||
while (true) |
|||
{ |
|||
Console.Write("{0}, enter you move (number): ", playerName(player)); |
|||
ret ~= i; |
|||
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"); |
|||
return ret; |
|||
} |
|||
} |
} |
||
static int getRandomMove(int player) |
|||
{ |
|||
if(position < 0 || position >= 9) |
|||
int movesLeft = GameBoard.Count(position => position == Unplayed); |
|||
return false; |
|||
int x = rnd.Next(0, movesLeft); |
|||
return board[position] == 0; |
|||
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. |
|||
bool finished() { |
|||
static int getSemiRandomMove(int player) |
|||
return winner() != -1; |
|||
{ |
|||
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). |
|||
int winner() { |
|||
static int getBestMove(int player) |
|||
enum wins = [ |
|||
{ |
|||
[0,1,2], [3,4,5], [6,7,8], |
|||
return -1; |
|||
[0,3,6], [1,4,7], [2,5,8], |
|||
} |
|||
[0,4,8], [2,4,6] |
|||
]; |
|||
static bool checkForWinningMove(int player, out int posToPlay) |
|||
{ |
|||
foreach(win; wins) { |
|||
posToPlay = Unplayed; |
|||
foreach (var line in wins) |
|||
if (twoOfThreeMatchPlayer(player, line, out posToPlay)) |
|||
continue; // nobody wins on this one |
|||
return true; |
|||
return false; |
|||
// the same player needs to take all three positions |
|||
} |
|||
if(board[win[1]] == desired && board[win[2]] == desired) |
|||
return desired; // a winner! |
|||
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; |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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):</pre> |
|||
=={{header|C++}}== |
|||
<syntaxhighlight lang="cpp"> |
|||
#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; |
|||
if(openPositions().length == 0) |
|||
return 0; // a draw |
|||
return -1; // the game is still going |
|||
} |
} |
||
int suggestMove(int player) |
|||
int _p; |
|||
out(ret) { |
|||
int _field[9]; |
|||
assert(ret >= 0); |
|||
}; |
|||
assert(ret < 9); |
|||
//-------------------------------------------------------------------------------------------------- |
|||
assert(isAvailable(ret)); |
|||
int main( int argc, char* argv[] ) |
|||
{ |
|||
srand( GetTickCount() ); |
|||
ttt tic; |
|||
tic.play(); |
|||
return 0; |
|||
} |
|||
//-------------------------------------------------------------------------------------------------- |
|||
</syntaxhighlight> |
|||
{{out}} Computer plays 'X' and human plays 'O' |
|||
<pre> |
|||
1 | 2 | X |
|||
---+---+--- |
|||
X | 5 | 6 |
|||
---+---+--- |
|||
7 | O | 9 |
|||
Enter your move ( 1 - 9 ) |
|||
</pre> |
|||
=={{header|Common Lisp}}== |
|||
<syntaxhighlight lang="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."))))) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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.</pre> |
|||
=={{header|D}}== |
|||
<syntaxhighlight lang="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; |
|||
} |
} |
||
body { |
|||
return |
return availablePositions.empty ? Game.draw: Game.going; |
||
} |
|||
Random(unpredictableSeed)).front; |
|||
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, $)]; |
|||
} |
} |
||
} |
} |
||
void main() { |
|||
GameBoard playGame() { |
|||
writeln("\tTic-tac-toe game player."); |
|||
GameBoard board; |
|||
bool playsHuman = true; |
|||
Board board; |
|||
while (!board.isFinished) { |
|||
int currentPlayer = 1; |
|||
board.writeln; |
|||
while(!board.finished()) { |
|||
writeln(board); |
|||
int move; |
int move; |
||
if (playsHuman) { |
|||
if(currentPlayer == 1) { |
|||
do { |
do { |
||
writef(" |
writef("Your move (available moves: %s)? ", |
||
map! |
board.availablePositions.map!q{ a + 1 }); |
||
readf("%d\n", &move); |
readf("%d\n", &move); |
||
move--; // Zero based indexing. |
|||
move |
if (move < 0) |
||
return board; |
|||
} |
} while (!board.isAvailable(move)); |
||
} else |
|||
move = board.suggestMove(currentPlayer); |
|||
move = board.computerMove; |
|||
assert(board.isAvailable(move)); |
assert(board.isAvailable(move)); |
||
writefln("\n%s chose %d", playsHuman ? "You" : "I", move + 1); |
|||
board.board[move] = playsHuman ? GameBoard.human : |
|||
writefln("%s chose %d", currentPlayer == 1 ? "You" : "I", move + 1); |
|||
GameBoard.computer; |
|||
playsHuman = !playsHuman; // Switch player. |
|||
board.board[move] = currentPlayer; |
|||
currentPlayer = currentPlayer == 2 ? 1 : 2; |
|||
} |
} |
||
return board; |
|||
} |
|||
if(winner == 0) |
|||
writeln("\nDraw"); |
|||
if(winner == 1) |
|||
void main() { |
|||
writeln("\nYou win!"); |
|||
"Tic-tac-toe game player.\n".writeln; |
|||
if(winner == 2) |
|||
immutable outcome = playGame.winner; |
|||
writeln("\nComputer wins."); |
|||
}</lang> |
|||
final switch (outcome) { |
|||
Output: |
|||
case GameBoard.Game.going: |
|||
<pre> Tic-tac-toe game player. |
|||
"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; |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Tic-tac-toe game player. |
|||
1|2|3 |
1|2|3 |
||
-+-+- |
-+-+- |
||
Line 601: | Line 4,410: | ||
-+-+- |
-+-+- |
||
7|8|9 |
7|8|9 |
||
Your move (available moves: [1, 2, 3, 4, 5, 6, 7, 8, 9])? 1 |
|||
Input the index of your move (from [1, 2, 3, 4, 5, 6, 7, 8, 9]): 1 |
|||
You chose 1 |
You chose 1 |
||
X|2|3 |
X|2|3 |
||
Line 610: | Line 4,419: | ||
7|8|9 |
7|8|9 |
||
I chose |
I chose 2 |
||
X| |
X|O|3 |
||
-+-+- |
-+-+- |
||
4| |
4|5|6 |
||
-+-+- |
-+-+- |
||
7|8|9 |
7|8|9 |
||
Your move (available moves: [3, 4, 5, 6, 7, 8, 9])? 5 |
|||
You chose 5 |
|||
Input the index of your move (from [2, 3, 4, 6, 7, 8, 9]): 2 |
|||
X|O|3 |
|||
You chose 2 |
|||
X|X|3 |
|||
-+-+- |
-+-+- |
||
4| |
4|X|6 |
||
-+-+- |
-+-+- |
||
7|8|9 |
7|8|9 |
||
I chose |
I chose 3 |
||
X| |
X|O|O |
||
-+-+- |
-+-+- |
||
4|X|6 |
|||
-+-+- |
-+-+- |
||
7|8|9 |
7|8|9 |
||
Your move (available moves: [4, 6, 7, 8, 9])? 9 |
|||
You chose 9 |
|||
Input the index of your move (from [3, 6, 7, 8, 9]): 3 |
|||
You chose 3 |
|||
You win! |
You win!</pre> |
||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
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. |
|||
[[File:DelphiTicTacToe.png|frame|none]] |
|||
<syntaxhighlight lang="Delphi"> |
|||
{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; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Elapsed Time: 19.126 Sec. |
|||
</pre> |
|||
=={{header|EasyLang}}== |
|||
It uses minimax with alpha-beta pruning. Therefore, the computer never loses. |
|||
[https://easylang.dev/apps/tictactoe.html Run it] |
|||
<syntaxhighlight> |
|||
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 = randint 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 |
|||
</syntaxhighlight> |
|||
=={{header|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? |
|||
<syntaxhighlight lang="erlang"> |
|||
-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 ). |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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. |
|||
</pre> |
|||
=={{header|ERRE}}== |
|||
Taken from ERRE distribution disk: comments and messages are in Italian. |
|||
<pre>!-------------------------------------------- |
|||
! 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 |
|||
</pre> |
|||
{{out}} |
|||
A game example |
|||
<pre> |
|||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ |
|||
░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ |
|||
░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ |
|||
░░▒▒▓▓ ▓▓▒▒░░ |
|||
░░▒▒▓▓ ┌───────────────┐ ╔═══════╤═══════╤═══════╗ ┌────────────┐ ▓▓▒▒░░ |
|||
░░▒▒▓▓ │───── 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 ? │ ▓▓▒▒░░ |
|||
░░▒▒▓▓ └────────────────────────────────────────────────────────────┘ ▓▓▒▒░░ |
|||
░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ |
|||
░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ |
|||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ |
|||
</pre> |
|||
=={{header|Euphoria}}== |
|||
{{works with|OpenEuphoria}} |
|||
No computer AI |
|||
<syntaxhighlight lang="euphoria"> |
|||
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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
| | |
|||
----- |
|||
| | |
|||
----- |
|||
| | |
|||
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)? |
|||
</pre> |
</pre> |
||
Line 641: | Line 5,492: | ||
A purely-functional solution with a naive (but perfect) computer player implementation. The first move is played randomly by the computer. |
A purely-functional solution with a naive (but perfect) computer player implementation. The first move is played randomly by the computer. |
||
< |
<syntaxhighlight lang="fsharp">type Brick = |
||
| Empty |
| Empty |
||
| Computer |
| Computer |
||
Line 773: | Line 5,624: | ||
let b = set Computer emptyBoard (choose (freeSpace emptyBoard)) |
let b = set Computer emptyBoard (choose (freeSpace emptyBoard)) |
||
printBoard b |
printBoard b |
||
gameLoop b User</ |
gameLoop b User</syntaxhighlight> |
||
Example game: |
Example game: |
||
Line 844: | Line 5,695: | ||
---+---+---+---+ |
---+---+---+---+ |
||
Game over. I have won. |
Game over. I have won. |
||
</pre> |
|||
=={{header|Forth}}== |
|||
{{works with|gforth|0.7.3}} |
|||
Game between two humans. |
|||
<syntaxhighlight lang="forth">create board 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , |
|||
\ board: 0=empty, 1=player X, 2=player O |
|||
: player. ( player -- ) C" XO" 1+ + @ emit ; |
|||
: spot ( n -- addr ) cells board + ; |
|||
: clean-board ( -- ) 9 0 do 0 i spot ! loop ; |
|||
: show-board |
|||
CR ." +---+---+---+ +---+---+---+" CR |
|||
3 0 do |
|||
." | " |
|||
3 0 do |
|||
i j 3 * + spot @ player. ." | " |
|||
loop |
|||
." | " |
|||
3 0 do |
|||
i j 3 * + . ." | " |
|||
loop |
|||
CR ." +---+---+---+ +---+---+---+" CR |
|||
loop ; |
|||
: spots-equal ( n1 n2 n3 -- f ) |
|||
over spot @ swap spot @ = >r spot @ swap spot @ = r> and ; |
|||
: spots-win ( n1 n2 n3 -- f ) |
|||
dup >r spots-equal r> spot @ 0<> and ; |
|||
: winner-check ( -- player ) |
|||
0 1 2 spots-win if 0 spot @ exit then |
|||
3 4 5 spots-win if 3 spot @ exit then |
|||
6 7 8 spots-win if 6 spot @ exit then |
|||
0 3 6 spots-win if 0 spot @ exit then |
|||
1 4 7 spots-win if 1 spot @ exit then |
|||
2 5 8 spots-win if 2 spot @ exit then |
|||
0 4 8 spots-win if 0 spot @ exit then |
|||
2 4 6 spots-win if 2 spot @ exit then |
|||
0 ; |
|||
: player-move ( player -- ) |
|||
begin |
|||
key dup emit [char] 0 - dup |
|||
spot @ 0= if spot ! exit else drop then |
|||
again ; |
|||
: game |
|||
clean-board show-board |
|||
9 0 do |
|||
i 2 mod 1+ dup ." Player " player. ." : " |
|||
player-move show-board |
|||
winner-check dup 0<> if player. ." wins !" unloop exit else drop then |
|||
loop |
|||
." Draw!" ; |
|||
game |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
Player X: 0 |
|||
+---+---+---+ +---+---+---+ |
|||
| X | | | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
Player O: 3 |
|||
+---+---+---+ +---+---+---+ |
|||
| X | | | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| O | | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
Player X: 1 |
|||
+---+---+---+ +---+---+---+ |
|||
| X | X | | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| O | | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
Player O: 4 |
|||
+---+---+---+ +---+---+---+ |
|||
| X | X | | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| O | O | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
Player X: 2 |
|||
+---+---+---+ +---+---+---+ |
|||
| X | X | X | | 0 | 1 | 2 | |
|||
+---+---+---+ +---+---+---+ |
|||
| O | O | | | 3 | 4 | 5 | |
|||
+---+---+---+ +---+---+---+ |
|||
| | | | | 6 | 7 | 8 | |
|||
+---+---+---+ +---+---+---+ |
|||
X wins ! ok |
|||
</pre> |
|||
=={{header|Fortran}}== |
|||
Objective: write program in less than 100 lines, not using semicolons. Computer never loses, but plays as random as possible. Player gets first move of first game. Afterwards, first move alternates between computer and player. |
|||
<syntaxhighlight lang="fortran"> |
|||
! This is a fortran95 implementation of the game of tic-tac-toe. |
|||
! - Objective was to use less than 100 lines. |
|||
! - No attention has been devoted to efficiency. |
|||
! - Indentation by findent: https://sourceforge.net/projects/findent/ |
|||
! - This is free software, use as you like at own risk. |
|||
! - Compile: gfortran -o tictactoe tictactoe.f90 |
|||
! - Run: ./tictactoe |
|||
! Comments to: wvermin at gmail dot com |
|||
module tic |
|||
implicit none |
|||
integer :: b(9) |
|||
contains |
|||
logical function iswin(p) |
|||
integer,intent(in) :: p |
|||
iswin = & |
|||
all(b([1,2,3])==p).or.all(b([4,5,6])==p).or.all(b([7,8,9])==p).or.& |
|||
all(b([1,4,7])==p).or.all(b([2,5,8])==p).or.all(b([3,6,9])==p).or.& |
|||
all(b([1,5,9])==p).or.all(b([3,5,7])==p) |
|||
end function iswin |
|||
subroutine printb(mes) |
|||
character(len=*) :: mes |
|||
integer :: i,j |
|||
character :: s(0:2) = ['.','X','O'] |
|||
print "(3a3,' ',3i3)",(s(b(3*i+1:3*i+3)),(j,j=3*i+1,3*i+3),i=0,2) |
|||
if(mes /= ' ') print "(/,a)",mes |
|||
end subroutine printb |
|||
integer recursive function minmax(player,bestm) result(bestv) |
|||
integer :: player,bestm,move,v,bm,win=1000,inf=100000 |
|||
real :: x |
|||
if (all(b .ne. 0)) then |
|||
bestv = 0 |
|||
else |
|||
bestv = -inf |
|||
do move=1,9 |
|||
if (b(move) == 0) then |
|||
b(move) = player |
|||
if (iswin(player)) then |
|||
v = win |
|||
else |
|||
call random_number(x) |
|||
v = -minmax(3-player,bm) - int(10*x) |
|||
endif |
|||
if (v > bestv) then |
|||
bestv = v |
|||
bestm = move |
|||
endif |
|||
b(move) = 0 |
|||
if (v == win) exit |
|||
endif |
|||
enddo |
|||
endif |
|||
end function minmax |
|||
end module tic |
|||
program tictactoe |
|||
! computer: player=1, user: player=2 |
|||
use tic |
|||
implicit none |
|||
integer :: move,ios,v,bestmove,ply,player=2,k,values(8) |
|||
integer,allocatable :: seed(:) |
|||
call date_and_time(values=values) |
|||
call random_seed(size=k) |
|||
allocate(seed(k)) |
|||
seed = values(8)+1000*values(7)+60*1000*values(6)+60*60*1000*values(5) |
|||
call random_seed(put=seed) |
|||
mainloop: do |
|||
b = 0 |
|||
call printb('You have O, I have X. You enter 0: game ends.') |
|||
plyloop: do ply=0,4 |
|||
if (player == 2 .or. ply >0 ) then ! user move |
|||
write(*,"(/,a)",advance='no'),'Your move? (0..9) ' |
|||
getloop: do |
|||
readloop: do |
|||
read (*,*,iostat=ios),move |
|||
if (ios == 0 .and. move >= 0 .and. move <= 9) exit readloop |
|||
write(*,"(a)",advance='no'),'huh? Try again (0..9): ' |
|||
enddo readloop |
|||
if ( move == 0) exit mainloop |
|||
if (b(move) == 0) exit getloop |
|||
write(*,"(a)",advance='no'),'Already occupied, again (0..9): ' |
|||
enddo getloop |
|||
b(move) = 2 |
|||
if(iswin(2)) then ! this should not happen |
|||
call printb('***** You win *****') |
|||
exit plyloop |
|||
endif |
|||
endif |
|||
v = minmax(1,bestmove) ! computer move |
|||
b(bestmove) = 1 |
|||
if(iswin(1)) then |
|||
call printb('***** I win *****') |
|||
exit plyloop |
|||
endif |
|||
write(*,"(/,a,i3)"), 'My move: ',bestmove |
|||
call printb(' ') |
|||
enddo plyloop |
|||
if(ply == 5) write(*,"('***** Draw *****',/)") |
|||
player = 3-player |
|||
enddo mainloop |
|||
end program |
|||
</syntaxhighlight> |
|||
=={{header|Go}}== |
|||
Intermediate, like Python's "Better skilled player." Computer wins and blocks where it can, but otherwise plays randomly. Plays multiple games and keeps score. Player gets first move of first game. Afterwards, loser gets to go first, or after cat games, first move alternates. |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
"bufio" |
|||
"fmt" |
|||
"math/rand" |
|||
"os" |
|||
"strings" |
|||
) |
|||
var b []byte |
|||
func printBoard() { |
|||
fmt.Printf("%s\n%s\n%s\n", b[0:3], b[3:6], b[6:9]) |
|||
} |
|||
var pScore, cScore int |
|||
var pMark, cMark byte = 'X', 'O' |
|||
var in = bufio.NewReader(os.Stdin) |
|||
func main() { |
|||
b = make([]byte, 9) |
|||
fmt.Println("Play by entering a digit.") |
|||
for { |
|||
// start of game |
|||
for i := range b { |
|||
b[i] = '1' + byte(i) |
|||
} |
|||
computerStart := cMark == 'X' |
|||
if computerStart { |
|||
fmt.Println("I go first, playing X's") |
|||
} else { |
|||
fmt.Println("You go first, playing X's") |
|||
} |
|||
TakeTurns: |
|||
for { |
|||
if !computerStart { |
|||
if !playerTurn() { |
|||
return |
|||
} |
|||
if gameOver() { |
|||
break TakeTurns |
|||
} |
|||
} |
|||
computerStart = false |
|||
computerTurn() |
|||
if gameOver() { |
|||
break TakeTurns |
|||
} |
|||
} |
|||
fmt.Println("Score: you", pScore, "me", cScore) |
|||
fmt.Println("\nLet's play again.") |
|||
} |
|||
} |
|||
func playerTurn() bool { |
|||
var pm string |
|||
var err error |
|||
for i := 0; i < 3; i++ { // retry loop |
|||
printBoard() |
|||
fmt.Printf("%c's move? ", pMark) |
|||
if pm, err = in.ReadString('\n'); err != nil { |
|||
fmt.Println(err) |
|||
return false |
|||
} |
|||
pm = strings.TrimSpace(pm) |
|||
if pm >= "1" && pm <= "9" && b[pm[0]-'1'] == pm[0] { |
|||
x := pm[0] - '1' |
|||
b[x] = pMark |
|||
return true |
|||
} |
|||
} |
|||
fmt.Println("You're not playing right.") |
|||
return false |
|||
} |
|||
var choices = make([]int, 9) |
|||
func computerTurn() { |
|||
printBoard() |
|||
var x int |
|||
defer func() { |
|||
fmt.Println("My move:", x+1) |
|||
b[x] = cMark |
|||
}() |
|||
// look for two in a row |
|||
block := -1 |
|||
for _, l := range lines { |
|||
var mine, yours int |
|||
x = -1 |
|||
for _, sq := range l { |
|||
switch b[sq] { |
|||
case cMark: |
|||
mine++ |
|||
case pMark: |
|||
yours++ |
|||
default: |
|||
x = sq |
|||
} |
|||
} |
|||
if mine == 2 && x >= 0 { |
|||
return // strategy 1: make winning move |
|||
} |
|||
if yours == 2 && x >= 0 { |
|||
block = x |
|||
} |
|||
} |
|||
if block >= 0 { |
|||
x = block // strategy 2: make blocking move |
|||
return |
|||
} |
|||
// default strategy: random move |
|||
choices = choices[:0] |
|||
for i, sq := range b { |
|||
if sq == '1'+byte(i) { |
|||
choices = append(choices, i) |
|||
} |
|||
} |
|||
x = choices[rand.Intn(len(choices))] |
|||
} |
|||
func gameOver() bool { |
|||
// check for win |
|||
for _, l := range lines { |
|||
if b[l[0]] == b[l[1]] && b[l[1]] == b[l[2]] { |
|||
printBoard() |
|||
if b[l[0]] == cMark { |
|||
fmt.Println("I win!") |
|||
cScore++ |
|||
pMark, cMark = 'X', 'O' |
|||
} else { |
|||
fmt.Println("You win!") |
|||
pScore++ |
|||
pMark, cMark = 'O', 'X' |
|||
} |
|||
return true |
|||
} |
|||
} |
|||
// check for empty squares |
|||
for i, sq := range b { |
|||
if sq == '1'+byte(i) { |
|||
return false |
|||
} |
|||
} |
|||
fmt.Println("Cat game.") |
|||
pMark, cMark = cMark, pMark |
|||
return true |
|||
} |
|||
var lines = [][]int{ |
|||
{0, 1, 2}, // rows |
|||
{3, 4, 5}, |
|||
{6, 7, 8}, |
|||
{0, 3, 6}, // columns |
|||
{1, 4, 7}, |
|||
{2, 5, 8}, |
|||
{0, 4, 8}, // diagonals |
|||
{2, 4, 6}, |
|||
}</syntaxhighlight> |
|||
=={{header|Groovy}}== |
|||
Simplified version of Tic Tac Toe for player vs. player (no AI computer-controlled option). |
|||
<syntaxhighlight lang="groovy">class Main { |
|||
def input = new Scanner(System.in) |
|||
static main(args) { |
|||
Main main = new Main(); |
|||
main.play(); |
|||
} |
|||
public void play() { |
|||
TicTackToe game = new TicTackToe(); |
|||
game.init() |
|||
def gameOver = false |
|||
def player = game.player1 |
|||
println "Players take turns marking a square. Only squares \n"+ |
|||
"not already marked can be picked. Once a player has \n"+ |
|||
"marked three squares in a row, column or diagonal, they win! If all squares \n"+ |
|||
"are marked and no three squares are the same, a tied game is declared.\n"+ |
|||
"Player 1 is O and Player 2 is X \n"+ |
|||
"Have Fun! \n\n" |
|||
println "${game.drawBoard()}" |
|||
while (!gameOver && game.plays < 9) { |
|||
player = game.currentPlayer == 1 ? game.player1 :game.player2 |
|||
def validPick = false; |
|||
while (!validPick) { |
|||
def square |
|||
println "Next $player, enter move by selecting square's number :" |
|||
try { |
|||
square = input.nextLine(); |
|||
} catch (Exception ex) { } |
|||
if (square.length() == 1 && Character.isDigit(square.toCharArray()[0])) { validPick = game.placeMarker(square) } |
|||
if (!validPick) { println "Select another Square" } |
|||
} |
|||
(game.checkWinner(player))? gameOver = true : game.switchPlayers() |
|||
println(game.drawBoard()); |
|||
} |
|||
println "Game Over, " + ((gameOver == true)? "$player Wins" : "Draw") |
|||
} |
|||
} |
|||
public class TicTackToe { |
|||
def board = new Object[3][3] |
|||
def final player1 = "player 1" |
|||
def final player2 = "player 2" |
|||
def final marker1 = 'X' |
|||
def final marker2 = 'O' |
|||
int currentPlayer |
|||
int plays |
|||
public TicTacToe(){ |
|||
} |
|||
def init() { |
|||
int counter = 0; |
|||
(0..2).each { row -> |
|||
(0..2).each { col -> |
|||
board[row][col] = (++counter).toString(); |
|||
} |
|||
} |
|||
plays = 0 |
|||
currentPlayer =1 |
|||
} |
|||
def switchPlayers() { |
|||
currentPlayer = (currentPlayer == 1) ? 2:1 |
|||
plays++ |
|||
} |
|||
def placeMarker(play) { |
|||
def result = false |
|||
(0..2).each { row -> |
|||
(0..2).each { col -> |
|||
if (board[row][col].toString()==play.toString()){ |
|||
board[row][col] = (currentPlayer == 1) ? marker2 : marker1; |
|||
result = true; |
|||
} |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
def checkWinner(player) { |
|||
char current = (player == player1)? marker2: marker1 |
|||
//Checking |
|||
return checkRows(current) || checkColumns(current) ||checkDiagonals(current); |
|||
} |
|||
def checkRows(char current){ |
|||
(0..2).any{ line -> |
|||
board[line].every { it == current} |
|||
} |
|||
} |
|||
def checkColumns(char current){ |
|||
(0..2).any{i -> |
|||
(0..2).every{j -> |
|||
board[j][i]==current } |
|||
} |
|||
} |
|||
def checkDiagonals(char current){ |
|||
def rightDiag = [board[0][0],board[1][1],board[2][2]] |
|||
def leftDiag = [board[0][2],board[1][1],board[2][0]] |
|||
return rightDiag.every{it == current} || leftDiag.every{it == current} |
|||
} |
|||
def drawBoard() { |
|||
StringBuilder builder = new StringBuilder("Game board: \n"); |
|||
(0..2).each { row-> |
|||
(0..2).each {col -> |
|||
builder.append("[" + board[row][col] + "]"); |
|||
} |
|||
builder.append("\n"); |
|||
} |
|||
builder.append("\n"); |
|||
return builder.toString(); |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|Haskell}}== |
|||
Computer player has three strategies: 1. Try to block the opponent first, 2. Try to guess a good position for the next move, 3. Place a piece randomly. |
|||
There are lots of comments throughout the code. |
|||
<syntaxhighlight lang="haskell"> |
|||
module Main where |
|||
import System.Random |
|||
import Data.List (intercalate, find, minimumBy) |
|||
import System.Environment (getArgs) |
|||
import Data.Char (digitToInt) |
|||
import Data.Maybe (listToMaybe, mapMaybe) |
|||
import Control.Monad (guard) |
|||
import Data.Ord (comparing) |
|||
-- check if there is a horizontal, vertical or diagonal line of |
|||
-- X or O |
|||
tictactoe :: String -> Bool |
|||
tictactoe a = tictactoeFor 'X' a /= tictactoeFor 'O' a |
|||
-- check if there is a horizontal, vertical or diagonal line |
|||
-- for the given player "n" |
|||
tictactoeFor :: Char -> String -> Bool |
|||
tictactoeFor n [a,b,c,d,e,f,g,h,i] = |
|||
[n,n,n] `elem` [[a,b,c],[d,e,f],[g,h,i],[a,d,g], |
|||
[b,e,h],[c,f,i],[a,e,i],[c,e,g]] |
|||
-- empty game board |
|||
start :: String |
|||
start = " " |
|||
-- check if there is an X or an O at the given position |
|||
isPossible :: Int -> String -> Bool |
|||
isPossible n game = (game !! n) `notElem` "XO" |
|||
-- try to place an X or an O at a given position. |
|||
-- "Right" + modified board means success, "Left" + unmodified board |
|||
-- means failure |
|||
place :: Int -> Char -> String -> Either String String |
|||
place i c game = |
|||
if isPossible i game |
|||
then Right $ take i game ++ [c] ++ drop (i + 1) game |
|||
else Left game |
|||
-- COMPUTER AI |
|||
-- get the number of movements, starting from a given non-empty board |
|||
-- and a position for the next movement, until the specified player |
|||
-- wins or no movement is possible |
|||
-- the positions are chosen sequentially, so there's not much |
|||
-- intelligence here anyway |
|||
developGame :: Bool -> Int -> Int -> Char -> String -> (Int, Char, String) |
|||
developGame iterateMore moves i player game |
|||
| i > 8 = |
|||
-- if i arrives to the last position, iterate again from 0 |
|||
-- but do it only once |
|||
if iterateMore |
|||
then developGame False moves 0 player game |
|||
-- draw game (after one iteration, still no winning moves) |
|||
else (moves, player, game) |
|||
-- draw game (game board full) or a win for the player |
|||
| moves == 9 || tictactoeFor player game = (moves, player, game) |
|||
-- make a move, if possible, and continue playing |
|||
| otherwise = case place i otherPlayer game of |
|||
-- position i is not empty. try with the next position |
|||
Left _ -> developGame iterateMore moves (i + 1) |
|||
otherPlayer game |
|||
-- position i was empty, so it was a valid move. |
|||
-- change the player and make a new move, starting at pos 0 |
|||
Right newGame -> developGame iterateMore (moves + 1) 0 |
|||
otherPlayer newGame |
|||
where |
|||
otherPlayer = changePlayer player |
|||
-- COMPUTER AI |
|||
-- starting from a given non-empty board, try to guess which position |
|||
-- could lead the player to the fastest victory. |
|||
bestMoveFor :: Char -> String -> Int |
|||
bestMoveFor player game = bestMove |
|||
where |
|||
-- drive the game to its end for each starting position |
|||
continuations = [ (x, developGame True 0 x player game) | |
|||
x <- [0..8] ] |
|||
-- compare the number of moves of the game and take the |
|||
-- shortest one |
|||
move (_, (m, _, _)) = m |
|||
(bestMove, _) = minimumBy (comparing move) continuations |
|||
-- canBlock checks if the opponent has two pieces in a row and the |
|||
-- other cell in the row is empty, and places the player's piece there, |
|||
-- blocking the opponent |
|||
canBlock :: Char -> String -> Maybe Int |
|||
canBlock p [a,b,c,d,e,f,g,h,i] = |
|||
listToMaybe $ mapMaybe blockable [[a,b,c],[d,e,f],[g,h,i],[a,d,g], |
|||
[b,e,h],[c,f,i],[a,e,i],[c,e,g]] |
|||
where |
|||
blockable xs = do |
|||
guard $ length (filter (== otherPlayer) xs) == 2 |
|||
x <- find (`elem` "123456789") xs |
|||
return $ digitToInt x |
|||
otherPlayer = changePlayer p |
|||
-- format a game board for on-screen printing |
|||
showGame :: String -> String |
|||
showGame [a,b,c,d,e,f,g,h,i] = |
|||
topBottom ++ |
|||
"| | 1 | 2 | 3 |\n" ++ |
|||
topBottom ++ |
|||
row "0" [[a],[b],[c]] ++ |
|||
row "3" [[d],[e],[f]] ++ |
|||
row "6" [[g],[h],[i]] |
|||
where |
|||
topBottom = "+----+---+---+---+\n" |
|||
row n x = "| " ++ n ++ "+ | " ++ |
|||
intercalate " | " x ++ " |\n" ++ topBottom |
|||
-- ask the user to press a numeric key and convert it to an int |
|||
enterNumber :: IO Int |
|||
enterNumber = do |
|||
c <- getChar |
|||
if c `elem` "123456789" |
|||
then do |
|||
putStrLn "" |
|||
return $ digitToInt c |
|||
else do |
|||
putStrLn "\nPlease enter a digit!" |
|||
enterNumber |
|||
-- a human player's turn: get the number of pieces put on the board, |
|||
-- the next piece to be put (X or O) and a game board, and return |
|||
-- a new game state, checking if the piece can be placed on the board. |
|||
-- if it can't, make the user try again. |
|||
turn :: (Int, Char, String) -> IO (Int, Char, String) |
|||
turn (count, player, game) = do |
|||
putStr $ "Please tell me where you want to put an " ++ |
|||
[player] ++ ": " |
|||
pos <- enterNumber |
|||
case place (pos - 1) player game of |
|||
Left oldGame -> do |
|||
putStrLn "That place is already taken!\n" |
|||
turn (count, player, oldGame) |
|||
Right newGame -> |
|||
return (count + 1, changePlayer player, newGame) |
|||
-- alternate between X and O players |
|||
changePlayer :: Char -> Char |
|||
changePlayer 'O' = 'X' |
|||
changePlayer 'X' = 'O' |
|||
-- COMPUTER AI |
|||
-- make an automatic turn, placing an X or an O game board. |
|||
-- the first movement is always random. |
|||
-- first, the computer looks for two pieces of his opponent in a row |
|||
-- and tries to block. |
|||
-- otherwise, it tries to guess the best position for the next movement. |
|||
-- as a last resort, it places a piece randomly. |
|||
autoTurn :: Bool -> (Int, Char, String) -> IO (Int, Char, String) |
|||
autoTurn forceRandom (count, player, game) = do |
|||
-- try a random position 'cause everything else failed |
|||
-- count == 0 overrides the value of forceRandom |
|||
i <- if count == 0 || forceRandom |
|||
then randomRIO (0,8) |
|||
else return $ |
|||
case canBlock player game of |
|||
-- opponent can't be blocked. try to guess |
|||
-- the best movement |
|||
Nothing -> bestMoveFor player game |
|||
-- opponent can be blocked, so just do it! |
|||
Just blockPos -> blockPos |
|||
-- if trying to place a piece at a calculated position doesn't work, |
|||
-- just try again with a random value |
|||
case place i player game of |
|||
Left oldGame -> autoTurn True (count, player, oldGame) |
|||
Right newGame -> do |
|||
putStrLn $ "It's player " ++ [player] ++ "'s turn." |
|||
return (count + 1, changePlayer player, newGame) |
|||
-- play a game until someone wins or the board becomes full. |
|||
-- depending on the value of the variable "auto", ask the user(s) to |
|||
-- put some pieces on the board or do it automatically |
|||
play :: Int -> (Int, Char, String) -> IO () |
|||
play auto cpg@(_, player, game) = do |
|||
newcpg@(newCount, newPlayer, newGame) <- case auto of |
|||
-- if both players are human, always ask them |
|||
0 -> turn cpg |
|||
-- if both players are computer, always play auto |
|||
1 -> autoTurn False cpg |
|||
-- X is computer, O is human |
|||
2 -> if player == 'X' then autoTurn False cpg else turn cpg |
|||
-- X is human, O is computer |
|||
3 -> if player == 'O' then autoTurn False cpg else turn cpg |
|||
putStrLn $ "\n" ++ showGame newGame |
|||
if tictactoe newGame |
|||
then putStrLn $ "Player " ++ [changePlayer newPlayer] ++ " wins!\n" |
|||
else |
|||
if newCount == 9 |
|||
then putStrLn "Draw!\n" |
|||
else play auto newcpg |
|||
-- main program: greet the user, ask for a game type, ask for the |
|||
-- player that'll start the game, and play the game beginning with an |
|||
-- empty board |
|||
main :: IO () |
|||
main = do |
|||
a <- getArgs |
|||
if null a |
|||
then usage |
|||
else do |
|||
let option = head a |
|||
if option `elem` ["0","1","2","3"] |
|||
then do |
|||
putStrLn $ "\n" ++ showGame start |
|||
let m = read option :: Int |
|||
play m (0, 'X', start) |
|||
else usage |
|||
usage :: IO () |
|||
usage = do |
|||
putStrLn "TIC-TAC-TOE GAME\n================\n" |
|||
putStrLn "How do you want to play?" |
|||
putStrLn "Run the program with one of the following options." |
|||
putStrLn "0 : both players are human" |
|||
putStrLn "1 : both players are computer" |
|||
putStrLn "2 : player X is computer and player O is human" |
|||
putStrLn "3 : player X is human and player O is computer" |
|||
putStrLn "Player X always begins." |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
Player X is computer, O is human. |
|||
<pre> |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | | | | |
|||
+----+---+---+---+ |
|||
| 3+ | | | | |
|||
+----+---+---+---+ |
|||
| 6+ | | | | |
|||
+----+---+---+---+ |
|||
It's player X's turn. |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | | | | |
|||
+----+---+---+---+ |
|||
| 3+ | | X | | |
|||
+----+---+---+---+ |
|||
| 6+ | | | | |
|||
+----+---+---+---+ |
|||
Please tell me where you want to put an O: 1 |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | | |
|||
+----+---+---+---+ |
|||
| 3+ | | X | | |
|||
+----+---+---+---+ |
|||
| 6+ | | | | |
|||
+----+---+---+---+ |
|||
It's player X's turn. |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | X | |
|||
+----+---+---+---+ |
|||
| 3+ | | X | | |
|||
+----+---+---+---+ |
|||
| 6+ | | | | |
|||
+----+---+---+---+ |
|||
Please tell me where you want to put an O: 7 |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | X | |
|||
+----+---+---+---+ |
|||
| 3+ | | X | | |
|||
+----+---+---+---+ |
|||
| 6+ | O | | | |
|||
+----+---+---+---+ |
|||
It's player X's turn. |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | X | |
|||
+----+---+---+---+ |
|||
| 3+ | X | X | | |
|||
+----+---+---+---+ |
|||
| 6+ | O | | | |
|||
+----+---+---+---+ |
|||
Please tell me where you want to put an O: 6 |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | X | |
|||
+----+---+---+---+ |
|||
| 3+ | X | X | O | |
|||
+----+---+---+---+ |
|||
| 6+ | O | | | |
|||
+----+---+---+---+ |
|||
It's player X's turn. |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | | X | |
|||
+----+---+---+---+ |
|||
| 3+ | X | X | O | |
|||
+----+---+---+---+ |
|||
| 6+ | O | X | | |
|||
+----+---+---+---+ |
|||
Please tell me where you want to put an O: 2 |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | O | X | |
|||
+----+---+---+---+ |
|||
| 3+ | X | X | O | |
|||
+----+---+---+---+ |
|||
| 6+ | O | X | | |
|||
+----+---+---+---+ |
|||
It's player X's turn. |
|||
+----+---+---+---+ |
|||
| | 1 | 2 | 3 | |
|||
+----+---+---+---+ |
|||
| 0+ | O | O | X | |
|||
+----+---+---+---+ |
|||
| 3+ | X | X | O | |
|||
+----+---+---+---+ |
|||
| 6+ | O | X | X | |
|||
+----+---+---+---+ |
|||
Draw! |
|||
</pre> |
</pre> |
||
Line 850: | Line 6,570: | ||
The following works in both Icon and Unicon. The computer plays randomly against a human player, with legal moves enforced and wins/draws notified. |
The following works in both Icon and Unicon. The computer plays randomly against a human player, with legal moves enforced and wins/draws notified. |
||
<syntaxhighlight lang="icon"> |
|||
<lang Icon> |
|||
# Play TicTacToe |
# Play TicTacToe |
||
Line 963: | Line 6,683: | ||
play_game () |
play_game () |
||
end |
end |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|J}}== |
|||
<syntaxhighlight lang="j"> |
|||
'`turn board open full'=: {.`}.`(0=[{board@])`(0-.@e.board) |
|||
'`cpu_move position'=: (?@#{])@([:I.0=board)`(cpu_move`you_move@.(_1 1 i.turn)) |
|||
you_move=: $:@echo@'invalid'^:(-.@e.i.@9)@:<:@".@(1!:1@1) |
|||
move=: position (][echo@'already taken!')`(-@turn@] , turn@]`[`(board@])})@.open ] |
|||
show=: [ [: echo@''@echo (,' '&,)/"1@('.XO'{~3 3$board) [ echo@'' |
|||
won=: [:+./ (3=[:|+/)"1@(], |:, (<@i.@2|:]),: <@i.@2|:|.)@(3 3$board) |
|||
prompt=: echo@:>:@i.@3 3@echo@'enter a move (1–9) each turn; you''re X''s' |
|||
outcome=: (' wins'echo@,~'.XO'{~-@turn)`(echo@'tie')@.(1:i.~won,full) |
|||
ttt=: outcome@([F.(show@move[_2:Z:won+.full))@(10{._1)@prompt |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> ttt 0 |
|||
enter a move (1–9) each turn; you're X's |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
. O . |
|||
. . . |
|||
. . . |
|||
1 |
|||
X O . |
|||
. . . |
|||
. . . |
|||
X O . |
|||
. . . |
|||
. . O |
|||
5 |
|||
X O . |
|||
. X . |
|||
. . O |
|||
X O . |
|||
. X . |
|||
. O O |
|||
7 |
|||
X O . |
|||
. X . |
|||
X O O |
|||
X O . |
|||
O X . |
|||
X O O |
|||
9 |
|||
already taken! |
|||
X O . |
|||
O X . |
|||
X O O |
|||
3 |
|||
X O X |
|||
O X . |
|||
X O O |
|||
X wins</pre> |
|||
This is a stateless, purely functional, tacit approach. CPU selects a random open position. The <code>turn</code> and <code>board</code> verbs extract the turn/board from the vector which represents the game state. The verb <code>open</code> tests whether the selected position is available on the board. Fold (<code>F.</code>) is used because <code>Until=:<tt>{{</tt>u^:(-.@:v)^:_<tt>}}</tt></code> would've quit upon receiving invalid input, as the argument would be unchanged between that iteration and the next. |
|||
=={{header|Java}}== |
|||
This version works in the terminal itself, and uses the numpad for data entry. The computer is unbeatable, but some lines can be removed to avoid that. There's also an override that thrown in, just for fun. |
|||
<syntaxhighlight lang="java"> |
|||
import java.io.BufferedReader; |
|||
import java.io.InputStreamReader; |
|||
import java.util.Hashtable; |
|||
public class TicTacToe |
|||
{ |
|||
public static void main(String[] args) |
|||
{ |
|||
TicTacToe now=new TicTacToe(); |
|||
now.startMatch(); |
|||
} |
|||
private int[][] marks; |
|||
private int[][] wins; |
|||
private int[] weights; |
|||
private char[][] grid; |
|||
private final int knotcount=3; |
|||
private final int crosscount=4; |
|||
private final int totalcount=5; |
|||
private final int playerid=0; |
|||
private final int compid=1; |
|||
private final int truceid=2; |
|||
private final int playingid=3; |
|||
private String movesPlayer; |
|||
private byte override; |
|||
private char[][] overridegrid={{'o','o','o'},{'o','o','o'},{'o','o','o'}}; |
|||
private char[][] numpad={{'7','8','9'},{'4','5','6'},{'1','2','3'}}; |
|||
private Hashtable<Integer,Integer> crossbank; |
|||
private Hashtable<Integer,Integer> knotbank; |
|||
public void startMatch() |
|||
{ |
|||
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); |
|||
System.out.print("Start?(y/n):"); |
|||
char choice='y'; |
|||
try |
|||
{ |
|||
choice=br.readLine().charAt(0); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
System.out.println(e.getMessage()); |
|||
} |
|||
if(choice=='n'||choice=='N') |
|||
{ |
|||
return; |
|||
} |
|||
System.out.println("Use a standard numpad as an entry grid, as so:\n "); |
|||
display(numpad); |
|||
System.out.println("Begin"); |
|||
int playerscore=0; |
|||
int compscore=0; |
|||
do |
|||
{ |
|||
int result=startGame(); |
|||
if(result==playerid) |
|||
playerscore++; |
|||
else if(result==compid) |
|||
compscore++; |
|||
System.out.println("Score: Player-"+playerscore+" AI-"+compscore); |
|||
System.out.print("Another?(y/n):"); |
|||
try |
|||
{ |
|||
choice=br.readLine().charAt(0); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
System.out.println(e.getMessage()); |
|||
} |
|||
}while(choice!='n'||choice=='N'); |
|||
System.out.println("Game over."); |
|||
} |
|||
private void put(int cell,int player) |
|||
{ |
|||
int i=-1,j=-1;; |
|||
switch(cell) |
|||
{ |
|||
case 1:i=2;j=0;break; |
|||
case 2:i=2;j=1;break; |
|||
case 3:i=2;j=2;break; |
|||
case 4:i=1;j=0;break; |
|||
case 5:i=1;j=1;break; |
|||
case 6:i=1;j=2;break; |
|||
case 7:i=0;j=0;break; |
|||
case 8:i=0;j=1;break; |
|||
case 9:i=0;j=2;break; |
|||
default:display(overridegrid);return; |
|||
} |
|||
char mark='x'; |
|||
if(player==0) |
|||
mark='o'; |
|||
grid[i][j]=mark; |
|||
display(grid); |
|||
} |
|||
private int startGame() |
|||
{ |
|||
init(); |
|||
display(grid); |
|||
int status=playingid; |
|||
while(status==playingid) |
|||
{ |
|||
put(playerMove(),0); |
|||
if(override==1) |
|||
{ |
|||
System.out.println("O wins."); |
|||
return playerid; |
|||
} |
|||
status=checkForWin(); |
|||
if(status!=playingid) |
|||
break; |
|||
try{Thread.sleep(1000);}catch(Exception e){System.out.print(e.getMessage());} |
|||
put(compMove(),1); |
|||
status=checkForWin(); |
|||
} |
|||
return status; |
|||
} |
|||
private void init() |
|||
{ |
|||
movesPlayer=""; |
|||
override=0; |
|||
marks=new int[8][6]; |
|||
wins=new int[][] //new int[8][3]; |
|||
{ |
|||
{7,8,9}, |
|||
{4,5,6}, |
|||
{1,2,3}, |
|||
{7,4,1}, |
|||
{8,5,2}, |
|||
{9,6,3}, |
|||
{7,5,3}, |
|||
{9,5,1} |
|||
}; |
|||
weights=new int[]{3,2,3,2,4,2,3,2,3}; |
|||
grid=new char[][]{{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}}; |
|||
crossbank=new Hashtable<Integer,Integer>(); |
|||
knotbank=new Hashtable<Integer,Integer>(); |
|||
} |
|||
private void mark(int m,int player) |
|||
{ |
|||
for(int i=0;i<wins.length;i++) |
|||
for(int j=0;j<wins[i].length;j++) |
|||
if(wins[i][j]==m) |
|||
{ |
|||
marks[i][j]=1; |
|||
if(player==playerid) |
|||
marks[i][knotcount]++; |
|||
else |
|||
marks[i][crosscount]++; |
|||
marks[i][totalcount]++; |
|||
} |
|||
} |
|||
private void fixWeights() |
|||
{ |
|||
for(int i=0;i<3;i++) |
|||
for(int j=0;j<3;j++) |
|||
if(marks[i][j]==1) |
|||
if(weights[wins[i][j]-1]!=Integer.MIN_VALUE) |
|||
weights[wins[i][j]-1]=Integer.MIN_VALUE; |
|||
for(int i=0;i<8;i++) |
|||
{ |
|||
if(marks[i][totalcount]!=2) |
|||
continue; |
|||
if(marks[i][crosscount]==2) |
|||
{ |
|||
int p=i,q=-1; |
|||
if(marks[i][0]==0) |
|||
q=0; |
|||
else if(marks[i][1]==0) |
|||
q=1; |
|||
else if(marks[i][2]==0) |
|||
q=2; |
|||
if(weights[wins[p][q]-1]!=Integer.MIN_VALUE) |
|||
{ |
|||
weights[wins[p][q]-1]=6; |
|||
} |
|||
} |
|||
if(marks[i][knotcount]==2) |
|||
{ |
|||
int p=i,q=-1; |
|||
if(marks[i][0]==0) |
|||
q=0; |
|||
else if(marks[i][1]==0) |
|||
q=1; |
|||
else if(marks[i][2]==0) |
|||
q=2; |
|||
if(weights[wins[p][q]-1]!=Integer.MIN_VALUE) |
|||
{ |
|||
weights[wins[p][q]-1]=5; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
private int compMove() |
|||
{ |
|||
int cell=move(); |
|||
System.out.println("Computer plays: "+cell); |
|||
//weights[cell-1]=Integer.MIN_VALUE; |
|||
return cell; |
|||
} |
|||
private int move() |
|||
{ |
|||
int max=Integer.MIN_VALUE; |
|||
int cell=0; |
|||
for(int i=0;i<weights.length;i++) |
|||
if(weights[i]>max) |
|||
{ |
|||
max=weights[i]; |
|||
cell=i+1; |
|||
} |
|||
//This section ensures the computer never loses |
|||
//Remove it for a fair match |
|||
//Dirty kluge |
|||
if(movesPlayer.equals("76")||movesPlayer.equals("67")) |
|||
cell=9; |
|||
else if(movesPlayer.equals("92")||movesPlayer.equals("29")) |
|||
cell=3; |
|||
else if (movesPlayer.equals("18")||movesPlayer.equals("81")) |
|||
cell=7; |
|||
else if(movesPlayer.equals("73")||movesPlayer.equals("37")) |
|||
cell=4*((int)(Math.random()*2)+1); |
|||
else if(movesPlayer.equals("19")||movesPlayer.equals("91")) |
|||
cell=4+2*(int)(Math.pow(-1, (int)(Math.random()*2))); |
|||
mark(cell,1); |
|||
fixWeights(); |
|||
crossbank.put(cell, 0); |
|||
return cell; |
|||
} |
|||
private int playerMove() |
|||
{ |
|||
System.out.print("What's your move?: "); |
|||
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); |
|||
int cell=0; |
|||
int okay=0; |
|||
while(okay==0) |
|||
{ |
|||
try |
|||
{ |
|||
cell=Integer.parseInt(br.readLine()); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
System.out.println(e.getMessage()); |
|||
} |
|||
if(cell==7494) |
|||
{ |
|||
override=1; |
|||
return -1; |
|||
} |
|||
if((cell<1||cell>9)||weights[cell-1]==Integer.MIN_VALUE) |
|||
System.out.print("Invalid move. Try again:"); |
|||
else |
|||
okay=1; |
|||
} |
|||
playerMoved(cell); |
|||
System.out.println(); |
|||
return cell; |
|||
} |
|||
private void playerMoved(int cell) |
|||
{ |
|||
movesPlayer+=cell; |
|||
mark(cell,0); |
|||
fixWeights(); |
|||
knotbank.put(cell, 0); |
|||
} |
|||
private int checkForWin() |
|||
{ |
|||
int crossflag=0,knotflag=0; |
|||
for(int i=0;i<wins.length;i++) |
|||
{ |
|||
if(crossbank.containsKey(wins[i][0])) |
|||
if(crossbank.containsKey(wins[i][1])) |
|||
if(crossbank.containsKey(wins[i][2])) |
|||
{ |
|||
crossflag=1; |
|||
break; |
|||
} |
|||
if(knotbank.containsKey(wins[i][0])) |
|||
if(knotbank.containsKey(wins[i][1])) |
|||
if(knotbank.containsKey(wins[i][2])) |
|||
{ |
|||
knotflag=1; |
|||
break; |
|||
} |
|||
} |
|||
if(knotflag==1) |
|||
{ |
|||
display(grid); |
|||
System.out.println("O wins."); |
|||
return playerid; |
|||
} |
|||
else if(crossflag==1) |
|||
{ |
|||
display(grid); |
|||
System.out.println("X wins."); |
|||
return compid; |
|||
} |
|||
for(int i=0;i<weights.length;i++) |
|||
if(weights[i]!=Integer.MIN_VALUE) |
|||
return playingid; |
|||
System.out.println("Truce"); |
|||
return truceid; |
|||
} |
|||
private void display(char[][] grid) |
|||
{ |
|||
for(int i=0;i<3;i++) |
|||
{ |
|||
System.out.println("\n-------"); |
|||
System.out.print("|"); |
|||
for(int j=0;j<3;j++) |
|||
System.out.print(grid[i][j]+"|"); |
|||
} |
|||
System.out.println("\n-------"); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
<pre> |
|||
Start?(y/n):y |
|||
Use a standard numpad as an entry grid, as so: |
|||
------- |
|||
|7|8|9| |
|||
------- |
|||
|4|5|6| |
|||
------- |
|||
|1|2|3| |
|||
------- |
|||
Begin |
|||
------- |
|||
| | | | |
|||
------- |
|||
| | | | |
|||
------- |
|||
| | | | |
|||
------- |
|||
What's your move?: 4 |
|||
------- |
|||
| | | | |
|||
------- |
|||
|o| | | |
|||
------- |
|||
| | | | |
|||
------- |
|||
(...) |
|||
Computer plays: 7 |
|||
------- |
|||
|x| |o| |
|||
------- |
|||
|o|x|o| |
|||
------- |
|||
|x|o|x| |
|||
------- |
|||
X wins. |
|||
Score: Player-0 AI-1 |
|||
Another?(y/n):n |
|||
Game over. |
|||
</pre> |
|||
This version uses javax.swing. |
|||
<syntaxhighlight lang="java">import javax.swing.*; |
|||
import java.awt.*; |
|||
import java.awt.event.*; |
|||
import java.util.logging.Logger; |
|||
/** |
|||
* TicTacToe Application |
|||
* @author Steve Robinson |
|||
* @version 1.0 |
|||
*/ |
|||
class TicTacToeFrame extends JFrame |
|||
{ |
|||
JButton [][] buttons= new JButton[3][3]; |
|||
JTextField statusBar; |
|||
GamePanel panel; |
|||
Integer turn; |
|||
GameListener listener=new GameListener(); |
|||
Integer count; |
|||
public TicTacToeFrame() |
|||
{ |
|||
setLayout(new BorderLayout()); |
|||
panel=new GamePanel(); |
|||
add(panel,BorderLayout.CENTER); |
|||
statusBar=new JTextField("Player1's Turn"); |
|||
statusBar.setEditable(false); |
|||
add(statusBar,BorderLayout.SOUTH); |
|||
setTitle("Tic Tac Toe!"); |
|||
setVisible(true); |
|||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
|||
setBounds(400,400,300,300); |
|||
} |
|||
class GamePanel extends JPanel |
|||
{ |
|||
public GamePanel() |
|||
{ |
|||
setLayout(new GridLayout(3,3)); |
|||
turn =1; |
|||
count=0; |
|||
for(int i=0;i<3;i++) |
|||
for(int j=0;j<3;j++) { |
|||
buttons[i][j]=new JButton(); |
|||
buttons[i][j].putClientProperty("INDEX", new Integer[]{i,j}); |
|||
buttons[i][j].putClientProperty("OWNER", null); |
|||
buttons[i][j].addActionListener(listener); |
|||
add(buttons[i][j]); |
|||
} |
|||
} |
|||
} |
|||
class GameListener implements ActionListener |
|||
{ |
|||
public void actionPerformed(ActionEvent e) |
|||
{ |
|||
count++; |
|||
JButton b=(JButton)e.getSource(); |
|||
Integer[]index=(Integer[]) b.getClientProperty("INDEX"); |
|||
//System.out.println(turn); //turn // //System.out.println("["+index[0]+"]"+"["+index[1]+"]"); // |
|||
b.putClientProperty("OWNER", turn); |
|||
Icon ico=new ImageIcon(turn.toString()+".gif"); |
|||
b.setIcon(ico); |
|||
b.setEnabled(false); |
|||
boolean result=checkVictoryCondition(index); |
|||
if(result) |
|||
{ |
|||
JOptionPane.showMessageDialog(null, "Player "+turn.toString()+" Wins"); |
|||
initComponents(); |
|||
} |
|||
else |
|||
{ |
|||
if(turn==1) |
|||
{ |
|||
turn=2; |
|||
statusBar.setText("Player2's Turn"); |
|||
} |
|||
else |
|||
{ |
|||
turn=1; |
|||
statusBar.setText("Player1's Turn"); |
|||
} |
|||
} |
|||
if(count==9) |
|||
{ |
|||
JOptionPane.showMessageDialog(null, "Match is a draw!"); |
|||
initComponents(); |
|||
} |
|||
} |
|||
Integer getOwner(JButton b) |
|||
{ |
|||
return (Integer)b.getClientProperty("OWNER"); |
|||
} |
|||
//PrintButtonMap for Diagnostics |
|||
void printbuttonMap(Integer [][]bMap) |
|||
{ |
|||
for(int i=0;i for(int j=0;j System.out.print(bMap[i][j]+" "); |
|||
System.out.println(""); |
|||
} |
|||
} |
|||
boolean checkVictoryCondition(Integer [] index) |
|||
{ |
|||
/*Integer[][]buttonMap=new Integer[][] { |
|||
{ getOwner(buttons[0][0]),getOwner(buttons[0][1]),getOwner(buttons[0][2])}, |
|||
{ getOwner(buttons[1][0]),getOwner(buttons[1][1]),getOwner(buttons[1][2])}, |
|||
{ getOwner(buttons[2][0]),getOwner(buttons[2][1]),getOwner(buttons[2][2])} |
|||
}; |
|||
printbuttonMap(buttonMap); */ |
|||
Integer a=index[0]; |
|||
Integer b=index[1]; |
|||
int i; |
|||
//check row |
|||
for(i=0;i<3;i++) { |
|||
if(getOwner(buttons[a][i])!=getOwner(buttons[a][b])) |
|||
break; |
|||
} |
|||
if(i==3) |
|||
return true; |
|||
//check column |
|||
for(i=0;i<3;i++) { |
|||
if(getOwner(buttons[i][b])!=getOwner(buttons[a][b])) |
|||
break; |
|||
} |
|||
if(i==3) |
|||
return true; |
|||
//check diagonal |
|||
if((a==2&&b==2)||(a==0&&b==0)||(a==1&&b==1)||(a==0&&b==2)||(a==2&&b==0)) |
|||
{ |
|||
//left diagonal |
|||
for(i=0;i if(getOwner(buttons[i][i])!=getOwner(buttons[a][b])) |
|||
break; |
|||
if(i==3) |
|||
return true; |
|||
//right diagonal |
|||
if((getOwner(buttons[0][2])==getOwner(buttons[a][b]))&&(getOwner(buttons[1][1])==getOwner(buttons[a][b]))&&(getOwner(buttons[2][0])==getOwner(buttons[a][b]))) |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
void initComponents() |
|||
{ |
|||
for(int i=0;i<3;i++) |
|||
for(int j=0;j<3;j++) { |
|||
buttons[i][j].putClientProperty("INDEX", new Integer[]{i,j}); |
|||
buttons[i][j].putClientProperty("OWNER",null); |
|||
buttons[i][j].setIcon(null); |
|||
buttons[i][j].setEnabled(true); |
|||
turn=1; |
|||
count=0; |
|||
statusBar.setText("Player1's Turn"); |
|||
} |
|||
} |
|||
} |
|||
class TicTacToe { |
|||
public static void main(String[] args) { |
|||
EventQueue.invokeLater(new Runnable(){ |
|||
public void run() |
|||
{ |
|||
TicTacToeFrame frame=new TicTacToeFrame(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Graphical Java Example |
|||
<syntaxhighlight lang="java"> |
|||
import javax.swing.*; |
|||
import java.awt.event.*; |
|||
import java.awt.*; |
|||
//Make sure the name of the class is the same as the .java file name. |
|||
//If you change the class name you should change the class object name in runGUI method |
|||
public class ticTacToeCallum implements ActionListener { |
|||
static JFrame frame; |
|||
static JPanel contentPane; |
|||
static JLabel lblEnterFirstPlayerName, lblEnterSecondPlayerName, lblFirstPlayerScore, lblSecondPlayerScore; |
|||
static JButton btnButton1, btnButton2, btnButton3, btnButton4, btnButton5, btnButton6, btnButton7, btnButton8, btnButton9, btnClearBoard, btnClearAll, btnCloseGame; |
|||
static JTextField txtEnterFirstPlayerName, txtEnterSecondPlayerName; |
|||
static Icon imgicon = new ImageIcon("saveIcon.JPG"); |
|||
Font buttonFont = new Font("Arial", Font.PLAIN, 20); |
|||
//to adjust the frame size change the values in pixels |
|||
static int width = 600; |
|||
static int length = 400; |
|||
static int firstPlayerScore = 0; |
|||
static int secondPlayerScore = 0; |
|||
static int playerTurn = 1; |
|||
static int roundComplete = 0; |
|||
static int button1 = 1, button2 = 1, button3 = 1, button4 = 1, button5 = 1, button6 = 1, button7 = 1, button8 = 1, button9 = 1; // 1 is true, 0 is false |
|||
public ticTacToeCallum(){ |
|||
frame = new JFrame("Tic Tac Toe ^_^"); |
|||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
|||
contentPane = new JPanel(); |
|||
contentPane.setLayout(new GridLayout(6, 3, 10, 10)); |
|||
contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); |
|||
btnButton1 = new JButton(""); |
|||
btnButton1.setFont(buttonFont); |
|||
btnButton1.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton1.setIcon(imgicon); |
|||
btnButton1.setActionCommand("CLICK1"); |
|||
btnButton1.addActionListener(this); |
|||
contentPane.add(btnButton1); |
|||
btnButton2 = new JButton(""); |
|||
btnButton2.setFont(buttonFont); |
|||
btnButton2.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton2.setIcon(imgicon); |
|||
btnButton2.setActionCommand("CLICK2"); |
|||
btnButton2.addActionListener(this); |
|||
contentPane.add(btnButton2); |
|||
btnButton3 = new JButton(""); |
|||
btnButton3.setFont(buttonFont); |
|||
btnButton3.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton3.setIcon(imgicon); |
|||
btnButton3.setActionCommand("CLICK3"); |
|||
btnButton3.addActionListener(this); |
|||
contentPane.add(btnButton3); |
|||
btnButton4 = new JButton(""); |
|||
btnButton4.setFont(buttonFont); |
|||
btnButton4.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton4.setIcon(imgicon); |
|||
btnButton4.setActionCommand("CLICK4"); |
|||
btnButton4.addActionListener(this); |
|||
contentPane.add(btnButton4); |
|||
btnButton5 = new JButton(""); |
|||
btnButton5.setFont(buttonFont); |
|||
btnButton5.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton5.setIcon(imgicon); |
|||
btnButton5.setActionCommand("CLICK5"); |
|||
btnButton5.addActionListener(this); |
|||
contentPane.add(btnButton5); |
|||
btnButton6 = new JButton(""); |
|||
btnButton6.setFont(buttonFont); |
|||
btnButton6.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton6.setIcon(imgicon); |
|||
btnButton6.setActionCommand("CLICK6"); |
|||
btnButton6.addActionListener(this); |
|||
contentPane.add(btnButton6); |
|||
btnButton7 = new JButton(""); |
|||
btnButton7.setFont(buttonFont); |
|||
btnButton7.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton7.setIcon(imgicon); |
|||
btnButton7.setActionCommand("CLICK7"); |
|||
btnButton7.addActionListener(this); |
|||
contentPane.add(btnButton7); |
|||
btnButton8 = new JButton(""); |
|||
btnButton8.setFont(buttonFont); |
|||
btnButton8.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton8.setIcon(imgicon); |
|||
btnButton8.setActionCommand("CLICK8"); |
|||
btnButton8.addActionListener(this); |
|||
contentPane.add(btnButton8); |
|||
btnButton9 = new JButton(""); |
|||
btnButton9.setFont(buttonFont); |
|||
btnButton9.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnButton9.setIcon(imgicon); |
|||
btnButton9.setActionCommand("CLICK9"); |
|||
btnButton9.addActionListener(this); |
|||
contentPane.add(btnButton9); |
|||
lblEnterFirstPlayerName = new JLabel("Enter First Player's Name"); |
|||
contentPane.add(lblEnterFirstPlayerName); |
|||
txtEnterFirstPlayerName = new JTextField(""); |
|||
contentPane.add(txtEnterFirstPlayerName); |
|||
lblFirstPlayerScore = new JLabel("Score: " + firstPlayerScore); |
|||
contentPane.add(lblFirstPlayerScore); |
|||
lblEnterSecondPlayerName = new JLabel("Enter Second Player's Name"); |
|||
contentPane.add(lblEnterSecondPlayerName); |
|||
txtEnterSecondPlayerName = new JTextField(""); |
|||
contentPane.add(txtEnterSecondPlayerName); |
|||
lblSecondPlayerScore = new JLabel("Score: " + secondPlayerScore); |
|||
contentPane.add(lblSecondPlayerScore); |
|||
btnClearBoard = new JButton("Clear Board"); |
|||
btnClearBoard.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnClearBoard.setIcon(imgicon); |
|||
btnClearBoard.setActionCommand("CLICKClearBoard"); |
|||
btnClearBoard.addActionListener(this); |
|||
contentPane.add(btnClearBoard); |
|||
btnClearAll = new JButton("Clear All"); |
|||
btnClearAll.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnClearAll.setIcon(imgicon); |
|||
btnClearAll.setActionCommand("CLICKClearAll"); |
|||
btnClearAll.addActionListener(this); |
|||
contentPane.add(btnClearAll); |
|||
btnCloseGame = new JButton("Close Game"); |
|||
btnCloseGame.setAlignmentX(JButton.CENTER_ALIGNMENT); |
|||
btnCloseGame.setIcon(imgicon); |
|||
btnCloseGame.setActionCommand("CLICKCloseGame"); |
|||
btnCloseGame.addActionListener(this); |
|||
contentPane.add(btnCloseGame); |
|||
frame.setContentPane(contentPane); |
|||
frame.pack(); |
|||
frame.setSize(width,length); |
|||
frame.setVisible(true); |
|||
} |
|||
public void actionPerformed(ActionEvent event) { |
|||
String eventName = event.getActionCommand(); |
|||
if (eventName.equals("CLICK1")) { |
|||
if (button1 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton1.setForeground(Color.RED); |
|||
btnButton1.setText("X"); |
|||
playerTurn = 2; |
|||
button1 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton1.setForeground(Color.GREEN); |
|||
btnButton1.setText("O"); |
|||
playerTurn = 1; |
|||
button1 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK2")) { |
|||
if (button2 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton2.setForeground(Color.RED); |
|||
btnButton2.setText("X"); |
|||
playerTurn = 2; |
|||
button2 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton2.setForeground(Color.GREEN); |
|||
btnButton2.setText("O"); |
|||
playerTurn = 1; |
|||
button2 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK3")) { |
|||
if (button3 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton3.setForeground(Color.RED); |
|||
btnButton3.setText("X"); |
|||
playerTurn = 2; |
|||
button3 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton3.setForeground(Color.GREEN); |
|||
btnButton3.setText("O"); |
|||
playerTurn = 1; |
|||
button3 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK4")) { |
|||
if (button4 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton4.setForeground(Color.RED); |
|||
btnButton4.setText("X"); |
|||
playerTurn = 2; |
|||
button4 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton4.setForeground(Color.GREEN); |
|||
btnButton4.setText("O"); |
|||
playerTurn = 1; |
|||
button4 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK5")) { |
|||
if (button5 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton5.setForeground(Color.RED); |
|||
btnButton5.setText("X"); |
|||
playerTurn = 2; |
|||
button5 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton5.setForeground(Color.GREEN); |
|||
btnButton5.setText("O"); |
|||
playerTurn = 1; |
|||
button5 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK6")) { |
|||
if (button6 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton6.setForeground(Color.RED); |
|||
btnButton6.setText("X"); |
|||
playerTurn = 2; |
|||
button6 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton6.setForeground(Color.GREEN); |
|||
btnButton6.setText("O"); |
|||
playerTurn = 1; |
|||
button6 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK7")) { |
|||
if (button7 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton7.setForeground(Color.RED); |
|||
btnButton7.setText("X"); |
|||
playerTurn = 2; |
|||
button7 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton7.setForeground(Color.GREEN); |
|||
btnButton7.setText("O"); |
|||
playerTurn = 1; |
|||
button7 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK8")) { |
|||
if (button8 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton8.setForeground(Color.RED); |
|||
btnButton8.setText("X"); |
|||
playerTurn = 2; |
|||
button8 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton8.setForeground(Color.GREEN); |
|||
btnButton8.setText("O"); |
|||
playerTurn = 1; |
|||
button8 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICK9")) { |
|||
if (button9 == 1){ |
|||
if (playerTurn == 1){ |
|||
btnButton9.setForeground(Color.RED); |
|||
btnButton9.setText("X"); |
|||
playerTurn = 2; |
|||
button9 = 0; |
|||
} else if (playerTurn == 2) { |
|||
btnButton9.setForeground(Color.GREEN); |
|||
btnButton9.setText("O"); |
|||
playerTurn = 1; |
|||
button9 = 0; |
|||
} |
|||
} |
|||
} else if (eventName.equals ("CLICKClearBoard")) { |
|||
btnButton1.setText(""); |
|||
btnButton2.setText(""); |
|||
btnButton3.setText(""); |
|||
btnButton4.setText(""); |
|||
btnButton5.setText(""); |
|||
btnButton6.setText(""); |
|||
btnButton7.setText(""); |
|||
btnButton8.setText(""); |
|||
btnButton9.setText(""); |
|||
button1 = 1; |
|||
button2 = 1; |
|||
button3 = 1; |
|||
button4 = 1; |
|||
button5 = 1; |
|||
button6 = 1; |
|||
button7 = 1; |
|||
button8 = 1; |
|||
button9 = 1; |
|||
playerTurn = 1; |
|||
roundComplete = 0; |
|||
} else if (eventName.equals ("CLICKClearAll")) { |
|||
btnButton1.setText(""); |
|||
btnButton2.setText(""); |
|||
btnButton3.setText(""); |
|||
btnButton4.setText(""); |
|||
btnButton5.setText(""); |
|||
btnButton6.setText(""); |
|||
btnButton7.setText(""); |
|||
btnButton8.setText(""); |
|||
btnButton9.setText(""); |
|||
firstPlayerScore = 0; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
secondPlayerScore = 0; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
txtEnterFirstPlayerName.setText(""); |
|||
txtEnterSecondPlayerName.setText(""); |
|||
button1 = 1; |
|||
button2 = 1; |
|||
button3 = 1; |
|||
button4 = 1; |
|||
button5 = 1; |
|||
button6 = 1; |
|||
button7 = 1; |
|||
button8 = 1; |
|||
button9 = 1; |
|||
playerTurn = 1; |
|||
roundComplete = 0; |
|||
} else if (eventName.equals ("CLICKCloseGame")) { |
|||
System.exit(0); |
|||
} |
|||
score(); |
|||
} |
|||
public static void score(){ |
|||
if (roundComplete == 0){ |
|||
if (btnButton1.getText().equals(btnButton2.getText()) && btnButton1.getText().equals(btnButton3.getText())){ |
|||
if (btnButton1.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton1.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton1.getText().equals(btnButton4.getText()) && btnButton1.getText().equals(btnButton7.getText())){ |
|||
if (btnButton1.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton1.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton1.getText().equals(btnButton5.getText()) && btnButton1.getText().equals(btnButton9.getText())){ |
|||
if (btnButton1.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton1.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton7.getText().equals(btnButton8.getText()) && btnButton7.getText().equals(btnButton9.getText())){ |
|||
if (btnButton7.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton7.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton7.getText().equals(btnButton5.getText()) && btnButton7.getText().equals(btnButton3.getText())){ |
|||
if (btnButton7.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton7.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton3.getText().equals(btnButton6.getText()) && btnButton3.getText().equals(btnButton9.getText())){ |
|||
if (btnButton3.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton3.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton4.getText().equals(btnButton5.getText()) && btnButton4.getText().equals(btnButton6.getText())){ |
|||
if (btnButton4.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton4.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
if (btnButton2.getText().equals(btnButton5.getText()) && btnButton2.getText().equals(btnButton8.getText())){ |
|||
if (btnButton2.getText().equals("X")){ |
|||
firstPlayerScore += 1; |
|||
lblFirstPlayerScore.setText("Score: " + firstPlayerScore); |
|||
roundComplete = 1; |
|||
} else if (btnButton2.getText().equals("O")){ |
|||
secondPlayerScore += 1; |
|||
lblSecondPlayerScore.setText("Score: " + secondPlayerScore); |
|||
roundComplete = 1; |
|||
} |
|||
} |
|||
} |
|||
if (roundComplete == 1){ |
|||
button1 = 0; |
|||
button2 = 0; |
|||
button3 = 0; |
|||
button4 = 0; |
|||
button5 = 0; |
|||
button6 = 0; |
|||
button7 = 0; |
|||
button8 = 0; |
|||
button9 = 0; |
|||
} |
|||
} |
|||
/** |
|||
* Create and show the GUI. |
|||
*/ |
|||
private static void runGUI() { |
|||
ticTacToeCallum greeting = new ticTacToeCallum(); |
|||
} |
|||
//Do not change this method |
|||
public static void main(String[] args) { |
|||
/* Methods that create and show a GUI should be run from an event-dispatching thread */ |
|||
javax.swing.SwingUtilities.invokeLater(new Runnable() { |
|||
public void run() { |
|||
runGUI(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
The following program may be executed for a player-against-player game. |
|||
<syntaxhighlight lang="java"> |
|||
import javax.swing.*; |
|||
import javax.swing.border.Border; |
|||
import java.awt.*; |
|||
public class TicTacToe { |
|||
private static int turnNumber = 0; |
|||
private static final JPanel panel = new JPanel(); |
|||
private static final JTextField ta = new JTextField("Player A's Turn (X)"); |
|||
private static final JButton r1c1 = new JButton(""); |
|||
private static final JButton r1c2 = new JButton(""); |
|||
private static final JButton r1c3 = new JButton(""); |
|||
private static final JButton r2c1 = new JButton(""); |
|||
private static final JButton r2c2 = new JButton(""); |
|||
private static final JButton r2c3 = new JButton(""); |
|||
private static final JButton r3c1 = new JButton(""); |
|||
private static final JButton r3c2 = new JButton(""); |
|||
private static final JButton r3c3 = new JButton(""); |
|||
private static final JButton restart = new JButton("New Game"); |
|||
private static final JPanel startMain = new JPanel(); |
|||
public static void main(String[]args){ |
|||
JFrame frame = new JFrame("Tic Tac Toe"); |
|||
frame.setSize(600,650); |
|||
ta.setEditable(false); |
|||
restart.addActionListener(e -> { |
|||
enableAll(); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}); |
|||
r1c1.setSize(67,67); |
|||
r1c1.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r1c1.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r1c1.setText("O"); |
|||
r1c1.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r1c1.setText("X"); |
|||
r1c1.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r1c2.setSize(67,67); |
|||
r1c2.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r1c2.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r1c2.setText("O"); |
|||
r1c2.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r1c2.setText("X"); |
|||
r1c2.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r1c3.setSize(67,67); |
|||
r1c3.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r1c3.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r1c3.setText("O"); |
|||
r1c3.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r1c3.setText("X"); |
|||
r1c3.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r2c1.setSize(67,67); |
|||
r2c1.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r2c1.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r2c1.setText("O"); |
|||
r2c1.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r2c1.setText("X"); |
|||
r2c1.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r2c2.setSize(67,67); |
|||
r2c2.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r2c2.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r2c2.setText("O"); |
|||
r2c2.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r2c2.setText("X"); |
|||
r2c2.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r2c3.setSize(67,67); |
|||
r2c3.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r2c3.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r2c3.setText("O"); |
|||
r2c3.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r2c3.setText("X"); |
|||
r2c3.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r3c1.setSize(67,67); |
|||
r3c1.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r3c1.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r3c1.setText("O"); |
|||
r3c1.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r3c1.setText("X"); |
|||
r3c1.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r3c2.setSize(67,67); |
|||
r3c2.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r3c2.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r3c2.setText("O"); |
|||
r3c2.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r3c2.setText("X"); |
|||
r3c2.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
r3c3.setSize(67,67); |
|||
r3c3.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
r3c3.addActionListener(e -> { |
|||
turnNumber++; |
|||
if(turnNumber % 2 == 0){ |
|||
r3c3.setText("O"); |
|||
r3c3.setEnabled(false); |
|||
ta.setText("Player A's Turn (X)"); |
|||
}else{ |
|||
r3c3.setText("X"); |
|||
r3c3.setEnabled(false); |
|||
ta.setText("Player B's Turn (O)"); |
|||
} |
|||
checkWin(); |
|||
}); |
|||
panel.setLayout(new GridLayout(3,3)); |
|||
panel.add(r1c1); |
|||
panel.add(r1c2); |
|||
panel.add(r1c3); |
|||
panel.add(r2c1); |
|||
panel.add(r2c2); |
|||
panel.add(r2c3); |
|||
panel.add(r3c1); |
|||
panel.add(r3c2); |
|||
panel.add(r3c3); |
|||
startMain.setLayout(new GridLayout(5,5)); |
|||
JButton start = new JButton("Start"); |
|||
JLabel main = new JLabel("Tic Tac Toe", SwingConstants.CENTER); |
|||
main.setFont(new Font("Trebuchet MS", Font.PLAIN, 70)); |
|||
main.setSize(400,400); |
|||
startMain.add(main); |
|||
startMain.add(start); |
|||
frame.add(startMain); |
|||
frame.setVisible(true); |
|||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
|||
start.addActionListener(e -> { |
|||
startMain.setVisible(false); |
|||
frame.add(restart, BorderLayout.PAGE_START); |
|||
frame.add(ta, BorderLayout.PAGE_END); |
|||
frame.add(panel, BorderLayout.CENTER); |
|||
}); |
|||
} |
|||
public static void checkWin(){ |
|||
if(r1c1.getText().equals("X") && r1c2.getText().equals("X") && r1c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r1c1.getText().equals("O") && r1c2.getText().equals("O") && r1c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r1c1.getText().equals("X") && r2c2.getText().equals("X") && r3c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r1c1.getText().equals("O") && r2c2.getText().equals("O") && r3c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r1c1.getText().equals("X") && r2c1.getText().equals("X") && r3c1.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r1c1.getText().equals("O") && r2c1.getText().equals("O") && r3c1.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r2c1.getText().equals("X") && r2c2.getText().equals("X") && r2c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r2c1.getText().equals("O") && r2c2.getText().equals("O") && r2c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r1c2.getText().equals("X") && r2c2.getText().equals("X") && r3c2.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r1c2.getText().equals("O") && r2c2.getText().equals("O") && r3c2.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r1c3.getText().equals("X") && r2c3.getText().equals("X") && r3c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r1c3.getText().equals("O") && r2c3.getText().equals("O") && r3c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r3c1.getText().equals("X") && r3c2.getText().equals("X") && r3c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r3c1.getText().equals("O") && r3c2.getText().equals("O") && r3c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(r3c1.getText().equals("X") && r2c2.getText().equals("X") && r1c3.getText().equals("X")){ |
|||
ta.setText("Player A Won! (X)"); |
|||
disableAll(); |
|||
}else if(r3c1.getText().equals("O") && r2c2.getText().equals("O") && r1c3.getText().equals("O")){ |
|||
ta.setText("Player B Won! (O)"); |
|||
disableAll(); |
|||
}else if(!r1c1.isEnabled() && !r1c2.isEnabled() && !r1c3.isEnabled() && !r2c1.isEnabled() && !r2c2.isEnabled() && !r2c3.isEnabled() && !r3c1.isEnabled() && !r3c2.isEnabled() && !r3c3.isEnabled()){ |
|||
ta.setText("Draw!"); |
|||
disableAll(); |
|||
} |
|||
} |
|||
public static void disableAll(){ |
|||
r1c1.setEnabled(false); |
|||
r1c2.setEnabled(false); |
|||
r1c3.setEnabled(false); |
|||
r2c1.setEnabled(false); |
|||
r2c2.setEnabled(false); |
|||
r2c3.setEnabled(false); |
|||
r3c1.setEnabled(false); |
|||
r3c2.setEnabled(false); |
|||
r3c3.setEnabled(false); |
|||
} |
|||
public static void enableAll(){ |
|||
turnNumber = 0; |
|||
r1c1.setEnabled(true); |
|||
r1c2.setEnabled(true); |
|||
r1c3.setEnabled(true); |
|||
r2c1.setEnabled(true); |
|||
r2c2.setEnabled(true); |
|||
r2c3.setEnabled(true); |
|||
r3c1.setEnabled(true); |
|||
r3c2.setEnabled(true); |
|||
r3c3.setEnabled(true); |
|||
r1c1.setText(""); |
|||
r1c2.setText(""); |
|||
r1c3.setText(""); |
|||
r2c1.setText(""); |
|||
r2c2.setText(""); |
|||
r2c3.setText(""); |
|||
r3c1.setText(""); |
|||
r3c2.setText(""); |
|||
r3c3.setText(""); |
|||
panel.setEnabled(true); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|Javascript}}== |
|||
HTML5 Canvas implementation. Should play perfectly or near-perfectly. |
|||
<syntaxhighlight lang="javascript"> |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<title>TicTacToe</title> |
|||
</head> |
|||
<body> |
|||
<canvas id="canvas" width="400" height="400"></canvas> |
|||
<script> |
|||
//All helper functions |
|||
isBetween = (num, a, b) => { |
|||
return num >= a && num <= b; |
|||
} |
|||
randInt = (low, high) => { |
|||
return Math.floor(Math.random() * (high - low + 1)) + low; |
|||
} |
|||
choice = arr => { |
|||
return arr[randInt(0, arr.length - 1)]; |
|||
} |
|||
//Checks if every value in an array equals an item |
|||
equals = (arr, item) => { |
|||
return arr.filter(a => { |
|||
return a === item; |
|||
}).length === arr.length; |
|||
} |
|||
//Returns number of items in array that equal an item |
|||
equallen = (arr, item) => { |
|||
return arr.filter(a => { |
|||
return a === item; |
|||
}).length |
|||
} |
|||
//Checks if any value in the array equals an item |
|||
equalanyof = (arr, item) => { |
|||
return equallen(arr, item) > 0; |
|||
} |
|||
//Should be scalable, but it uses default elements for calculations and tracking |
|||
let canvas = document.getElementById("canvas"); |
|||
let ctx = canvas.getContext("2d"); |
|||
const width = canvas.width; |
|||
const blockSize = canvas.width / 3; |
|||
const lineSize = blockSize / 5; |
|||
//Draws background |
|||
ctx.fillStyle = "rgb(225, 225, 225)"; |
|||
ctx.fillRect(0, 0, 400, 400); |
|||
//Title page |
|||
ctx.fillStyle = "rgb(0, 0, 0)"; |
|||
ctx.font = width / (250 / 17) + "px Arial"; //34 |
|||
ctx.textAlign = "center"; |
|||
ctx.fillText("Tic Tac Toe", width / 2, width / (2 + 2 / 3)); //200, 150 |
|||
//Button for starting |
|||
ctx.fillStyle = "rgb(200, 200, 200)"; |
|||
ctx.fillRect(width / 3.2, width / 2, width / (2 + 2 / 3), width / 8); //125, 200, 150, 50 |
|||
ctx.fillStyle = "rgb(0, 0, 0)"; |
|||
ctx.font = width / (200 / 9) + "px Arial"; //18 |
|||
ctx.fillText("Start", width / 2, width / (40 / 23)); //200, 230 |
|||
//Uses an array so a forEach loop can scan it for the correct tile |
|||
let tileArray = []; //Contains all tiles |
|||
let available = []; //Contains only available tiles |
|||
class Tile { |
|||
constructor(x, y) { |
|||
this.x = x * blockSize; |
|||
this.y = y * blockSize; |
|||
this.state = "none"; |
|||
tileArray.push(this); |
|||
available.push(this); |
|||
} |
|||
draw() { |
|||
ctx.strokeStyle = "rgb(175, 175, 175)"; |
|||
ctx.lineWidth = blockSize / 10; |
|||
if (this.state === "X") { |
|||
ctx.beginPath(); |
|||
ctx.moveTo(this.x + blockSize / 4, this.y + blockSize / 4); |
|||
ctx.lineTo(this.x + blockSize / (4 / 3), this.y + blockSize / (4 / 3)); |
|||
ctx.moveTo(this.x + blockSize / 4, this.y + blockSize / (4 / 3)); |
|||
ctx.lineTo(this.x + blockSize / (4 / 3), this.y + blockSize / 4); |
|||
ctx.stroke(); |
|||
} else if (this.state === "O") { |
|||
ctx.beginPath(); |
|||
ctx.arc(this.x + blockSize / 2, this.y + blockSize / 2, blockSize / 4, 0, 2 * Math.PI); |
|||
ctx.stroke(); |
|||
} |
|||
//Removes this from the available array |
|||
const ind = available.indexOf(this); |
|||
available = available.slice(0, ind).concat(available.slice(ind + 1, available.length)); |
|||
} |
|||
} |
|||
//Defines the game |
|||
let game = { |
|||
state: "start", |
|||
turn: "Player", |
|||
player: "X", |
|||
opp: "O" |
|||
} |
|||
//Generates tiles |
|||
for (let x = 0; x < 3; x++) { |
|||
for (let y = 0; y < 3; y++) { |
|||
new Tile(x, y); |
|||
} |
|||
} |
|||
//Gets the mouse position |
|||
getMousePos = evt => { |
|||
let rect = canvas.getBoundingClientRect(); |
|||
return { |
|||
x: evt.clientX - rect.left, |
|||
y: evt.clientY - rect.top |
|||
} |
|||
} |
|||
//Checks for win conditions |
|||
checkCondition = () => { |
|||
//Local variables are created to make access easier |
|||
let as = tileArray[0].state; |
|||
let bs = tileArray[1].state; |
|||
let cs = tileArray[2].state; |
|||
let ds = tileArray[3].state; |
|||
let es = tileArray[4].state; |
|||
let fs = tileArray[5].state; |
|||
let gs = tileArray[6].state; |
|||
let hs = tileArray[7].state; |
|||
let is = tileArray[8].state; |
|||
//Equals function checks if each value in the array has a state of X or O |
|||
if (equals([as, bs, cs], "X") || equals([ds, es, fs], "X") || equals([gs, hs, is], "X") || |
|||
equals([as, ds, gs], "X") || equals([bs, es, hs], "X") || equals([cs, fs, is], "X") || |
|||
equals([as, es, is], "X") || equals([cs, es, gs], "X")) { |
|||
alert("Player wins!"); |
|||
game.state = "over"; |
|||
} else if (equals([as, bs, cs], "O") || equals([ds, es, fs], "O") || equals([gs, hs, is], "O") || |
|||
equals([as, ds, gs], "O") || equals([bs, es, hs], "O") || equals([cs, fs, is], "O") || |
|||
equals([as, es, is], "O") || equals([cs, es, gs], "O")) { |
|||
alert("Opponent wins!"); |
|||
game.state = "over"; |
|||
//It is a tie if none of the above conditions are fulfilled and there are no available tiles |
|||
} else if (available.length === 0) { |
|||
alert("It's a tie!"); |
|||
game.state = "over"; |
|||
} |
|||
} |
|||
//Controls the opponent. Uses many nested switches/if-else for efficiency |
|||
oppTurn = () => { |
|||
if (game.state === "game") { |
|||
let tile = 0; |
|||
//Similar local variables as the win checker |
|||
let at = tileArray[0].state; |
|||
let bt = tileArray[1].state; |
|||
let ct = tileArray[2].state; |
|||
let dt = tileArray[3].state; |
|||
let et = tileArray[4].state; |
|||
let ft = tileArray[5].state; |
|||
let gt = tileArray[6].state; |
|||
let ht = tileArray[7].state; |
|||
let it = tileArray[8].state; |
|||
let all = [at, bt, ct, dt, et, ft, gt, ht, it]; |
|||
/*The AI will automatically win if possible |
|||
I considered using a filter based system, but it was ugly and |
|||
inelegant, and also redundant |
|||
I used a nested if-else instead |
|||
Equallen checks how many values in the array equal the given value*/ |
|||
if (equallen(all, "O") >= 2) { |
|||
if (equallen([at, bt, ct], "O") === 2 && equallen([at, bt, ct], "X") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (bt === "none") { |
|||
tile = tileArray[1]; |
|||
} else if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} |
|||
} else if (equallen([dt, et, ft], "O") === 2 && equallen([dt, et, ft], "X") === 0) { |
|||
if (dt === "none") { |
|||
tile = tileArray[3]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (ft === "none") { |
|||
tile = tileArray[5]; |
|||
} |
|||
} else if (equallen([gt, ht, it], "O") === 2 && equallen([gt, ht, it], "X") === 0) { |
|||
if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} else if (ht === "none") { |
|||
tile = tileArray[7]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([at, dt, gt], "O") === 2 && equallen([at, dt, gt], "X") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (dt === "none") { |
|||
tile = tileArray[3]; |
|||
} else if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} |
|||
} else if (equallen([bt, et, ht], "O") === 2 && equallen([bt, et, ht], "X") === 0) { |
|||
if (bt === "none") { |
|||
tile = tileArray[1]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (ht === "none") { |
|||
tile = tileArray[7]; |
|||
} |
|||
} else if (equallen([ct, ft, it], "O") === 2 && equallen([ct, ft, it], "X") === 0) { |
|||
if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} else if (ft === "none") { |
|||
tile = tileArray[5]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([at, et, it], "O") === 2 && equallen([at, et, it], "X") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([ct, et, gt], "O") === 2 && equallen([ct, et, gt], "X") === 0) { |
|||
if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} |
|||
} |
|||
} |
|||
//Stops player from winning if possible |
|||
if (equallen(all, "X") >= 2 && tile === 0) { |
|||
if (equallen([at, bt, ct], "X") === 2 && equallen([at, bt, ct], "O") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (bt === "none") { |
|||
tile = tileArray[1]; |
|||
} else if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} |
|||
} else if (equallen([dt, et, ft], "X") === 2 && equallen([dt, et, ft], "O") === 0) { |
|||
if (dt === "none") { |
|||
tile = tileArray[3]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (ft === "none") { |
|||
tile = tileArray[5]; |
|||
} |
|||
} else if (equallen([gt, ht, it], "X") === 2 && equallen([gt, ht, it], "O") === 0) { |
|||
if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} else if (ht === "none") { |
|||
tile = tileArray[7]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([at, dt, gt], "X") === 2 && equallen([at, dt, gt], "O") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (dt === "none") { |
|||
tile = tileArray[3]; |
|||
} else if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} |
|||
} else if (equallen([bt, et, ht], "X") === 2 && equallen([bt, et, ht], "O") === 0) { |
|||
if (bt === "none") { |
|||
tile = tileArray[1]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (ht === "none") { |
|||
tile = tileArray[7]; |
|||
} |
|||
} else if (equallen([ct, ft, it], "X") === 2 && equallen([ct, ft, it], "O") === 0) { |
|||
if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} else if (ft === "none") { |
|||
tile = tileArray[5]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([at, et, it], "X") === 2 && equallen([at, et, it], "O") === 0) { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} |
|||
} else if (equallen([ct, et, gt], "X") === 2 && equallen([ct, et, gt], "O") === 0) { |
|||
if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} else if (et === "none") { |
|||
tile = tileArray[4]; |
|||
} else if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} |
|||
} |
|||
} |
|||
//Other options in case the above are not fulfilled |
|||
//Controls the course of play over the game |
|||
if (tile === 0) { |
|||
switch (9 - available.length) { |
|||
case 1: |
|||
//If the center is taken, it plays randomly in the corner |
|||
//Otherwise, it takes the center |
|||
if (et === "X") { |
|||
tile = tileArray[choice([0, 2, 6, 8])]; |
|||
} else { |
|||
tile = tileArray[4]; |
|||
} |
|||
break; |
|||
case 3: |
|||
if (et === "X" && (equalanyof([at, ct, gt, it], "O"))) { |
|||
/*To counter the strategy of |
|||
O - - |
|||
- X - |
|||
X - - |
|||
O - - |
|||
- X - |
|||
- - X |
|||
and related strategies*/ |
|||
if (at === "X") { |
|||
if (it === "none") { |
|||
tile = tileArray[8]; |
|||
} else { |
|||
tile = tileArray[choice([2, 6])]; |
|||
} |
|||
} else if (ct === "X") { |
|||
if (gt === "none") { |
|||
tile = tileArray[6]; |
|||
} else { |
|||
tile = tileArray[choice([0, 8])]; |
|||
} |
|||
} else if (gt === "X") { |
|||
if (ct === "none") { |
|||
tile = tileArray[2]; |
|||
} else { |
|||
tile = tileArray[choice([0, 8])]; |
|||
} |
|||
} else if (it === "X") { |
|||
if (at === "none") { |
|||
tile = tileArray[0]; |
|||
} else { |
|||
tile = tileArray[choice([2, 6])]; |
|||
} |
|||
} |
|||
} else { |
|||
tile = choice(tileArray); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
//Generates a random number if it could cause an error |
|||
if (tile.state != "none") { |
|||
tile = choice(available); |
|||
} |
|||
//Draws the selection |
|||
tile.state = game.opp; |
|||
tile.draw(); |
|||
checkCondition(); |
|||
game.turn = "Player"; |
|||
} |
|||
} |
|||
//Click handler |
|||
document.onclick = event => { |
|||
let pos = getMousePos(event); |
|||
switch (game.state) { |
|||
case "start": |
|||
//Checks if the button was clicked |
|||
if (isBetween(pos.x, width / 3.2, width / (16 / 11)) && isBetween(pos.y, width / 2, width / 1.6)) { |
|||
game.state = "game" |
|||
//Draws the setup for the game |
|||
ctx.fillStyle = "rgb(225, 225, 225)"; |
|||
ctx.fillRect(0, 0, 400, 400); |
|||
//Draws the lines |
|||
ctx.fillStyle = "rgb(200, 200, 200)"; |
|||
ctx.fillRect(blockSize - lineSize / 2, 0, lineSize, width); |
|||
ctx.fillRect(blockSize * 2 - lineSize / 2, 0, lineSize, width); |
|||
ctx.fillRect(0, blockSize - lineSize / 2, width, lineSize); |
|||
ctx.fillRect(0, blockSize * 2 - lineSize / 2, width, lineSize); |
|||
} |
|||
break; |
|||
case "game": |
|||
if (game.turn === "Player") { |
|||
//Goes through the tile array, checking if the click occurred there |
|||
tileArray.forEach(tile => { |
|||
if (isBetween(pos.x, tile.x, tile.x + blockSize) && isBetween(pos.y, tile.y, tile.y + blockSize)) { |
|||
if (available.indexOf(tile) != -1) { |
|||
tile.state = game.player; |
|||
tile.draw(); |
|||
checkCondition(); |
|||
game.turn = "Opponent"; |
|||
oppTurn(); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
</script> |
|||
</body> |
|||
</html> |
|||
</syntaxhighlight> |
|||
A Node.js implementation using strategy heuristics as defined in the Wikipedia page linked above. |
|||
Some of the steps can be embargoed until the board only has n plays left. This makes for a bit of |
|||
randomness in the gameplay. |
|||
Human is X and goes first. |
|||
<syntaxhighlight lang="javascript"> |
|||
// Board |
|||
const topLeft = 1; |
|||
const topMid = 2; |
|||
const topRight = 3; |
|||
const midLeft = 4; |
|||
const center = 5; |
|||
const midRight = 6; |
|||
const botLeft = 7; |
|||
const botMid = 8; |
|||
const botRight = 9; |
|||
const tiles = [ |
|||
topLeft, topMid, topRight, |
|||
midLeft, center, midRight, |
|||
botLeft, botMid, botRight |
|||
]; |
|||
const corners = [ |
|||
topLeft, topRight, |
|||
botLeft, botRight |
|||
]; |
|||
const sides = [ |
|||
topMid, |
|||
midLeft, midRight, |
|||
botMid |
|||
]; |
|||
const winningCombos = [ |
|||
[topLeft, topMid, topRight], |
|||
[midLeft, center, midRight], |
|||
[botLeft, botMid, botRight], |
|||
[topLeft, midLeft, botLeft], |
|||
[topMid, center, botMid], |
|||
[topRight, midRight, botRight], |
|||
[topLeft, center, botRight], |
|||
[topRight, center, botLeft], |
|||
]; |
|||
const board = new Map(); |
|||
// Utility |
|||
const reset = () => tiles.forEach(e => board.set(e, ' ')); |
|||
const repeat = (s, n) => Array(n).fill(s).join(''); |
|||
const fromBoard = e => board.get(e); |
|||
const notSpace = e => e !== ' '; |
|||
const occupied = e => notSpace(fromBoard(e)); |
|||
const isAvailable = e => !occupied(e); |
|||
const notString = s => e => fromBoard(e) !== s; |
|||
const containsOnly = s => a => a.filter(occupied).map(fromBoard).join('') === s; |
|||
const chooseRandom = a => a[Math.floor(Math.random() * a.length)]; |
|||
const legalPlays = () => tiles.filter(isAvailable); |
|||
const legalCorners = () => corners.filter(isAvailable); |
|||
const legalSides = () => sides.filter(isAvailable); |
|||
const opponent = s => s === 'X' ? 'O' : 'X'; |
|||
const hasElements = a => a.length > 0; |
|||
const compose = (...fns) => (...x) => fns.reduce((a, b) => c => a(b(c)))(...x); |
|||
const isDef = t => t !== undefined; |
|||
const flatten = a => a.reduce((p, c) => [...p, ...c], []); |
|||
const findShared = a => [...flatten(a).reduce((p, c) => |
|||
p.has(c) ? p.set(c, [...p.get(c), c]) : p.set(c, [c]), |
|||
new Map()).values()].filter(e => e.length > 1).map(e => e[0]); |
|||
const wrap = (f, s, p = 9) => n => { |
|||
if (isDef(n) || legalPlays().length > p) { |
|||
return n; |
|||
} |
|||
const r = f(n); |
|||
if (isDef(r)) { |
|||
console.log(`${s}: ${r}`); |
|||
} |
|||
return r; |
|||
}; |
|||
const drawBoard = () => console.log(` |
|||
${[fromBoard(topLeft), fromBoard(topMid), fromBoard(topRight)].join('|')} |
|||
-+-+- |
|||
${[fromBoard(midLeft), fromBoard(center), fromBoard(midRight)].join('|')} |
|||
-+-+- |
|||
${[fromBoard(botLeft), fromBoard(botMid), fromBoard(botRight)].join('|')} |
|||
`); |
|||
const win = s => () => { |
|||
if (winningCombos.find(containsOnly(repeat(s, 3)))) { |
|||
console.log(`${s} wins!`); |
|||
reset() |
|||
} else if (hasElements(legalPlays())) { |
|||
console.log(`${opponent(s)}s turn:`); |
|||
} else { |
|||
console.log('Draw!'); |
|||
reset(); |
|||
} |
|||
}; |
|||
const play = s => n => occupied(n) ? console.log('Illegal') : board.set(n, s); |
|||
// Available strategy steps |
|||
const attack = (s, t = 2) => () => { |
|||
const m = winningCombos.filter(containsOnly(repeat(s, t))); |
|||
if (hasElements(m)) { |
|||
return chooseRandom(chooseRandom(m).filter(notString(s))) |
|||
} |
|||
}; |
|||
const fork = (s, isDefence = false) => () => { |
|||
let result; |
|||
const p = winningCombos.filter(containsOnly(s)); |
|||
const forks = findShared(p).filter(isAvailable); |
|||
// On defence, when there is only one fork, play it, else choose a |
|||
// two-in-a row attack to force the opponent to not execute the fork. |
|||
if (forks.length > 1 && isDefence) { |
|||
const me = opponent(s); |
|||
const twoInRowCombos = winningCombos.filter(containsOnly(repeat(me, 1))); |
|||
const chooseFrom = twoInRowCombos.reduce((p, a) => { |
|||
const avail = a.filter(isAvailable); |
|||
avail.forEach((e, i) => { |
|||
board.set(e, me).set(i ? avail[i - 1] : avail[i + 1], opponent(me)); |
|||
winningCombos.filter(containsOnly(repeat(s, 2))).length < 2 |
|||
? p.push(e) |
|||
: undefined; |
|||
}); |
|||
avail.forEach(e => board.set(e, ' ')); |
|||
return p; |
|||
}, []); |
|||
result = hasElements(chooseFrom) |
|||
? chooseRandom(chooseFrom) |
|||
: attack(opponent(s), 1)() |
|||
} |
|||
return result || chooseRandom(forks); |
|||
}; |
|||
const defend = (s, t = 2) => attack(opponent(s), t); |
|||
const defendFork = s => fork(opponent(s), true); |
|||
const chooseCenter = () => isAvailable(center) ? center : undefined; |
|||
const chooseCorner = () => chooseRandom(legalCorners()); |
|||
const chooseSide = () => chooseRandom(legalSides()); |
|||
const randLegal = () => chooseRandom(legalPlays()); |
|||
// Implemented strategy |
|||
const playToWin = s => compose( |
|||
win(s), |
|||
drawBoard, |
|||
play(s), |
|||
wrap(randLegal, 'Chose random'), |
|||
wrap(chooseSide, 'Chose random side', 8), |
|||
wrap(chooseCorner, 'Chose random corner', 8), |
|||
wrap(chooseCenter, 'Chose center', 7), |
|||
wrap(defendFork(s), 'Defended fork'), |
|||
wrap(fork(s), 'Forked'), |
|||
wrap(defend(s), 'Defended'), |
|||
wrap(attack(s), 'Attacked') |
|||
); |
|||
// Prep players |
|||
const O = n => playToWin('O')(n); |
|||
const X = n => playToWin('X')(n); |
|||
// Begin |
|||
reset(); |
|||
console.log("Let's begin..."); |
|||
drawBoard(); |
|||
console.log('X Begins: Enter a number from 1 - 9'); |
|||
// Manage user input. |
|||
const standard_input = process.stdin; |
|||
const overLog = s => { |
|||
process.stdout.moveCursor(0, -9); |
|||
process.stdout.cursorTo(0); |
|||
process.stdout.clearScreenDown(); |
|||
process.stdout.write(s); |
|||
}; |
|||
standard_input.setEncoding('utf-8'); |
|||
standard_input.on('data', (data) => { |
|||
if (data === '\n') { |
|||
overLog('O: '); |
|||
O(); |
|||
} else { |
|||
overLog(`X: Plays ${data}`); |
|||
X(Number(data)); |
|||
} |
|||
}); |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Let's begin... |
|||
X: Plays 1 |
|||
O: Chose random corner: 7 |
|||
X: Plays 5 |
|||
O: Defended: 9 |
|||
X: Plays 8 |
|||
O: Defended: 2 |
|||
X: Plays 3 |
|||
O: Chose random side: 6 |
|||
X: Plays 4 |
|||
X|O|X |
|||
-+-+- |
|||
X|X|O |
|||
-+-+- |
|||
O|X|O |
|||
Draw! |
|||
</pre> |
|||
=={{header|jq}}== |
|||
'''Adapted from [[#Wren|Wren]]''' |
|||
'''Works with jq, the C implementation of jq''' |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
As with the Wren solution, in successive rounds, play alternates between the user (O) and the computer (X) being the first to move. When the computer is first to move, its first placement is selected at random. |
|||
As of this writing, the C and Go implementations of jq do not include a PRN generator, |
|||
and so in the following the MRG32k3a module available at [[:Category:jq/MRG32k3a.jq]] |
|||
is used. |
|||
<syntaxhighlight lang="jq"> |
|||
include "MRG32k3a" {search: "."}; # see comment above |
|||
### Generic functions |
|||
def inform(msg): |
|||
. as $in |
|||
| msg + "\n" | stderr |
|||
| $in; |
|||
def lpad($len): tostring | ($len - length) as $l | (" " * $l) + .; |
|||
# Create an m x n matrix with initial values specified by . |
|||
def matrix($m; $n): |
|||
if $m == 0 then [] |
|||
else . as $init |
|||
| if $m == 1 then [range(0;$n) | $init] |
|||
elif $m > 0 then |
|||
matrix(1; $n) as $row |
|||
| [range(0; $m) | $row ] |
|||
else error("matrix\($m);\($n)) invalid") |
|||
end |
|||
end; |
|||
### Tic-Tac-Toe |
|||
# The board is represented by a matrix in which: |
|||
# 0 : blank; -1: computer; 1: user |
|||
def init: |
|||
{b: (0 | matrix(3;3)), |
|||
bestI: 0, |
|||
bestJ: 0, |
|||
prng: seed(now | tostring | sub("^.*[.]";"") | tonumber)}; |
|||
# Output: 0 if undecided, null if game over, otherwise the player id |
|||
def checkWinner: |
|||
first(range(0; 3) as $i |
|||
| if .b[$i][0] != 0 and .b[$i][1] == .b[$i][0] and .b[$i][2] == .b[$i][0] then .b[$i][0] |
|||
elif .b[0][$i] != 0 and .b[1][$i] == .b[0][$i] and .b[2][$i] == .b[0][$i] then .b[0][$i] |
|||
else empty |
|||
end) |
|||
// if (.b[1][1] == 0) then 0 |
|||
elif (.b[1][1] == .b[0][0] and .b[2][2] == .b[0][0]) then .b[0][0] |
|||
elif (.b[1][1] == .b[2][0] and .b[0][2] == .b[1][1]) then .b[1][1] |
|||
elif all(.b[][]; . != 0) then null |
|||
else 0 |
|||
end ; |
|||
def showBoard: |
|||
["X", " ", "O"] as $t |
|||
| .b as $b |
|||
| range(0;3) as $i |
|||
| reduce range(0;3) as $j (""; |
|||
. + "\($t[$b[$i][$j] + 1]) " ), # b == -1 => "X"; b == 1 => "O" |
|||
"-----"; |
|||
# Examine possible moves of the player identified by $value, avoiding embarrassment |
|||
# Set .result, .bestI, .bestJ, etc |
|||
def testMove($value; $depth): |
|||
checkWinner as $score |
|||
| if $score == null then .result = 0 |
|||
elif $score != 0 |
|||
then .result = (if $score == $value then 1 else -1 end) |
|||
else .best = -1 |
|||
| .changed = 0 |
|||
| reduce range(0;3) as $i (.; |
|||
reduce range(0;3) as $j (.; |
|||
if .b[$i][$j] == 0 |
|||
then .b[$i][$j] = $value |
|||
| .changed = $value |
|||
| testMove(-$value; $depth + 1) as $test |
|||
| (-$test.result) as $score |
|||
| .b[$i][$j] = 0 |
|||
| if $score > .best |
|||
then if $depth == 0 |
|||
then .bestI = $i |
|||
| .bestJ = $j |
|||
end |
|||
| .best = $score |
|||
end |
|||
end ) ) |
|||
| .result = if .changed != 0 then .best else 0 end |
|||
end; |
|||
# Issue prompts on stderr; |
|||
# always allow q for quit |
|||
def read($prompt; $regex): |
|||
def r: |
|||
($prompt | stderr | empty), |
|||
(try ((input |
|||
| if . == "q" then halt |
|||
else select(test($regex)) |
|||
end) // r) |
|||
catch if . == "break" then halt else r end ); |
|||
r; |
|||
# Input: irrelevant |
|||
# $user should be boolean; specify `true` if the user plays first |
|||
def game($user): |
|||
"Board positions are numbered so:\n1 2 3\n4 5 6\n7 8 9", |
|||
"You have O, I have X.\n", |
|||
(init + {$user} |
|||
| label $out |
|||
| foreach range(0;9) as $k (.; |
|||
if .user |
|||
then .move = null |
|||
| until(.move; |
|||
(read("Your move: "; "^ *[1-9]") | tonumber - 1) as $move |
|||
| ($move/3|floor) as $i |
|||
| ($move % 3) as $j |
|||
| if .b[$i][$j] == 0 |
|||
then .b[$i][$j] = 1 |
|||
| .move = $move |
|||
end ) |
|||
end |
|||
| if (.user|not) |
|||
then # in the interests of entertainment, randomize if computer opens |
|||
if $k == 0 |
|||
then .prng |= nextFloat |
|||
| .bestI = (.prng.nextFloat * 100 | trunc) % 3 |
|||
| .prng |= nextFloat |
|||
| .bestJ = (.prng.nextFloat * 100 | trunc) % 3 |
|||
else testMove(-1; 0) |
|||
end |
|||
| .b[.bestI][.bestJ] = -1 |
|||
| inform("My move: \(.bestI * 3 + .bestJ + 1)") |
|||
end |
|||
| .user |= not |
|||
; |
|||
# Output: |
|||
showBoard, |
|||
(checkWinner |
|||
| if . == 0 then empty |
|||
else (if . == 1 then "You win" |
|||
elif . == -1 then "I win" |
|||
else "A draw.\n\n" |
|||
end), |
|||
break $out |
|||
end ) )) ; |
|||
# Alternate players |
|||
def controller: |
|||
def c($user): |
|||
game($user), |
|||
(read("Play again? [yn]: "; "^[yYnN]") as $in |
|||
| if $in|.[0:1]|ascii_downcase == "n" then halt |
|||
else c($user|not) |
|||
end); |
|||
c(true); |
|||
controller |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
Invocation: jq -nRrf tic-tac-toe.jq |
|||
<pre> |
|||
Board positions are numbered so: |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
You have O, I have X. |
|||
Your move: 5 |
|||
----- |
|||
O |
|||
----- |
|||
----- |
|||
My move: 1 |
|||
X |
|||
----- |
|||
O |
|||
----- |
|||
----- |
|||
Your move: |
|||
... etc ... |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
One move look-ahead algorithm. Computer plays to win or at least draw. |
|||
<syntaxhighlight lang="julia">const winningpositions = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], |
|||
[2, 5, 8], [3, 6, 9],[1, 5, 9], [7, 5, 3]] |
|||
function haswon(brd, xoro) |
|||
marked = findall(x -> x == xoro, brd) |
|||
for pos in winningpositions |
|||
if length(pos) <= length(marked) && pos == sort(marked)[1:3] |
|||
return true |
|||
end |
|||
end |
|||
false |
|||
end |
|||
function readcharwithprompt(prompt, expected) |
|||
ret = '*' |
|||
while !(ret in expected) |
|||
print("\n", prompt, " -> ") |
|||
ret = lowercase(chomp(readline()))[1] |
|||
end |
|||
ret |
|||
end |
|||
availablemoves(brd) = findall(x -> x == ' ', brd) |
|||
cornersopen(brd) = [x for x in [1, 3, 7, 9] if brd[x] == ' '] |
|||
int2char(x) = Char(x + UInt8('0')) |
|||
char2int(x) = UInt8(x) - UInt8('0') |
|||
getyn(query) = readcharwithprompt(query, ['y', 'n']) |
|||
gettheirmove(brd) = char2int(readcharwithprompt("Your move(1-9)", int2char.(availablemoves(brd)))) |
|||
function findwin(brd, xoro) |
|||
tmpbrd = deepcopy(brd) |
|||
for mv in availablemoves(tmpbrd) |
|||
tmpbrd[mv] = xoro |
|||
if haswon(tmpbrd, xoro) |
|||
return mv |
|||
end |
|||
tmpbrd[mv] = ' ' |
|||
end |
|||
return nothing |
|||
end |
|||
function choosemove(brd, mychar, theirchar) |
|||
if all(x -> x == ' ', brd) |
|||
brd[rand(cornersopen(brd))] = mychar # corner trap if starting game |
|||
elseif availablemoves(brd) == [] # no more moves |
|||
println("Game is over. It was a draw.") |
|||
exit(0) |
|||
elseif (x = findwin(brd, mychar)) != nothing || (x = findwin(brd, theirchar)) != nothing |
|||
brd[x] = mychar # win if possible, block their win otherwise if their win is possible |
|||
elseif brd[5] == ' ' |
|||
brd[5] = mychar # take center if open and not doing corner trap |
|||
elseif (corners = cornersopen(brd)) != [] |
|||
brd[rand(corners)] = mychar # choose a corner over a side middle move |
|||
else |
|||
brd[rand(availablemoves(brd))] = mychar # random otherwise |
|||
end |
|||
end |
|||
function display(brd) |
|||
println("+-----------+") |
|||
println("| ", brd[1], " | ", brd[2], " | ", brd[3], " |") |
|||
println("| ", brd[4], " | ", brd[5], " | ", brd[6], " |") |
|||
println("| ", brd[7], " | ", brd[8], " | ", brd[9], " |") |
|||
println("+-----------+") |
|||
end |
|||
function tictactoe() |
|||
board = fill(' ', 9) |
|||
println("Board move grid:\n 1 2 3\n 4 5 6\n 7 8 9") |
|||
yn = getyn("Would you like to move first (y/n)?") |
|||
if yn == 'y' |
|||
mychar = 'O' |
|||
theirchar = 'X' |
|||
board[gettheirmove(board)] = theirchar |
|||
else |
|||
mychar = 'X' |
|||
theirchar = 'O' |
|||
end |
|||
while true |
|||
choosemove(board, mychar, theirchar) |
|||
println("Computer has moved.") |
|||
display(board) |
|||
if haswon(board, mychar) |
|||
println("Game over. Computer wins!") |
|||
exit(0) |
|||
elseif availablemoves(board) == [] |
|||
break |
|||
end |
|||
board[gettheirmove(board)] = theirchar |
|||
println("Player has moved.") |
|||
display(board) |
|||
if haswon(board, theirchar) |
|||
println("Game over. Player wins!") |
|||
exit(0) |
|||
elseif availablemoves(board) == [] |
|||
break |
|||
end |
|||
end |
|||
println("Game over. It was a draw.") |
|||
end |
|||
tictactoe() |
|||
</syntaxhighlight> {{output}} <pre> |
|||
Board move grid: |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
Would you like to move first (y/n)? -> y |
|||
Your move(1-9) -> 5 |
|||
Computer has moved. |
|||
+-----------+ |
|||
| | | | |
|||
| | X | | |
|||
| O | | | |
|||
+-----------+ |
|||
Your move(1-9) -> 1 |
|||
Player has moved. |
|||
+-----------+ |
|||
| X | | | |
|||
| | X | | |
|||
| O | | | |
|||
+-----------+ |
|||
Computer has moved. |
|||
+-----------+ |
|||
| X | | | |
|||
| | X | | |
|||
| O | | O | |
|||
+-----------+ |
|||
Your move(1-9) -> 3 |
|||
Player has moved. |
|||
+-----------+ |
|||
| X | | X | |
|||
| | X | | |
|||
| O | | O | |
|||
+-----------+ |
|||
Computer has moved. |
|||
+-----------+ |
|||
| X | | X | |
|||
| | X | | |
|||
| O | O | O | |
|||
+-----------+ |
|||
Game over. Computer wins! |
|||
</pre> |
|||
=={{header|Koka}}== |
|||
Effectful Version |
|||
<syntaxhighlight lang="koka"> |
|||
import std/os/readline |
|||
fun member(x: a, xs: list<a>, compare: (a, a) -> bool) : bool |
|||
match xs |
|||
Nil -> False |
|||
Cons(y, ys) -> x.compare(y) || member(x, ys,compare) |
|||
fun member(xs: list<a>, x: a, compare: (a, a) -> bool) : bool |
|||
x.member(xs, compare) |
|||
struct coord |
|||
x: int |
|||
y: int |
|||
fun show(c: coord) : string { |
|||
"(" ++ c.x.show() ++ ", " ++ c.y.show() ++ ")" |
|||
} |
|||
fun (==)(c1: coord, c2: coord) : bool |
|||
c1.x == c2.x && c1.y == c2.y |
|||
effect ctl eject(): a |
|||
fun parse(str : string, moves : list<coord>) : <console, exn|_e>coord |
|||
match str.list() |
|||
Cons('0', Cons(' ', Cons('0', Nil))) -> Coord(0,0) |
|||
Cons('0', Cons(' ', Cons('1', Nil))) -> Coord(0,1) |
|||
Cons('0', Cons(' ', Cons('2', Nil))) -> Coord(0,2) |
|||
Cons('1', Cons(' ', Cons('0', Nil))) -> Coord(1,0) |
|||
Cons('1', Cons(' ', Cons('1', Nil))) -> Coord(1,1) |
|||
Cons('1', Cons(' ', Cons('2', Nil))) -> Coord(1,2) |
|||
Cons('2', Cons(' ', Cons('0', Nil))) -> Coord(2,0) |
|||
Cons('2', Cons(' ', Cons('1', Nil))) -> Coord(2,1) |
|||
Cons('2', Cons(' ', Cons('2', Nil))) -> Coord(2,2) |
|||
Cons('0', Cons(',', Cons('0', Nil))) -> Coord(0,0) |
|||
Cons('0', Cons(',', Cons('1', Nil))) -> Coord(0,1) |
|||
Cons('0', Cons(',', Cons('2', Nil))) -> Coord(0,2) |
|||
Cons('1', Cons(',', Cons('0', Nil))) -> Coord(1,0) |
|||
Cons('1', Cons(',', Cons('1', Nil))) -> Coord(1,1) |
|||
Cons('1', Cons(',', Cons('2', Nil))) -> Coord(1,2) |
|||
Cons('2', Cons(',', Cons('0', Nil))) -> Coord(2,0) |
|||
Cons('2', Cons(',', Cons('1', Nil))) -> Coord(2,1) |
|||
Cons('2', Cons(',', Cons('2', Nil))) -> Coord(2,2) |
|||
Cons('q', Nil) -> eject() |
|||
_ -> |
|||
println("Invalid move, please try again") |
|||
gen_move(moves) |
|||
fun gen_move(moves : list<coord>) : <console, exn|_e> coord |
|||
val move : coord = parse(readline(), moves) |
|||
if moves.any() fn (c : coord) { c == move } then { |
|||
println("Invalid move, please try again") |
|||
gen_move(moves) |
|||
} else { |
|||
move |
|||
} |
|||
fun create-board() : list<list<char>> { |
|||
[['.','.','.'],['.','.','.'],['.','.','.']] |
|||
} |
|||
fun show(grid: list<list<char>>) : string |
|||
var line := 0 |
|||
grid.foldl(" 0 1 2\n") fn(acc, row: list<char>) |
|||
val out = row.foldl(acc ++ line.show() ++ " ") fn(acc, col: char) |
|||
acc ++ col.string() ++ " " |
|||
++ "\n" |
|||
line := line + 1 |
|||
out |
|||
fun get_board_position(board : list<list<char>>, coord : coord) : maybe<char> { |
|||
match board[coord.y] { |
|||
Nothing -> Nothing |
|||
Just(row) -> row[coord.x] |
|||
} |
|||
} |
|||
fun mark_board(board: list<list<char>>,coord: coord, mark: char): maybe<list<list<char>>> |
|||
val new_row: maybe<list<char>> = match board[coord.y] |
|||
Nothing -> Nothing |
|||
Just(row) -> Just(row.take(coord.x) ++ mark.Cons(row.drop(coord.x + 1))) |
|||
match new_row |
|||
Nothing -> Nothing |
|||
Just(row) -> Just(board.take(coord.y) ++ row.Cons(board.drop(coord.y + 1))) |
|||
effect ctl not_full() : () |
|||
fun check_full(board: list<list<char>>) : bool |
|||
fun helper() |
|||
var full := True |
|||
board.foreach() fn(row) |
|||
if '.'.member(row) fn (a, b) a == b then { |
|||
not_full() |
|||
} |
|||
full |
|||
with ctl not_full() False |
|||
helper() |
|||
fun check_win(board: list<list<char>>, mark: char) : <div, exn|_e>bool |
|||
var win := False |
|||
var i := 0 |
|||
while {i < 3} { |
|||
if board.get_board_position(Coord(i,0)).unwrap() == mark && board.get_board_position(Coord(i,1)).unwrap() == mark && board.get_board_position(Coord(i,2)).unwrap() == mark then { |
|||
win := True |
|||
} |
|||
if board.get_board_position(Coord(0,i)).unwrap() == mark && board.get_board_position(Coord(1,i)).unwrap() == mark && board.get_board_position(Coord(2,i)).unwrap() == mark then { |
|||
win := True |
|||
} |
|||
i := i + 1 |
|||
} |
|||
if board.get_board_position(Coord(0,0)).unwrap() == mark && board.get_board_position(Coord(1,1)).unwrap() == mark && board.get_board_position(Coord(2,2)).unwrap() == mark then { |
|||
win := True |
|||
} |
|||
if board.get_board_position(Coord(0,2)).unwrap() == mark && board.get_board_position(Coord(1,1)).unwrap() == mark && board.get_board_position(Coord(2,0)).unwrap() == mark then { |
|||
win := True |
|||
} |
|||
win |
|||
fun human_logic(board: list<list<char>>, moves: list<coord>, mark: char, other_mark: char) : <console,div,exn|_e> coord |
|||
gen_move(moves) |
|||
fun ai_logic(board: list<list<char>>, moves: list<coord>, mark: char, other_mark: char) |
|||
board.gen_ai_move(moves,mark, other_mark) |
|||
struct move |
|||
move : coord |
|||
score: int |
|||
fun (==)(m1 : move, m2 : move) : bool { |
|||
m1.move == m2.move && m1.score == m2.score |
|||
} |
|||
fun eval_board(board : list<list<char>>, mark: char, other-mark : char) { |
|||
if board.check_win(mark) then { |
|||
10 |
|||
} |
|||
elif board.check_win(other-mark) then { |
|||
-10 |
|||
} |
|||
else { |
|||
0 |
|||
} |
|||
} |
|||
fun maximum-move(a : move, b : move) : move { |
|||
if a.score > b.score then { |
|||
a |
|||
} |
|||
else { |
|||
b |
|||
} |
|||
} |
|||
fun gen_ai_move(board : list<list<char>>, moves : list<coord>, mark : char, other-mark : char) { |
|||
val best_move : move = Move(Coord(-1,-1), -1000) |
|||
[0,1,2].foldl(best_move) fn (bstMove : move, i : int) { |
|||
[0,1,2].foldl(bstMove) fn (bstMve : move, j : int) { |
|||
if Coord(i,j).member(moves) fn (a,b) {a == b} then { |
|||
bstMve |
|||
} |
|||
else { |
|||
val new_board : list<list<char>> = board.mark_board(Coord(i,j), mark).unwrap() |
|||
val new-max = maximum-move(bstMve, Move(Coord(i,j), new_board.minimax(moves ++ [Coord(i,j)], 0, False, mark, other-mark))) |
|||
new-max |
|||
} |
|||
} |
|||
}.move |
|||
} |
|||
fun unwrap(x: maybe<a>): <exn> a |
|||
match x |
|||
Just(a) -> a |
|||
Nothing -> throw("value was Nothing") |
|||
// A basic implementation of Minimax |
|||
// This uses brace style to show that it is possible |
|||
fun minimax(board : list<list<char>>, moves: list<coord>, depth : int, isMaximizingPlayer : bool, mark : char, other-mark : char) : <div,exn|_e> int { |
|||
val score : int = board.eval_board(mark, other-mark) |
|||
if score == 10 then { |
|||
score |
|||
} |
|||
elif score == -10 then { |
|||
score |
|||
} |
|||
elif board.check_full() then { |
|||
0 |
|||
} |
|||
else { |
|||
if isMaximizingPlayer then { |
|||
val bestVal: int = -1000 |
|||
[0,1,2].foldl(bestVal) fn (bstVal : int, i : int) { |
|||
[0,1,2].foldl(bstVal) fn (bstVl : int, j : int) { |
|||
if Coord(i,j).member(moves) fn(a, b) {a == b} then { |
|||
bstVl |
|||
} |
|||
else { |
|||
val new_board : list<list<char>> = board.mark_board(Coord(i,j), mark).unwrap() |
|||
val value : int = new_board.minimax(moves ++ [Coord(i,j)], depth + 1, !isMaximizingPlayer, mark, other-mark) |
|||
max(bstVl, value) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
val bestVal: int = 1000 |
|||
[0,1,2].foldl(bestVal) fn (bstVal : int, i : int) { |
|||
[0,1,2].foldl(bstVal) fn (bstVl : int, j : int) { |
|||
if Coord(i,j).member(moves) fn(a,b) {a == b} then { |
|||
bstVl |
|||
} |
|||
else { |
|||
val new_board : list<list<char>> = board.mark_board(Coord(i,j), other-mark).unwrap() |
|||
val value : int = new_board.minimax(moves ++ [Coord(i,j)], depth + 1, !isMaximizingPlayer, mark, other-mark) |
|||
min(bstVl, value) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
// The main business logic of the entire game |
|||
// This function checks if there is a draw or a win |
|||
fun play_game() |
|||
val board = get_board() |
|||
if board.check_full() then |
|||
println("Draw!") |
|||
println("Final board:") |
|||
board.show().println |
|||
else |
|||
"Next Turn:".println |
|||
board.show().println |
|||
val current_mark = get_current_mark() |
|||
val other_mark = get_other_mark() |
|||
play_turn(current_mark, other_mark) |
|||
val new_board = get_board() |
|||
if new_board.check_win(current_mark) then |
|||
println("Player " ++ current_mark.show() ++ " wins!") |
|||
println("Final board:") |
|||
new_board.show().println |
|||
else |
|||
flip() |
|||
play_game() |
|||
effect human_turn |
|||
fun play_human_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord |
|||
effect ai_turn |
|||
fun play_ai_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord |
|||
effect player1 |
|||
fun player1_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord |
|||
fun get_player1_mark() : char |
|||
effect player2 |
|||
fun player2_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord |
|||
fun get_player2_mark() : char |
|||
effect turn |
|||
// Changes the current player to a different one |
|||
fun flip() : () |
|||
// Calls the appropriate code for the current player |
|||
fun play_turn(mark: char, other_mark: char) : () |
|||
// Gets the symbol of the active player |
|||
fun get_current_mark(): char |
|||
// Gets the symbol of the inactive player |
|||
fun get_other_mark(): char |
|||
// This allows us to access the board |
|||
fun get_board() : list<list<char>> |
|||
// This allows us to access the moves that have been made |
|||
fun get_moves(): list<coord> |
|||
type player |
|||
Human |
|||
AI |
|||
type players |
|||
One |
|||
Two |
|||
// This function encapsulates the state of the entire game |
|||
// Think of it as calling a method on an object but with pure functions |
|||
fun initialize_and_start_game(game_type: int) |
|||
var current_player := One |
|||
var current_board := create-board() |
|||
var all_moves := [] |
|||
var player1 := Human |
|||
var player2 := AI |
|||
match game_type |
|||
1 -> { |
|||
player1 := Human |
|||
player2 := Human |
|||
} |
|||
2 -> { |
|||
player1 := Human |
|||
player2 := AI |
|||
} |
|||
3 -> { |
|||
player1 := AI |
|||
player2 := AI |
|||
} |
|||
_ -> throw("invalid game type") |
|||
with handler |
|||
ctl eject() |
|||
println("Have a nice day.") |
|||
with fun play_human_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) |
|||
human_logic(pair.fst,pair.snd,marks.fst,marks.snd) |
|||
with fun play_ai_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) |
|||
ai_logic(pair.fst,pair.snd,marks.fst,marks.snd) |
|||
with handler |
|||
fun player1_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) |
|||
match player1 |
|||
Human -> play_human_turn(pair, marks) |
|||
AI -> play_ai_turn(pair, marks) |
|||
fun get_player1_mark() |
|||
'X' |
|||
with handler |
|||
fun player2_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) |
|||
match player2 |
|||
Human -> play_human_turn(pair, marks) |
|||
AI -> play_ai_turn(pair, marks) |
|||
fun get_player2_mark() |
|||
'O' |
|||
with handler |
|||
return(x) () |
|||
fun flip() |
|||
match current_player |
|||
One -> current_player := Two |
|||
Two -> current_player := One |
|||
fun play_turn(mark: char, other_mark: char) |
|||
match current_player |
|||
One -> { |
|||
val coord = player1_turn((current_board, all_moves), (mark, other_mark)) |
|||
current_board := mark_board(current_board,coord,get_player1_mark()).unwrap |
|||
all_moves := Cons(coord, all_moves) |
|||
() |
|||
} |
|||
Two -> { |
|||
val coord = player2_turn((current_board, all_moves), (mark, other_mark)) |
|||
current_board := mark_board(current_board,coord,get_player2_mark()).unwrap |
|||
all_moves := Cons(coord, all_moves) |
|||
() |
|||
} |
|||
fun get_current_mark() match current_player |
|||
One -> get_player1_mark() |
|||
Two -> get_player2_mark() |
|||
fun get_other_mark() match current_player |
|||
Two -> get_player1_mark() |
|||
One -> get_player2_mark() |
|||
fun get_board() current_board |
|||
fun get_moves() all_moves |
|||
play_game() |
|||
fun prompt_game_type() |
|||
match readline().list() |
|||
Cons('1', Nil) -> 1 |
|||
Cons('2', Nil) -> 2 |
|||
Cons('3', Nil) -> 3 |
|||
_ -> |
|||
println("Invalid game mode, please try again") |
|||
prompt_game_type() |
|||
fun main () |
|||
println("Welcome to Tic Tac Toe!") |
|||
println("Please select a game mode:") |
|||
println("1. Two player") |
|||
println("2. One player") |
|||
println("3. Zero player") |
|||
println("Enter the number of the game mode you want to play") |
|||
val game_type = prompt_game_type() |
|||
"Enter your input as 'x y' or 'x,y' when selecting a spot".println |
|||
initialize_and_start_game(game_type) |
|||
</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="scala">// version 1.1.51 |
|||
import java.util.Random |
|||
val r = Random() |
|||
val b = Array(3) { IntArray(3) } // board -> 0: blank; -1: computer; 1: human |
|||
var bestI = 0 |
|||
var bestJ = 0 |
|||
fun checkWinner(): Int { |
|||
for (i in 0..2) { |
|||
if (b[i][0] != 0 && b[i][1] == b[i][0] && b[i][2] == b[i][0]) return b[i][0] |
|||
if (b[0][i] != 0 && b[1][i] == b[0][i] && b[2][i] == b[0][i]) return b[0][i] |
|||
} |
|||
if (b[1][1] == 0) 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 |
|||
} |
|||
fun showBoard() { |
|||
val t = "X O" |
|||
for (i in 0..2) { |
|||
for (j in 0..2) print("${t[b[i][j] + 1]} ") |
|||
println() |
|||
} |
|||
println("-----") |
|||
} |
|||
fun testMove(value: Int, depth: Int): Int { |
|||
var best = -1 |
|||
var changed = 0 |
|||
var score = checkWinner() |
|||
if (score != 0) return if (score == value) 1 else -1 |
|||
for (i in 0..2) { |
|||
for (j in 0..2) { |
|||
if (b[i][j] != 0) continue |
|||
b[i][j] = value |
|||
changed = value |
|||
score = -testMove(-value, depth + 1) |
|||
b[i][j] = 0 |
|||
if (score <= best) continue |
|||
if (depth == 0) { |
|||
bestI = i |
|||
bestJ = j |
|||
} |
|||
best = score |
|||
} |
|||
} |
|||
return if (changed != 0) best else 0 |
|||
} |
|||
fun game(user: Boolean): String { |
|||
var u = user |
|||
for (i in 0..2) b[i].fill(0) |
|||
print("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n") |
|||
print("You have O, I have X.\n\n") |
|||
for (k in 0..8) { |
|||
while (u) { |
|||
var move: Int? |
|||
do { |
|||
print("Your move: ") |
|||
move = readLine()!!.toIntOrNull() |
|||
} |
|||
while (move != null && move !in 1..9) |
|||
move = move!! - 1 |
|||
val i = move / 3 |
|||
val j = move % 3 |
|||
if (b[i][j] != 0) continue |
|||
b[i][j] = 1 |
|||
break |
|||
} |
|||
if (!u) { |
|||
if (k == 0) { // randomize if computer opens, less boring |
|||
bestI = r.nextInt(Int.MAX_VALUE) % 3 |
|||
bestJ = r.nextInt(Int.MAX_VALUE) % 3 |
|||
} |
|||
else testMove(-1, 0) |
|||
b[bestI][bestJ] = -1 |
|||
val myMove = bestI * 3 + bestJ + 1 |
|||
println("My move: $myMove") |
|||
} |
|||
showBoard() |
|||
val win = checkWinner() |
|||
if (win != 0) return (if (win == 1) "You win" else "I win") + ".\n\n" |
|||
u = !u |
|||
} |
|||
return "A draw.\n\n" |
|||
} |
|||
fun main(args: Array<String>) { |
|||
var user = false |
|||
while (true) { |
|||
user = !user |
|||
print(game(user)) |
|||
var yn: String |
|||
do { |
|||
print("Play again y/n: ") |
|||
yn = readLine()!!.toLowerCase() |
|||
} |
|||
while (yn != "y" && yn != "n") |
|||
if (yn != "y") return |
|||
println() |
|||
} |
|||
}</syntaxhighlight> |
|||
Sample game: |
|||
<pre> |
|||
Board postions are numbered so: |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
You have O, I have X. |
|||
Your move: 2 |
|||
O |
|||
----- |
|||
My move: 1 |
|||
X O |
|||
----- |
|||
Your move: 8 |
|||
X O |
|||
O |
|||
----- |
|||
My move: 5 |
|||
X O |
|||
X |
|||
O |
|||
----- |
|||
Your move: 9 |
|||
X O |
|||
X |
|||
O O |
|||
----- |
|||
My move: 7 |
|||
X O |
|||
X |
|||
X O O |
|||
----- |
|||
Your move: 3 |
|||
X O O |
|||
X |
|||
X O O |
|||
----- |
|||
My move: 4 |
|||
X O O |
|||
X X |
|||
X O O |
|||
----- |
|||
I win. |
|||
Play again y/n: n |
|||
</pre> |
|||
=={{header|Lasso}}== |
|||
{{incomplete|Lasso|Computer doesn't ''play'' - it merely manages the board.}} |
|||
This example uses an HTML form for the UI, buttons representing the game state, and Lasso's built inn session handler to keep track of who's turn it is, what the game matrix state is, and the winner history. |
|||
As image uploads has been disabled, a live version can be viewed at: [http://jono.guthrie.net.nz/rosetta/Tic-tac-toe.lasso http://jono.guthrie.net.nz/rosetta/Tic-tac-toe.lasso] |
|||
<syntaxhighlight lang="lasso">[ |
|||
session_start('user') |
|||
session_addvar('user', 'matrix') |
|||
session_addvar('user', 'winrecord') |
|||
session_addvar('user', 'turn') |
|||
var(matrix)->isNotA(::array) ? var(matrix = array('-','-','-','-','-','-','-','-','-')) |
|||
var(winrecord)->isNotA(::array) ? var(winrecord = array) |
|||
var(turn)->isNotA(::string) ? var(turn = 'x') |
|||
if(web_request->params->asStaticArray >> 'reset') => { |
|||
$matrix = array('-','-','-','-','-','-','-','-','-') |
|||
$turn = 'x' |
|||
} |
|||
with i in web_request->params->asStaticArray do => { |
|||
if(#i->name->beginswith('p')) => { |
|||
local(num = #i->name->asCopy) |
|||
#num->removeLeading('p') |
|||
#num = integer(#num) |
|||
#num > 0 && $matrix->get(#num) == '-' ? $matrix->get(#num) = #i->value |
|||
$turn == 'o' ? $turn = 'x' | $turn = 'o' |
|||
} |
|||
} |
|||
local( |
|||
istie = false, |
|||
winner = 'noone', |
|||
clear = false |
|||
) |
|||
// determine if we have a winner |
|||
if($matrix->find('-')->size < 9) => { |
|||
local(winners = array('123','456','789','147','258','369','159','357')) |
|||
loop(8) => { |
|||
local(xscore = 0,oscore = 0,use = #winners->get(loop_count)) |
|||
with v in #use->values do => { |
|||
$matrix->findposition('x') >> integer(#v) ? #xscore++ |
|||
$matrix->findposition('o') >> integer(#v) ? #oscore++ |
|||
} |
|||
if(#xscore == 3) => { |
|||
#winner = 'x' |
|||
$winrecord->insert('x') |
|||
#clear = true |
|||
loop_abort |
|||
} |
|||
if(#oscore == 3) => { |
|||
#winner = 'o' |
|||
$winrecord->insert('o') |
|||
#clear = true |
|||
loop_abort |
|||
} |
|||
} |
|||
} |
|||
// determine if tie |
|||
if(not $matrix->find('-')->size && #winner == 'noone') => { |
|||
#istie = true |
|||
#winner = 'tie' |
|||
$winrecord->insert('tie') |
|||
#clear = true |
|||
} |
|||
] |
|||
<form action="?" method="post"> |
|||
<table> |
|||
<tr> |
|||
[loop(3) => {^]<td><button name="p[loop_count]" value="[$turn]"[ |
|||
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"' |
|||
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}] |
|||
</tr> |
|||
<tr> |
|||
[loop(-from=4,-to=6) => {^]<td><button name="p[loop_count]" value="[$turn]"[ |
|||
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"' |
|||
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}] |
|||
</tr> |
|||
<tr> |
|||
[loop(-from=7,-to=9) => {^]<td><button name="p[loop_count]" value="[$turn]"[ |
|||
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"' |
|||
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}] |
|||
</tr> |
|||
</table> |
|||
</form> |
|||
[if(#istie && #winner == 'tie')] |
|||
<p><b>It's a tie!</b></p> |
|||
[else(#winner != 'noone')] |
|||
<p>[#winner->uppercase&] won! Congratulations.</p> |
|||
[else]<math>Insert formula here</math> |
|||
<p>It is now [$turn]'s turn!</p> |
|||
[/if] |
|||
<p><a href="?reset">Reset</a></p> |
|||
[if($winrecord->size)]<p>Win record: [$winrecord->join(', ')]</p>[/if] |
|||
[if(#clear == true) => { |
|||
$matrix = array('-','-','-','-','-','-','-','-','-') |
|||
$turn = 'x' |
|||
}]</syntaxhighlight> |
|||
=={{header|Lingo}}== |
|||
The standard way to create GUI apps in Lingo is to use the authoring tool "Director" as GUI builder. The code below instead uses a simple framework (stored in global "$") that eases programmatic GUI creation.<br /> |
|||
Screenshot of application window: http://valentin.dasdeck.com/lingo/tic-tac-toe/tic-tac-toe-lingo.png<br /> |
|||
"Human" cannot win this game. |
|||
<syntaxhighlight lang="lingo">global $ -- object representing simple framework |
|||
global gBoard -- current board image |
|||
global gBoardTemplate -- empty board image |
|||
global gHumanChip -- cross image |
|||
global gComputerChip -- circle image |
|||
global gM -- 3x3 matrix storing game state: 0=free cell, 1=human cell, -1=computer cell |
|||
global gStep -- index of current move (1..9) |
|||
global gGameOverFlag -- TRUE if current game is over |
|||
---------------------------------------- |
|||
-- Entry point |
|||
---------------------------------------- |
|||
on startMovie |
|||
-- libs |
|||
$.import("sprite") |
|||
-- window properties |
|||
_movie.stage.title = "Tic-Tac-Toe" |
|||
_movie.stage.rect = rect(0, 0, 224, 310) |
|||
_movie.centerStage = TRUE |
|||
-- load images from filesystem |
|||
m = new(#bitmap) |
|||
m.importFileInto($.@("resources/cross.bmp"), [#trimWhiteSpace:FALSE]) |
|||
gHumanChip = m.image |
|||
m = new(#bitmap) |
|||
m.importFileInto($.@("resources/circle.bmp"), [#trimWhiteSpace:FALSE]) |
|||
gComputerChip = m.image |
|||
-- create GUI |
|||
m = new(#bitmap) |
|||
m.importFileInto($.@("resources/board.bmp")) |
|||
m.regpoint = point(0, 0) |
|||
s = $.sprite.make(m, [#loc:point(20, 20)], TRUE) |
|||
s.addListener(#mouseDown, _movie, #humanMove) |
|||
gBoard = m.image |
|||
gBoardTemplate = gBoard.duplicate() |
|||
m = $.sprite.newMember(#button, [#text:"New Game (Human starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)]) |
|||
s = $.sprite.make(m, [#loc:point(20, 220)], TRUE) |
|||
s.addListener(#mouseDown, _movie, #newGame, 1) |
|||
m = $.sprite.newMember(#button, [#text:"New Game (Computer starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)]) |
|||
s = $.sprite.make(m, [#loc:point(20, 250)], TRUE) |
|||
s.addListener(#mouseDown, _movie, #newGame, -1) |
|||
m = $.sprite.newMember(#field, [#name:"feedback", #editable:FALSE, #fontstyle:"bold", #alignment:"center",\ |
|||
#border:0, #color:rgb(255, 0, 0), #rect:rect(0, 0, 180, 0)]) |
|||
s = $.sprite.make(m, [#loc:point(20, 280)], TRUE) |
|||
newGame(1) |
|||
-- show the application window |
|||
_movie.updateStage() |
|||
_movie.stage.visible = TRUE |
|||
end |
|||
---------------------------------------- |
|||
-- Starts a new game |
|||
---------------------------------------- |
|||
on newGame (whoStarts) |
|||
-- reset board |
|||
gBoard.copyPixels(gBoardTemplate, gBoardTemplate.rect, gBoardTemplate.rect) |
|||
-- clear feedback |
|||
member("feedback").text = "" |
|||
-- reset states |
|||
gM = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] |
|||
gStep = 0 |
|||
gGameOverFlag = FALSE |
|||
if whoStarts=-1 then computerMove() |
|||
end |
|||
---------------------------------------- |
|||
-- Handles a human move (mouse click) |
|||
---------------------------------------- |
|||
on humanMove () |
|||
if gGameOverFlag then return |
|||
-- find cell for mouse position |
|||
p = _mouse.clickLoc - sprite(1).loc |
|||
if p.locH mod 60<4 or p.locV mod 60<4 then return |
|||
p = p / 60 |
|||
x = p[1] + 1 |
|||
y = p[2] + 1 |
|||
if gM[x][y] then return -- ignore illegal moves |
|||
gM[x][y] = 1 |
|||
-- update cell image |
|||
p = p * 60 |
|||
gBoard.copyPixels(gHumanChip, gHumanChip.rect.offset(4+p[1], 4+p[2]), gHumanChip.rect) |
|||
-- proceed (unless game over) |
|||
gStep = gStep + 1 |
|||
if not checkHumanMove(x, y) then computerMove() |
|||
end |
|||
---------------------------------------- |
|||
-- Checks if human has won or game ended with draw |
|||
---------------------------------------- |
|||
on checkHumanMove (x, y) |
|||
if sum([gM[x][1], gM[x][2], gM[x][3]])=3 then return gameOver(1, [[x, 1], [x, 2], [x, 3]]) |
|||
if sum([gM[1][y], gM[2][y], gM[3][y]])=3 then return gameOver(1, [[1, y], [2, y], [3, y]]) |
|||
if x=y and sum([gM[1][1], gM[2][2], gM[3][3]])=3 then return gameOver(1, [[1, 1], [2, 2], [3, 3]]) |
|||
if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]])=3 then return gameOver(1, [[1, 3], [2, 2], [3, 1]]) |
|||
if gStep=9 then return gameOver(0) |
|||
return FALSE |
|||
end |
|||
---------------------------------------- |
|||
-- Checks if selecting specified empty cell makes computer or human win |
|||
---------------------------------------- |
|||
on checkCellWins (x, y, who) |
|||
wins = who*2 |
|||
if sum([gM[1][y], gM[2][y], gM[3][y]]) = wins then return [[1, y], [2, y], [3, y]] |
|||
if sum([gM[x][1], gM[x][2], gM[x][3]]) = wins then return [[x, 1], [x, 2], [x, 3]] |
|||
if x=y and sum([gM[1][1], gM[2][2], gM[3][3]]) = wins then return [[1, 1], [2, 2], [3, 3]] |
|||
if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]]) = wins then return [[1, 3], [2, 2], [3, 1]] |
|||
return FALSE |
|||
end |
|||
---------------------------------------- |
|||
-- Handles game over |
|||
---------------------------------------- |
|||
on gameOver (winner, cells) |
|||
gGameOverFlag = TRUE |
|||
if winner = 0 then |
|||
member("feedback").text = "It's a draw!" |
|||
else |
|||
-- hilite winning line with yellow |
|||
img = image(56, 56, 32) |
|||
img.fill(img.rect, rgb(255, 255, 0)) |
|||
repeat with c in cells |
|||
x = (c[1]-1)*60 + 4 |
|||
y = (c[2]-1)*60 + 4 |
|||
gBoard.copyPixels(img, img.rect.offset(x, y), img.rect, [#ink:#darkest]) |
|||
end repeat |
|||
member("feedback").text = ["Human", "Computer"][1+(winner=-1)] & " has won!" |
|||
end if |
|||
return TRUE |
|||
end |
|||
---------------------------------------- |
|||
-- Calculates next computer move |
|||
---------------------------------------- |
|||
on computerMove () |
|||
gStep = gStep + 1 |
|||
-- move 1: select center |
|||
if gStep=1 then return doComputerMove(2, 2) |
|||
-- move 2 (human started) |
|||
if gStep=2 then |
|||
if gM[2][2]=1 then |
|||
-- if center, select arbitrary corner |
|||
return doComputerMove(1, 1) |
|||
else |
|||
-- otherwise select center |
|||
return doComputerMove(2, 2) |
|||
end if |
|||
end if |
|||
-- move 3 (computer started) |
|||
if gStep=3 then |
|||
-- if corner, select diagonally opposite corner |
|||
if gM[1][1]=1 then return doComputerMove(3, 3) |
|||
if gM[3][3]=1 then return doComputerMove(1, 1) |
|||
if gM[1][3]=1 then return doComputerMove(3, 1) |
|||
return doComputerMove(1, 1) -- top left corner as default |
|||
end if |
|||
-- get free cells |
|||
free = [] |
|||
repeat with x = 1 to 3 |
|||
repeat with y = 1 to 3 |
|||
if gM[x][y]=0 then free.add([x, y]) |
|||
end repeat |
|||
end repeat |
|||
-- check if computer can win now |
|||
repeat with c in free |
|||
res = checkCellWins(c[1], c[2], -1) |
|||
if res<>FALSE then |
|||
doComputerMove(c[1], c[2]) |
|||
return gameOver(-1, res) |
|||
end if |
|||
end repeat |
|||
-- check if human could win with next move (if yes, prevent it) |
|||
repeat with c in free |
|||
res = checkCellWins(c[1], c[2], 1) |
|||
if res<>FALSE then return doComputerMove(c[1], c[2], TRUE) |
|||
end repeat |
|||
-- move 4 (human started): prevent "double mills" |
|||
if gStep=4 then |
|||
if gM[2][2]=1 and (gM[1][1]=1 or gM[3][3]=1) then return doComputerMove(3, 1) |
|||
if gM[2][2]=1 and (gM[1][3]=1 or gM[3][1]=1) then return doComputerMove(1, 1) |
|||
if gM[2][3]+gM[3][2]=2 then return doComputerMove(3, 3) |
|||
if gM[1][2]+gM[2][3]=2 then return doComputerMove(1, 3) |
|||
if gM[1][2]+gM[2][1]=2 then return doComputerMove(1, 1) |
|||
if gM[2][1]+gM[3][2]=2 then return doComputerMove(3, 1) |
|||
if (gM[1][3]+gM[3][1]=2) or (gM[1][1]+gM[3][3]=2) then return doComputerMove(2, 1) |
|||
end if |
|||
-- move 5 (computer started): try to create a "double mill" |
|||
if gStep=5 then |
|||
repeat with x = 1 to 3 |
|||
col = [gM[x][1], gM[x][2], gM[x][3]] |
|||
if not (sum(col)=-1 and max(col)=0) then next repeat |
|||
repeat with y = 1 to 3 |
|||
row = [gM[1][y], gM[2][y], gM[3][y]] |
|||
if not (sum(row)=-1 and max(row)=0 and gM[x][y]=0) then next repeat |
|||
return doComputerMove(x, y) |
|||
end repeat |
|||
end repeat |
|||
end if |
|||
-- otherwise use first free cell |
|||
c = free[1] |
|||
doComputerMove(c[1], c[2]) |
|||
end |
|||
---------------------------------------- |
|||
-- Updates state matrix and cell image |
|||
---------------------------------------- |
|||
on doComputerMove (x, y, checkDraw) |
|||
gM[x][y] = -1 |
|||
gBoard.copyPixels(gComputerChip, gComputerChip.rect.offset(4+(x-1)*60, 4+(y-1)*60), gComputerChip.rect) |
|||
if checkDraw and gStep=9 then gameOver(0) |
|||
end |
|||
---------------------------------------- |
|||
-- |
|||
---------------------------------------- |
|||
on sum (aLine) |
|||
return aLine[1]+aLine[2]+aLine[3] |
|||
end</syntaxhighlight> |
|||
=={{header|Lua}}== |
|||
Version for LuaJIT with or without ffi, negamax algorithm with alpha-beta pruning and caching of results. Human can't win. |
|||
<syntaxhighlight lang="lua">#!/usr/bin/env luajit |
|||
ffi=require"ffi" |
|||
local function printf(fmt,...) io.write(string.format(fmt, ...)) end |
|||
local board="123456789" -- board |
|||
local pval={1, -1} -- player 1=1 2=-1 for negamax |
|||
local pnum={} for k,v in ipairs(pval) do pnum[v]=k end |
|||
local symbol={'X','O'} -- default symbols X and O |
|||
local isymbol={} for k,v in pairs(symbol) do isymbol[v]=pval[k] end |
|||
math.randomseed(os.time()^5*os.clock()) -- time-seed the random gen |
|||
local random=math.random |
|||
-- usage of ffi variables give 20% speed |
|||
ffi.cdef[[ |
|||
typedef struct{ |
|||
char value; |
|||
char flag; |
|||
int depth; |
|||
}cData; |
|||
]] |
|||
-- draw the "board" in the way the numpad is organized |
|||
local function draw(board) |
|||
for i=7,1,-3 do |
|||
print(board:sub(i,i+2)) |
|||
end |
|||
end |
|||
-- pattern for win situations |
|||
local check={"(.)...%1...%1","..(.).%1.%1..", |
|||
"(.)%1%1......","...(.)%1%1...","......(.)%1%1", |
|||
"(.)..%1..%1..",".(.)..%1..%1.","..(.)..%1..%1", |
|||
} |
|||
-- calculate a win situation for which player or draw |
|||
local function win(b) |
|||
local sub |
|||
for i=1,#check do |
|||
sub=b:match(check[i]) |
|||
if sub then break end |
|||
end |
|||
sub=isymbol[sub] |
|||
return sub or 0 |
|||
end |
|||
-- input only validate moves of not yet filled positions |
|||
local function input(b,player) |
|||
char=symbol[pnum[player]] |
|||
local inp |
|||
repeat |
|||
printf("Player %d (\"%s\") move: ",pnum[player],char) |
|||
inp=tonumber(io.read()) or 0 |
|||
until inp>=1 and inp<=9 and b:find(inp) |
|||
b=b:gsub(inp,char) |
|||
return b,inp |
|||
end |
|||
-- ask how many human or AI players |
|||
local function playerselect() |
|||
local ai={} |
|||
local yn |
|||
for i=1,2 do |
|||
repeat |
|||
printf("Player %d human (Y/n)? ", i) yn=io.read():lower() |
|||
until yn:match("[yn]") or yn=='' |
|||
if yn=='n' then |
|||
ai[pval[i]]=true |
|||
printf("Player %d is AI\n", i) |
|||
else |
|||
printf("Player %d is human\n", i) |
|||
end |
|||
end |
|||
return ai |
|||
end |
|||
local function endgame() |
|||
repeat |
|||
printf("\nEnd game? (y/n)? ", i) yn=io.read():lower() |
|||
until yn:match("[yn]") |
|||
if yn=='n' then |
|||
return false |
|||
else |
|||
printf("\nGOOD BYE PROFESSOR FALKEN.\n\nA STRANGE GAME.\nTHE ONLY WINNING MOVE IS\nNOT TO PLAY.\n\nHOW ABOUT A NICE GAME OF CHESS?\n") |
|||
return true |
|||
end |
|||
end |
|||
-- AI Routine |
|||
local function shuffle(t) |
|||
for i=#t,1,-1 do |
|||
local rnd=random(i) |
|||
t[i], t[rnd] = t[rnd], t[i] |
|||
end |
|||
return t |
|||
end |
|||
-- move generator |
|||
local function genmove(node, color) |
|||
return coroutine.wrap(function() |
|||
local moves={} |
|||
for m in node:gmatch("%d") do |
|||
moves[#moves+1]=m |
|||
end |
|||
shuffle(moves) -- to make it more interesting |
|||
for _,m in ipairs(moves) do |
|||
local child=node:gsub(m,symbol[pnum[color]]) |
|||
coroutine.yield(child, m) |
|||
end |
|||
end) |
|||
end |
|||
--[[ |
|||
Negamax with alpha-beta pruning and table caching |
|||
]] |
|||
local cache={} |
|||
local best, aimove, tDepth |
|||
local LOWERB,EXACT,UPPERB=-1,0,1 -- has somebody an idea how to make them real constants? |
|||
local function negamax(node, depth, color, α, β) |
|||
color=color or 1 |
|||
α=α or -math.huge |
|||
β=β or math.huge |
|||
-- check for cached node |
|||
local αOrg=α |
|||
local cData=cache[node] |
|||
if cData and cData.depth>=depth and depth~=tDepth then |
|||
if cData.flag==EXACT then |
|||
return cData.value |
|||
elseif cData.flag==LOWERB then |
|||
α=math.max(α,cData.value) |
|||
elseif cData.flag==UPPERB then |
|||
β=math.min(β,cData.value) |
|||
end |
|||
if α>=β then |
|||
return cData.value |
|||
end |
|||
end |
|||
local winner=win(node) |
|||
if depth==0 or winner~=0 then |
|||
return winner*color |
|||
end |
|||
local value=-math.huge |
|||
for child,move in genmove(node, color) do |
|||
value=math.max(value, -negamax(child, depth-1, -color, -β, -α)) |
|||
if value>α then |
|||
α=value |
|||
if depth==tDepth then |
|||
best=child |
|||
aimove=move |
|||
end |
|||
end |
|||
if α>=β then break end |
|||
end |
|||
-- cache known data |
|||
--cData={} -- if you want Lua tables instead of ffi you can switch the two lines here, costs 20% speed |
|||
cData=ffi.new("cData") |
|||
cData.value=value |
|||
if value<=αOrg then |
|||
cData.flag=UPPERB |
|||
elseif value>=β then |
|||
cData.flag=LOWERB |
|||
else |
|||
cData.flag=EXACT |
|||
end |
|||
cData.depth=depth |
|||
cache[node]=cData |
|||
return α |
|||
end |
|||
-- MAIN |
|||
do |
|||
local winner,value |
|||
local score={[-1]=0, [0]=0, [1]=0} |
|||
repeat |
|||
print("\n TIC-TAC-TOE\n") |
|||
local aiplayer=playerselect() |
|||
local player=1 |
|||
board="123456789" |
|||
for i=1,#board do |
|||
draw(board) |
|||
tDepth=10-i |
|||
if aiplayer[player] then |
|||
negamax(board, tDepth, player, -math.huge, math.huge) |
|||
board=best |
|||
printf("AI %d moves %s\n", pnum[player], aimove) |
|||
else |
|||
board=input(board,player) |
|||
end |
|||
winner=win(board) |
|||
if winner~=0 then break end |
|||
player=-player |
|||
end |
|||
score[winner]=score[winner]+1 |
|||
if winner and winner~=0 then |
|||
printf("*** Player %d (%s) has won\n", pnum[winner], symbol[pnum[winner]]) |
|||
else |
|||
printf("*** No winner\n") |
|||
end |
|||
printf("Score Player 1: %d Player 2: %d Draw: %d\n",score[1],score[-1],score[0]) |
|||
draw(board) |
|||
until endgame() |
|||
end</syntaxhighlight> |
|||
{{out}} |
|||
<pre>> time echo "n\nn\ny\n"| ./tictactoe.lua ⏎ |
|||
TIC-TAC-TOE |
|||
Player 1 human (Y/n)? Player 1 is AI |
|||
Player 2 human (Y/n)? Player 2 is AI |
|||
789 |
|||
456 |
|||
123 |
|||
AI 1 moves 1 |
|||
789 |
|||
456 |
|||
X23 |
|||
AI 2 moves 5 |
|||
789 |
|||
4O6 |
|||
X23 |
|||
AI 1 moves 9 |
|||
78X |
|||
4O6 |
|||
X23 |
|||
AI 2 moves 2 |
|||
78X |
|||
4O6 |
|||
XO3 |
|||
AI 1 moves 8 |
|||
7XX |
|||
4O6 |
|||
XO3 |
|||
AI 2 moves 7 |
|||
OXX |
|||
4O6 |
|||
XO3 |
|||
AI 1 moves 3 |
|||
OXX |
|||
4O6 |
|||
XOX |
|||
AI 2 moves 6 |
|||
OXX |
|||
4OO |
|||
XOX |
|||
AI 1 moves 4 |
|||
*** No winner |
|||
Score Player 1: 0 Player 2: 0 Draw: 1 |
|||
OXX |
|||
XOO |
|||
XOX |
|||
End game? (y/n)? |
|||
GOOD BYE PROFESSOR FALKEN. |
|||
A STRANGE GAME. |
|||
THE ONLY WINNING MOVE IS |
|||
NOT TO PLAY. |
|||
HOW ABOUT A NICE GAME OF CHESS? |
|||
echo "n\nn\ny\n" 0,00s user 0,00s system 69% cpu 0,002 total |
|||
./tictactoe.lua 0,03s user 0,00s system 98% cpu 0,035 total</pre> |
|||
=={{header|M2000 Interpreter}}== |
|||
Computer May loose; |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module Tic.Tac.Toe { |
|||
Dim Board$(1 to 3, 1 to 3)=" " |
|||
WinGame=False |
|||
p=Board$() |
|||
RandomPosition=lambda -> { |
|||
=(random(1,3), random(1,3)) |
|||
} |
|||
BoardItemEmpty=Lambda p (x, y) -> { |
|||
=Array$(p, x, y)=" " |
|||
} |
|||
BoardSetItem=Lambda p (x, y, w$) -> { |
|||
link p to a$() |
|||
a$(x, y)=w$ |
|||
} |
|||
T=9 |
|||
R=0 |
|||
C=0 |
|||
Repeat { |
|||
Print "Computer Move:" |
|||
CompMove() |
|||
T-- |
|||
DrawBoard() |
|||
CheckWin() |
|||
if WinGame Then Print "Computer Win": Exit |
|||
if T=0 then exit |
|||
Repeat { |
|||
GetRowCol("Input Row", &R) |
|||
GetRowCol("Input Column", &C) |
|||
If BoardItemEmpty(R,C) then call boardsetitem(R,C,"O") : exit |
|||
} Always |
|||
T-- |
|||
DrawBoard() |
|||
CheckWin() |
|||
if WinGame Then Print "You Win": Exit |
|||
} until T=0 or WinGame |
|||
Sub DrawBoard() |
|||
Print "R/C 1 2 3" |
|||
Print " 1) "; Board$(1,1);"|";Board$(1,2);"|";Board$(1,3) |
|||
Print " -+-+-" |
|||
Print " 2) "; Board$(2,1);"|";Board$(2,2);"|";Board$(2,3) |
|||
Print " -+-+-" |
|||
Print " 3) "; Board$(3,1);"|";Board$(3,2);"|";Board$(3,3) |
|||
End Sub |
|||
Sub CheckWin() |
|||
WinGame=false |
|||
local i,j,three$ |
|||
For i=1 to 3 |
|||
three$="" |
|||
For j=1 to 3 : three$+=Board$(i,j) : Next j |
|||
CheckThree() |
|||
three$="" |
|||
For j=1 to 3 : three$+=Board$(j,i) :Next j |
|||
CheckThree() |
|||
Next i |
|||
three$="" |
|||
For i=1 to 3 : three$+=Board$(i,i): Next i |
|||
CheckThree() |
|||
three$="" |
|||
For i=1 to 3:three$+=Board$(i,4-i): Next i |
|||
CheckThree() |
|||
End Sub |
|||
Sub CheckThree() |
|||
if instr(three$," ")=0 then WinGame=WinGame or Filter$(three$, left$(three$,1))="" |
|||
End Sub |
|||
Sub CompMove() |
|||
if T<9 and Board$(2,2)=" " then { |
|||
call boardsetitem(2,2,"X") |
|||
} Else { |
|||
local i=3, j=3, found=false |
|||
if T<=6 then { |
|||
CompThink("X","X") |
|||
} |
|||
let i=3, j=3 |
|||
If Not found And T<6 then { |
|||
CompThink("O","X") |
|||
} |
|||
If not found then { |
|||
Repeat { |
|||
comp=RandomPosition() |
|||
If BoardItemEmpty(!comp) then call boardsetitem(!comp, "X") : exit |
|||
} Always |
|||
} |
|||
} |
|||
End Sub |
|||
Sub CompThink(Bad$, Good$) |
|||
While i>0 { |
|||
j=3 |
|||
While j>0 { |
|||
if Board$(i,j)=" " then { |
|||
Board$(i,j)=Bad$ |
|||
CheckWin() |
|||
if WinGame then { |
|||
Board$(i,j)=Good$:i=0:j=0: found=true |
|||
} Else Board$(i,j)=" " |
|||
} |
|||
j-- |
|||
} |
|||
i-- |
|||
} |
|||
End Sub |
|||
Sub GetRowCol(What$, &W) |
|||
Print What$;":"; |
|||
Repeat { |
|||
W=Val("0"+Key$) |
|||
} until W>=1 and W<=3 |
|||
Print Str$(W,"") |
|||
End Sub |
|||
} |
|||
Tic.Tac.Toe |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
Computer Move: |
|||
R/C 1 2 3 |
|||
1) | | |
|||
-+-+- |
|||
2) | | |
|||
-+-+- |
|||
3) | |X |
|||
Input Row:2 |
|||
Input Column:2 |
|||
R/C 1 2 3 |
|||
1) | | |
|||
-+-+- |
|||
2) |O| |
|||
-+-+- |
|||
3) | |X |
|||
Computer Move: |
|||
R/C 1 2 3 |
|||
1) | | |
|||
-+-+- |
|||
2) |O| |
|||
-+-+- |
|||
3) X| |X |
|||
Input Row:3 |
|||
Input Column:2 |
|||
R/C 1 2 3 |
|||
1) | | |
|||
-+-+- |
|||
2) |O| |
|||
-+-+- |
|||
3) X|O|X |
|||
Computer Move: |
|||
R/C 1 2 3 |
|||
1) |X| |
|||
-+-+- |
|||
2) |O| |
|||
-+-+- |
|||
3) X|O|X |
|||
Input Row:2 |
|||
Input Column:1 |
|||
R/C 1 2 3 |
|||
1) |X| |
|||
-+-+- |
|||
2) O|O| |
|||
-+-+- |
|||
3) X|O|X |
|||
Computer Move: |
|||
R/C 1 2 3 |
|||
1) |X| |
|||
-+-+- |
|||
2) O|O|X |
|||
-+-+- |
|||
3) X|O|X |
|||
Input Row:1 |
|||
Input Column:3 |
|||
R/C 1 2 3 |
|||
1) |X|O |
|||
-+-+- |
|||
2) O|O|X |
|||
-+-+- |
|||
3) X|O|X |
|||
Computer Move: |
|||
R/C 1 2 3 |
|||
1) X|X|O |
|||
-+-+- |
|||
2) O|O|X |
|||
-+-+- |
|||
3) X|O|X |
|||
</pre > |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="text">DynamicModule[{board = ConstantArray[0, {3, 3}], text = "Playing...", |
|||
first, rows = |
|||
Join[#, Transpose@#, {Diagonal@#, Diagonal@Reverse@#}] &}, |
|||
Column@{Graphics[{Thickness[.02], |
|||
Table[With[{i = i, j = j}, |
|||
Button[{White, Rectangle[{i, j} - 1, {i, j}], Black, |
|||
Dynamic[Switch[board[[i, j]], 0, Black, 1, |
|||
Circle[{i, j} - .5, .3], -1, |
|||
Line[{{{i, j} - .2, {i, j} - .8}, {{i - .2, |
|||
j - .8}, {i - .8, j - .2}}}]]]}, |
|||
Which[text != "Playing...", board = ConstantArray[0, {3, 3}]; |
|||
text = "Playing...", board[[i, j]] == 0, |
|||
If[board == ConstantArray[0, {3, 3}], |
|||
first = {i, |
|||
j} /. {{2, 2} -> 1, {1 | 3, 1 | 3} -> 2, _ -> 3}]; |
|||
board[[i, j]] = 1; |
|||
FinishDynamic[]; |
|||
Which[MemberQ[rows[board], {1, 1, 1}], text = "You win.", |
|||
FreeQ[board, 0], text = "Draw.", True, |
|||
board[[Sequence @@ |
|||
SortBy[Select[Tuples[{Range@3, Range@3}], |
|||
board[[Sequence @@ #]] == |
|||
0 &], -Total[ |
|||
Sort /@ |
|||
rows[ReplacePart[ |
|||
board, # -> -1]] /. {{-1, -1, -1} -> |
|||
512, {-1, 1, 1} -> 64, {-1, -1, 0} -> |
|||
8, {0, 1, 1} -> -1, {_, _, _} -> 0}] - |
|||
Switch[#, {2, 2}, 1, {1 | 3, 1 | 3}, |
|||
If[first == 2, -1, 0], _, |
|||
If[first == 2, 0, -1]] &][[1]]]] = -1; |
|||
Which[MemberQ[rows[board], {-1, -1, -1}], |
|||
text = "You lost.", FreeQ[board, 0], |
|||
text = "Draw."]]]]], {i, 1, 3}, {j, 1, 3}], Thickness[.01], |
|||
Line[{{{1, 0}, {1, 3}}, {{2, 0}, {2, 3}}, {{0, 1}, {3, 1}}, {{0, |
|||
2}, {3, 2}}}]}], Dynamic@text}]</syntaxhighlight> |
|||
=={{header|MATLAB}}== |
|||
Allows for choice between any combination of human or computer players. Computer players are intelligent, but not perfect. It implements the "rules" used by the Newell and Simon's 1972 tic-tac-toe program (as explained by Wikipedia), but this implementation does not factor in the move before the move causing the fork (either for creation or prevention). |
|||
<syntaxhighlight lang="matlab">function TicTacToe |
|||
% Set up the board (one for each player) |
|||
boards = false(3, 3, 2); % Players' pieces |
|||
rep = [' 1 | 4 | 7' ; ' 2 | 5 | 8' ; ' 3 | 6 | 9']; |
|||
% Prompt user with options |
|||
fprintf('Welcome to Tic-Tac-Toe!\n') |
|||
nHumans = str2double(input('Enter the number of human players: ', 's')); |
|||
if isnan(nHumans) || ceil(nHumans) ~= nHumans || nHumans < 1 || nHumans > 2 |
|||
nHumans = 0; |
|||
pHuman = false(2, 1); |
|||
elseif nHumans == 1 |
|||
humanFirst = input('Would the human like to go first (Y/N)? ', 's'); |
|||
if length(humanFirst) == 1 && lower(humanFirst) == 'n' |
|||
pHuman = [false ; true]; |
|||
else |
|||
pHuman = [true ; false]; |
|||
end |
|||
else |
|||
pHuman = true(2, 1); |
|||
end |
|||
if any('o' == input('Should Player 1 use X or O? ', 's')) |
|||
marks = 'OX'; |
|||
else |
|||
marks = 'XO'; |
|||
end |
|||
fprintf('So Player 1 is %shuman and %cs and Player 2 is %shuman and %cs.\n', ... |
|||
char('not '.*~pHuman(1)), marks(1), char('not '.*~pHuman(2)), marks(2)) |
|||
if nHumans > 0 |
|||
fprintf('Select the space to mark by entering the space number.\n') |
|||
fprintf('No entry will quit the game.\n') |
|||
end |
|||
% Play game |
|||
gameOver = false; |
|||
turn = 1; |
|||
while ~gameOver |
|||
fprintf('\n') |
|||
disp(rep) |
|||
fprintf('\n') |
|||
if pHuman(turn) |
|||
[move, isValid, isQuit] = GetMoveFromPlayer(turn, boards); |
|||
gameOver = isQuit; |
|||
else |
|||
move = GetMoveFromComputer(turn, boards); |
|||
fprintf('Player %d chooses %d\n', turn, move) |
|||
isValid = true; |
|||
isQuit = false; |
|||
end |
|||
if isValid && ~isQuit |
|||
[r, c] = ind2sub([3 3], move); |
|||
boards(r, c, turn) = true; |
|||
rep(r, 4*c) = marks(turn); |
|||
if CheckWin(boards(:, :, turn)) |
|||
gameOver = true; |
|||
fprintf('\n') |
|||
disp(rep) |
|||
fprintf('\nPlayer %d wins!\n', turn) |
|||
elseif CheckDraw(boards) |
|||
gameOver = true; |
|||
fprintf('\n') |
|||
disp(rep) |
|||
fprintf('\nCat''s game!\n') |
|||
end |
|||
turn = ~(turn-1)+1; |
|||
end |
|||
end |
|||
end |
|||
function [move, isValid, isQuit] = GetMoveFromPlayer(pNum, boards) |
|||
% move - 1-9 indicating move position, 0 if invalid move |
|||
% isValid - logical indicating if move was valid, true if quitting |
|||
% isQuit - logical indicating if player wishes to quit game |
|||
p1 = boards(:, :, 1); |
|||
p2 = boards(:, :, 2); |
|||
moveStr = input(sprintf('Player %d: ', pNum), 's'); |
|||
if isempty(moveStr) |
|||
fprintf('Play again soon!\n') |
|||
move = 0; |
|||
isValid = true; |
|||
isQuit = true; |
|||
else |
|||
move = str2double(moveStr); |
|||
isQuit = false; |
|||
if isnan(move) || move < 1 || move > 9 || p1(move) || p2(move) |
|||
fprintf('%s is an invalid move.\n', moveStr) |
|||
isQuit = 0; |
|||
isValid = false; |
|||
else |
|||
isValid = true; |
|||
end |
|||
end |
|||
end |
|||
function move = GetMoveFromComputer(pNum, boards) |
|||
% pNum - 1-2 player number |
|||
% boards - 3x3x2 logical array where pBoards(:,:,1) is player 1's marks |
|||
% Assumes that it is possible to make a move |
|||
if ~any(boards(:)) % Play in the corner for first move |
|||
move = 1; |
|||
else % Use Newell and Simon's "rules to win" |
|||
pMe = boards(:, :, pNum); |
|||
pThem = boards(:, :, ~(pNum-1)+1); |
|||
possMoves = find(~(pMe | pThem)).'; |
|||
% Look for a winning move |
|||
move = FindWin(pMe, possMoves); |
|||
if move |
|||
return |
|||
end |
|||
% Look to block opponent from winning |
|||
move = FindWin(pThem, possMoves); |
|||
if move |
|||
return |
|||
end |
|||
% Look to create a fork (two non-blocked lines of two) |
|||
for m = possMoves |
|||
newPMe = pMe; |
|||
newPMe(m) = true; |
|||
if CheckFork(newPMe, pThem) |
|||
move = m; |
|||
return |
|||
end |
|||
end |
|||
% Look to make two in a row so long as it doesn't force opponent to fork |
|||
notGoodMoves = false(size(possMoves)); |
|||
for m = possMoves |
|||
newPMe = pMe; |
|||
newPMe(m) = true; |
|||
if CheckPair(newPMe, pThem) |
|||
nextPossMoves = possMoves; |
|||
nextPossMoves(nextPossMoves == m) = []; |
|||
theirMove = FindWin(newPMe, nextPossMoves); |
|||
newPThem = pThem; |
|||
newPThem(theirMove) = true; |
|||
if ~CheckFork(newPThem, newPMe) |
|||
move = m; |
|||
return |
|||
else |
|||
notGoodMoves(possMoves == m) = true; |
|||
end |
|||
end |
|||
end |
|||
possMoves(notGoodMoves) = []; |
|||
% Play the center if available |
|||
if any(possMoves == 5) |
|||
move = 5; |
|||
return |
|||
end |
|||
% Play the opposite corner of the opponent's piece if available |
|||
corners = [1 3 7 9]; |
|||
move = intersect(possMoves, ... |
|||
corners(~(pMe(corners) | pThem(corners)) & pThem(fliplr(corners)))); |
|||
if ~isempty(move) |
|||
move = move(1); |
|||
return |
|||
end |
|||
% Play an empty corner if available |
|||
move = intersect(possMoves, corners); |
|||
if move |
|||
move = move(1); |
|||
return |
|||
end |
|||
% Play an empty side if available |
|||
sides = [2 4 6 8]; |
|||
move = intersect(possMoves, sides); |
|||
if move |
|||
move = move(1); |
|||
return |
|||
end |
|||
% No good moves, so move randomly |
|||
possMoves = find(~(pMe | pThem)); |
|||
move = possMoves(randi(length(possMoves))); |
|||
end |
|||
end |
|||
function move = FindWin(board, possMoves) |
|||
% board - 3x3 logical representing one player's pieces |
|||
% move - integer indicating position to move to win, or 0 if no winning move |
|||
for m = possMoves |
|||
newPMe = board; |
|||
newPMe(m) = true; |
|||
if CheckWin(newPMe) |
|||
move = m; |
|||
return |
|||
end |
|||
end |
|||
move = 0; |
|||
end |
|||
function win = CheckWin(board) |
|||
% board - 3x3 logical representing one player's pieces |
|||
% win - logical indicating if that player has a winning board |
|||
win = any(all(board)) || any(all(board, 2)) || ... |
|||
all(diag(board)) || all(diag(fliplr(board))); |
|||
end |
|||
function fork = CheckFork(p1, p2) |
|||
% fork - logical indicating if player 1 has created a fork unblocked by player 2 |
|||
fork = sum([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ... |
|||
sum(diag(p1))-sum(diag(p2)) ... |
|||
sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2) > 1; |
|||
end |
|||
function pair = CheckPair(p1, p2) |
|||
% pair - logical indicating if player 1 has two in a line unblocked by player 2 |
|||
pair = any([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ... |
|||
sum(diag(p1))-sum(diag(p2)) ... |
|||
sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2); |
|||
end |
|||
function draw = CheckDraw(boards) |
|||
% boards - 3x3x2 logical representation of all players' pieces |
|||
draw = all(all(boards(:, :, 1) | boards(:, :, 2))); |
|||
end</syntaxhighlight> |
|||
{{out}} |
|||
Computer goes first and plays perfectly: |
|||
<pre>Welcome to Tic-Tac-Toe! |
|||
Enter the number of human players: 1 |
|||
Would the human like to go first (Y/N)? n |
|||
Should Player 1 use X or O? x |
|||
So Player 1 is not human and Xs and Player 2 is human and Os. |
|||
Select the space to mark by entering the space number. |
|||
No entry will quit the game. |
|||
1 | 4 | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 1 chooses 1 |
|||
X | 4 | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 2: 4 |
|||
X | O | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 1 chooses 2 |
|||
X | O | 7 |
|||
X | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 2: 3 |
|||
X | O | 7 |
|||
X | 5 | 8 |
|||
O | 6 | 9 |
|||
Player 1 chooses 5 |
|||
X | O | 7 |
|||
X | X | 8 |
|||
O | 6 | 9 |
|||
Player 2: 8 |
|||
X | O | 7 |
|||
X | X | O |
|||
O | 6 | 9 |
|||
Player 1 chooses 9 |
|||
X | O | 7 |
|||
X | X | O |
|||
O | 6 | X |
|||
Player 1 wins!</pre> |
|||
Computer goes first, but misses opportunity to set up for a fork, setting up human player instead: |
|||
<pre>Welcome to Tic-Tac-Toe! |
|||
Enter the number of human players: 1 |
|||
Would the human like to go first (Y/N)? n |
|||
Should Player 1 use X or O? x |
|||
So Player 1 is not human and Xs and Player 2 is human and Os. |
|||
Select the space to mark by entering the space number. |
|||
No entry will quit the game. |
|||
1 | 4 | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 1 chooses 1 |
|||
X | 4 | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | 9 |
|||
Player 2: 9 |
|||
X | 4 | 7 |
|||
2 | 5 | 8 |
|||
3 | 6 | O |
|||
Player 1 chooses 2 |
|||
X | 4 | 7 |
|||
X | 5 | 8 |
|||
3 | 6 | O |
|||
Player 2: 3 |
|||
X | 4 | 7 |
|||
X | 5 | 8 |
|||
O | 6 | O |
|||
Player 1 chooses 6 |
|||
X | 4 | 7 |
|||
X | 5 | 8 |
|||
O | X | O |
|||
Player 2: 7 |
|||
X | 4 | O |
|||
X | 5 | 8 |
|||
O | X | O |
|||
Player 1 chooses 5 |
|||
X | 4 | O |
|||
X | X | 8 |
|||
O | X | O |
|||
Player 2: 8 |
|||
X | 4 | O |
|||
X | X | O |
|||
O | X | O |
|||
Player 2 wins!</pre> |
|||
=={{header|mIRC Scripting Language}}== |
|||
<syntaxhighlight lang="mirc">alias ttt { |
|||
if ($2 isin %ttt) || (!%ttt) { |
|||
var %ttt~ = $remove($iif(%ttt,%ttt,1 2 3 4 5 6 7 8 9),$2,X,O) |
|||
var %ttt~~ = $replace($iif(%ttt,%ttt,1 2 3 4 5 6 7 8 9),$2,X) |
|||
set %ttt $replace(%ttt~~,$iif(($regex(%ttt~~,/(?:O . . (?:(?:. O .|O) . . (\d)|(?:. (\d) .|(\d)) . . O)|(\d) . . (?:. O .|O) . . O|. . (?:O . (?:O . (\d)|(\d) . O)|(\d) . O . O) . .)/)) || ($regex(%ttt~~,/^(?:. . . )*(?:O (?:O (\d)|(\d) O)|(\d) O O)(?: . . .)*$/)),$regml(1),$iif(($regex(%ttt~~,/(?:X . . (?:(?:. X .|X) . . (\d)|(?:. (\d) .|(\d)) . . X)|(\d) . . (?:. X .|X) . . X|. . (?:X . (?:X . (\d)|(\d) . X)|(\d) . X . X) . .)/)) || ($regex(%ttt~~,/^(?:. . . )*(?:X (?:X (\d)|(\d) X)|(\d) X X)(?: . . .)*$/)),$regml(1),$iif($remove(%ttt~,2,4,6,8,$chr(32)),$iif((5 isin $remove(%ttt~,2,4,6,8)) && ($rand(0,$numtok($v2,32)) == 0),5,$gettok($remove(%ttt~,2,4,6,8),$rand(1,$numtok($remove(%ttt~,2,4,6,8),32)),32)),$gettok(%ttt~,$rand(1,$numtok(%ttt~,32)),32)))),O) |
|||
tokenize 32 %ttt |
|||
if ($regex(%ttt,/(?:X . . (?:X|. X .) . . X|. . X . X . X . .)/)) || ($regex(%ttt,/^(?:. . . )*X X X(?: . . .)*$/)) { |
|||
echo -ag $me Wins |
|||
tokenize 32 %ttt~~ |
|||
unset %ttt |
|||
} |
|||
elseif ($regex(%ttt,/(?:O . . (?:O|. O .) . . O|. . O . O . O . .)/)) || ($regex(%ttt,/^(?:. . . )*O O O(?: . . .)*$/)) { |
|||
echo -ag $me Loses |
|||
unset %ttt |
|||
} |
|||
elseif (!$regex(%ttt,/\d/)) { |
|||
echo -ag Draw |
|||
unset %ttt |
|||
} |
|||
echo -ag � $+ $iif($1 isnum,$chr(32),$1) $+ $chr(124) $+ $iif($2 isnum,$chr(32),$2) $+ $chr(124) $+ $iif($3 isnum, ,$3) |
|||
echo -ag � $+ $iif($4 isnum,$chr(32),$4) $+ $chr(124) $+ $iif($5 isnum,$chr(32),$5) $+ $chr(124) $+ $iif($6 isnum, ,$6) |
|||
echo -ag � $+ $iif($7 isnum,$chr(32),$7) $+ $chr(124) $+ $iif($8 isnum,$chr(32),$8) $+ $chr(124) $+ $iif($9 isnum, ,$9) |
|||
} |
|||
else { |
|||
echo -ag Place Taken |
|||
tokenize 32 %ttt |
|||
echo -ag � $+ $iif($1 isnum,$chr(32),$1) $+ $chr(124) $+ $iif($2 isnum,$chr(32),$2) $+ $chr(124) $+ $iif($3 isnum, ,$3) |
|||
echo -ag � $+ $iif($4 isnum,$chr(32),$4) $+ $chr(124) $+ $iif($5 isnum,$chr(32),$5) $+ $chr(124) $+ $iif($6 isnum, ,$6) |
|||
echo -ag � $+ $iif($7 isnum,$chr(32),$7) $+ $chr(124) $+ $iif($8 isnum,$chr(32),$8) $+ $chr(124) $+ $iif($9 isnum, ,$9) |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|МК-61/52}}== |
|||
<syntaxhighlight lang="mk-61">9 С/П ПП 28 пи * cos x<0 16 ИП2 |
|||
ПП 28 1 - БП 51 ИП7 ПП 28 ИП7 |
|||
ПП 28 КИП2 ИП2 ВП 4 4 С/П 1 - |
|||
x=0 33 8 П2 С/П П7 ИП2 4 - x#0 |
|||
43 x<0 45 8 + П8 ИП7 - x#0 55 |
|||
ИП8 ВП 6 6 С/П ИП2 В/О</syntaxhighlight> |
|||
Cell numbering 1 to 9; starts from the upper left cell, then clockwise in a spiral. The first move is a calculator. Result: 44 - draw, 66 - victory of the calculator. |
|||
=={{header|Nim}}== |
|||
{{trans|Python}} |
|||
This is a translation of the second version with the better AI, but with some differences. For instance, we have chosen to display the board in the same way as the first version. All procedures have a parameter "board" rather accessing a global variable. We use also base 1-indexing for the board. Etc. |
|||
<syntaxhighlight lang="nim">import options, random, sequtils, strutils |
|||
type |
|||
Board = array[1..9, char] |
|||
Score = (char, array[3, int]) |
|||
const NoChoice = 0 |
|||
var board: Board = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] |
|||
const Wins = [[1, 2, 3], [4, 5, 6], [7, 8, 9], |
|||
[1, 4, 7], [2, 5, 8], [3, 6, 9], |
|||
[1, 5, 9], [3, 5, 7]] |
|||
template toIndex(ch: char): int = |
|||
## Convert a character to an index in board. |
|||
ord(ch) - ord('0') |
|||
proc print(board: Board) = |
|||
for i in [1, 4, 7]: |
|||
echo board[i..(i + 2)].join(" ") |
|||
proc score(board: Board): Option[Score] = |
|||
for w in Wins: |
|||
let b = board[w[0]] |
|||
if b in "XO" and w.allIt(board[it] == b): |
|||
return some (b, w) |
|||
result = none(Score) |
|||
proc finished(board: Board): bool = |
|||
board.allIt(it in "XO") |
|||
proc space(board: Board): seq[char] = |
|||
for b in board: |
|||
if b notin "XO": |
|||
result.add b |
|||
proc myTurn(board: var Board; xo: char): char = |
|||
let options = board.space() |
|||
result = options.sample() |
|||
board[result.toIndex] = xo |
|||
proc myBetterTurn(board: var Board; xo: char): int = |
|||
let ox = if xo == 'X': 'O' else: 'X' |
|||
var oneBlock = NoChoice |
|||
let options = board.space.mapIt(it.toIndex) |
|||
block search: |
|||
for choice in options: |
|||
var brd = board |
|||
brd[choice] = xo |
|||
if brd.score.isSome: |
|||
result = choice |
|||
break search |
|||
if oneBlock == NoChoice: |
|||
brd[choice] = ox |
|||
if brd.score.isSome: |
|||
oneBlock = choice |
|||
result = if oneBlock != NoChoice: oneBlock else: options.sample() |
|||
board[result] = xo |
|||
proc yourTurn(board: var Board; xo: char): int = |
|||
let options = board.space() |
|||
var choice: char |
|||
while true: |
|||
stdout.write "\nPut your $# in any of these positions: $# ".format(xo, options.join()) |
|||
let input = stdin.readLine().strip() |
|||
if input.len == 1 and input[0] in options: |
|||
choice = input[0] |
|||
break |
|||
echo "Whoops I don't understand the input" |
|||
result = choice.toIndex |
|||
board[result] = xo |
|||
proc me(board: var Board; xo: char): Option[Score] = |
|||
board.print() |
|||
echo "\nI go at ", board.myBetterTurn(xo) |
|||
result = board.score() |
|||
proc you(board: var Board; xo: char): Option[Score] = |
|||
board.print() |
|||
echo "\nYou went at ", board.yourTurn(xo) |
|||
result = board.score() |
|||
proc play() = |
|||
while not board.finished(): |
|||
let score = board.me('X') |
|||
if score.isSome: |
|||
board.print() |
|||
let (winner, line) = score.get() |
|||
echo "\n$# wins along ($#).".format(winner, line.join(", ")) |
|||
return |
|||
if not board.finished(): |
|||
let score = board.you('O') |
|||
if score.isSome: |
|||
board.print() |
|||
let (winner, line) = score.get() |
|||
echo "\n$# wins along ($#).".format(winner, line.join(", ")) |
|||
return |
|||
echo "\nA draw." |
|||
echo "Tic-tac-toe game player." |
|||
echo "Input the index of where you wish to place your mark at your turn." |
|||
randomize() |
|||
play()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Tic-tac-toe game player. |
|||
Input the index of where you wish to place your mark at your turn. |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
I go at 9 |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 X |
|||
Put your O in any of these positions: 12345678 7 |
|||
You went at 7 |
|||
1 2 3 |
|||
4 5 6 |
|||
O 8 X |
|||
I go at 5 |
|||
1 2 3 |
|||
4 X 6 |
|||
O 8 X |
|||
Put your O in any of these positions: 123468 1 |
|||
You went at 1 |
|||
O 2 3 |
|||
4 X 6 |
|||
O 8 X |
|||
I go at 4 |
|||
O 2 3 |
|||
X X 6 |
|||
O 8 X |
|||
Put your O in any of these positions: 2368 6 |
|||
You went at 6 |
|||
O 2 3 |
|||
X X O |
|||
O 8 X |
|||
I go at 3 |
|||
O 2 X |
|||
X X O |
|||
O 8 X |
|||
Put your O in any of these positions: 28 8 |
|||
You went at 8 |
|||
O 2 X |
|||
X X O |
|||
O O X |
|||
I go at 2 |
|||
A draw.</pre> |
|||
=={{header|Objeck}}== |
|||
Tic-tac-toe game using Minimax algorithm. |
|||
<syntaxhighlight lang="objeck">class TicTacToe { |
|||
@board : Char[,]; |
|||
@cpu_opening : Bool; |
|||
enum Status { |
|||
INVALID_MOVE, |
|||
PLAYING, |
|||
QUIT, |
|||
TIE, |
|||
CPU_WIN, |
|||
PLAYER_WIN |
|||
} |
|||
consts Weights { |
|||
MIN := -1000, |
|||
MAX := 1000 |
|||
} |
|||
function : Main(args : String[]) ~ Nil { |
|||
cpu_score := 0; |
|||
player_score := 0; |
|||
for(i :=0; i < 5; i += 1;) { |
|||
game := TicTacToe->New(); |
|||
result := game->Play(); |
|||
if(result = Status->PLAYER_WIN) { |
|||
player_score += 1; |
|||
"\n=> Player Wins!"->PrintLine(); |
|||
} |
|||
else if(result = Status->CPU_WIN) { |
|||
cpu_score += 1; |
|||
"\n=> CPU Wins!"->PrintLine(); |
|||
} |
|||
else if(result = Status->TIE) { |
|||
"\n=> Tie."->PrintLine(); |
|||
} |
|||
else { |
|||
break; |
|||
}; |
|||
}; |
|||
"\nHuman={$player_score}, CPU={$cpu_score}"->PrintLine(); |
|||
} |
|||
New() { |
|||
@board := Char->New[3, 3]; |
|||
for(index := 0; index < 9; index += 1;) { |
|||
j := index / 3; |
|||
i := index % 3; |
|||
@board[i, j] := '1' + index; |
|||
}; |
|||
@cpu_opening := true; |
|||
} |
|||
method : Play() ~ Status { |
|||
players_turn := Int->Random(1) = 1 ? true : false; |
|||
if(players_turn) { |
|||
@cpu_opening := false; |
|||
"\n*** NEW (Player) ***\n"->PrintLine(); |
|||
Draw(); |
|||
} |
|||
else { |
|||
"\n*** NEW (CPU) ***\n"->PrintLine(); |
|||
}; |
|||
playing := true; |
|||
do { |
|||
status : Status; |
|||
if(players_turn) { |
|||
status := PlayerMove(); |
|||
players_turn := false; |
|||
} |
|||
else { |
|||
status := CpuMove(); |
|||
players_turn := true; |
|||
}; |
|||
if(players_turn) { |
|||
Draw(); |
|||
}; |
|||
select(status) { |
|||
label Status->INVALID_MOVE: { |
|||
"\n=> Invalid Move"->PrintLine(); |
|||
} |
|||
label Status->PLAYER_WIN: { |
|||
return Status->PLAYER_WIN; |
|||
} |
|||
label Status->CPU_WIN: { |
|||
return Status->CPU_WIN; |
|||
} |
|||
label Status->TIE: { |
|||
return Status->TIE; |
|||
} |
|||
label Status->QUIT: { |
|||
playing := false; |
|||
} |
|||
}; |
|||
} |
|||
while(playing); |
|||
return Status->QUIT; |
|||
} |
|||
method : PlayerMove() ~ Status { |
|||
move := System.IO.Console->ReadString(); |
|||
if(move->Size() = 0) { |
|||
return Status->INVALID_MOVE; |
|||
}; |
|||
option := move->Get(0); |
|||
if(option = 'q') { |
|||
return Status->QUIT; |
|||
}; |
|||
if(LegalMove(option, 'X')) { |
|||
if(IsWinner(@board, 'X')) { |
|||
return Status->PLAYER_WIN; |
|||
} |
|||
else if(IsTied()) { |
|||
return Status->TIE; |
|||
} |
|||
else { |
|||
return Status->PLAYING; |
|||
}; |
|||
} |
|||
else { |
|||
return Status->INVALID_MOVE; |
|||
}; |
|||
} |
|||
method : CpuMove() ~ Status { |
|||
if(@cpu_opening) { |
|||
select(Int->Random(2)) { |
|||
label 0: { |
|||
@board[0, 0] := 'O'; |
|||
} |
|||
label 1: { |
|||
@board[1, 1] := 'O'; |
|||
} |
|||
label 2: { |
|||
@board[2, 2] := 'O'; |
|||
} |
|||
}; |
|||
@cpu_opening := false; |
|||
} |
|||
else { |
|||
BestCpuMove(CopyBoard()); |
|||
}; |
|||
if(IsWinner(@board, 'O')) { |
|||
return Status->CPU_WIN; |
|||
} |
|||
else if(IsTied()) { |
|||
return Status->TIE; |
|||
} |
|||
else { |
|||
return Status->PLAYING; |
|||
}; |
|||
} |
|||
method : Minimax(board : Char[,], depth : Int, is_max : Bool, alpha : Int, beta : Int) ~ Int { |
|||
score := EvaluateMove(board); |
|||
if(score = 10 | score = -10) { |
|||
return score; |
|||
}; |
|||
if(IsTied()) { |
|||
return 0; |
|||
}; |
|||
if(is_max) { |
|||
best := Weights->MIN; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
for(j := 0; j < 3; j += 1;) { |
|||
if(board[i,j] <> 'X' & board[i,j] <>'O') { |
|||
test := board[i,j]; |
|||
board[i,j] := 'O'; |
|||
best := Int->Max(best, Minimax(board, depth + 1, false, alpha, beta)); |
|||
alpha := Int->Max(alpha, best); |
|||
board[i,j] := test; |
|||
if(beta <= alpha) { |
|||
return best; |
|||
}; |
|||
}; |
|||
}; |
|||
}; |
|||
return best; |
|||
} |
|||
else { |
|||
best := Weights->MAX; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
for(j := 0; j < 3; j += 1;) { |
|||
if(board[i,j] <> 'X' & board[i,j] <>'O') { |
|||
test := board[i,j]; |
|||
board[i,j] := 'X'; |
|||
best := Int->Min(best, Minimax(board, depth + 1, true, alpha, beta)); |
|||
beta := Int->Min(beta, best); |
|||
board[i,j] := test; |
|||
if(beta <= alpha) { |
|||
return best; |
|||
}; |
|||
}; |
|||
}; |
|||
}; |
|||
return best; |
|||
}; |
|||
} |
|||
method : BestCpuMove(board : Char[,]) ~ Nil { |
|||
best := Weights->MIN; # empty |
|||
best_i := -1; |
|||
best_j := -1; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
for(j := 0; j < 3; j += 1;) { |
|||
if(board[i,j] <> 'X' & board[i,j] <> 'O') { |
|||
test := board[i,j]; |
|||
board[i,j] := 'O'; |
|||
move := Int->Max(best, Minimax(board, 0, false, Weights->MIN, Weights->MAX)); |
|||
board[i,j] := test; |
|||
if(move > best) { |
|||
best_i := i; |
|||
best_j := j; |
|||
best := move; |
|||
}; |
|||
}; |
|||
}; |
|||
}; |
|||
@board[best_i, best_j] := 'O'; |
|||
} |
|||
method : EvaluateMove(board : Char[,]) ~ Int { |
|||
if(IsWinner(board, 'O')) { |
|||
return 10; |
|||
} |
|||
else if(IsWinner(board, 'X')) { |
|||
return -10; |
|||
} |
|||
else { |
|||
return 0; |
|||
}; |
|||
} |
|||
method : CopyBoard() ~ Char[,] { |
|||
board := Char->New[3, 3]; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
for(j := 0; j < 3; j += 1;) { |
|||
board[i,j] := @board[i,j]; |
|||
}; |
|||
}; |
|||
return board; |
|||
} |
|||
method : LegalMove(move : Char, player: Char) ~ Bool { |
|||
if(move >= '1' & move <= '9') { |
|||
index := (move - '1')->As(Int); |
|||
j := index / 3; i := index % 3; |
|||
if(@board[i, j] = 'X' | @board[i, j] = 'O') { |
|||
return false; |
|||
}; |
|||
@board[i, j] := player; |
|||
return true; |
|||
} |
|||
else { |
|||
return false; |
|||
}; |
|||
} |
|||
method : IsWinner(board : Char[,], player : Char) ~ Bool { |
|||
# --- diagonal --- |
|||
check := 0; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
if(board[i, i] = player) { |
|||
check += 1; |
|||
}; |
|||
}; |
|||
if(check = 3) { |
|||
return true; |
|||
}; |
|||
check := 0; |
|||
j := 2; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
if(board[i, j] = player) { |
|||
check += 1; |
|||
}; |
|||
j -= 1; |
|||
}; |
|||
if(check = 3) { |
|||
return true; |
|||
}; |
|||
# --- vertical --- |
|||
for(i := 0; i < 3; i += 1;) { |
|||
check := 0; |
|||
for(j := 0; j < 3; j += 1;) { |
|||
if(board[i, j] = player) { |
|||
check += 1; |
|||
}; |
|||
}; |
|||
if(check = 3) { |
|||
return true; |
|||
}; |
|||
}; |
|||
# --- horizontal --- |
|||
for(j := 0; j < 3; j += 1;) { |
|||
check := 0; |
|||
for(i := 0; i < 3; i += 1;) { |
|||
if(board[i, j] = player) { |
|||
check += 1; |
|||
}; |
|||
}; |
|||
if(check = 3) { |
|||
return true; |
|||
}; |
|||
}; |
|||
return false; |
|||
} |
|||
method : IsTied() ~ Bool { |
|||
for(i := 0; i < 3; i += 1;) { |
|||
for(j := 0; j < 3; j += 1;) { |
|||
if(@board[i, j] <> 'X' & @board[i, j] <> 'O') { |
|||
return false; |
|||
}; |
|||
}; |
|||
}; |
|||
return true; |
|||
} |
|||
method : Draw() ~ Nil { |
|||
a1 := @board[0, 0]; a2 := @board[1, 0]; a3 := @board[2, 0]; |
|||
b1 := @board[0, 1]; b2 := @board[1, 1]; b3 := @board[2, 1]; |
|||
c1 := @board[0, 2]; c2 := @board[1, 2]; c3 := @board[2, 2]; |
|||
"==========="->PrintLine(); |
|||
" {$a1} | {$a2} | {$a3} "->PrintLine(); |
|||
"---|---|---"->PrintLine(); |
|||
" {$b1} | {$b2} | {$b3} "->PrintLine(); |
|||
"---|---|---"->PrintLine(); |
|||
" {$c1} | {$c2} | {$c3} "->PrintLine(); |
|||
"===========\n"->PrintLine(); |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|Pascal}}== |
|||
Similar to on C version (same depth first strategy). Computer always win. |
|||
In some ways easier to read, but because Pascal lack return/break/continue |
|||
functions a little long and different style. Tested with FreePascal on macOS. |
|||
I would expect this version should compile with most Pascal variants including Delphi, but YMMR. |
|||
<syntaxhighlight lang="pascal">program Tic(Input, Output); |
|||
type |
|||
Contents = (Unassigned, Human, Computer); |
|||
var |
|||
BestI, BestJ: integer; { best solution a depth of zero in the search } |
|||
B: array[0..2, 0..2] of Contents; {zero based so modulus works later} |
|||
Player: Contents; |
|||
procedure DisplayBoard; |
|||
var |
|||
I, J: integer; |
|||
T: array [Contents] of char; |
|||
begin |
|||
T[Unassigned] := ' '; |
|||
T[Human] := 'O'; |
|||
T[Computer] := 'X'; |
|||
for I := 0 to 2 do |
|||
begin |
|||
for J := 0 to 2 do |
|||
begin |
|||
Write(T[B[I, J]]); |
|||
if J <> 2 then |
|||
Write(' | '); |
|||
end; |
|||
WriteLn; |
|||
if I < 2 then |
|||
WriteLn('---------'); |
|||
end; |
|||
WriteLn; |
|||
WriteLn; |
|||
end; |
|||
function SwapPlayer(Player: Contents): Contents; |
|||
begin |
|||
if Player = Computer then |
|||
SwapPlayer := Human |
|||
else |
|||
SwapPlayer := Computer; |
|||
end; |
|||
function CheckWinner: Contents; |
|||
var |
|||
I: integer; |
|||
begin |
|||
CheckWinner := Unassigned; { no winner yet } |
|||
for I := 0 to 2 do |
|||
begin |
|||
{ first horizontal solution } |
|||
if (CheckWinner = Unassigned) and (B[I, 0] <> Unassigned) and |
|||
(B[I, 1] = B[I, 0]) and (B[I, 2] = B[I, 0]) then |
|||
CheckWinner := B[I, 0] |
|||
else |
|||
{ now vertical solution } |
|||
if (CheckWinner = Unassigned) and (B[0, I] <> Unassigned) and |
|||
(B[1, I] = B[0, I]) and (B[2, I] = B[0, I]) then |
|||
CheckWinner := B[0, I]; |
|||
end; |
|||
{ now check the paths of the two cross line slants that share the middle position } |
|||
if (CheckWinner = Unassigned) and (B[1, 1] <> Unassigned) then |
|||
begin |
|||
if (B[1, 1] = B[0, 0]) and (B[2, 2] = B[0, 0]) then |
|||
CheckWinner := B[0, 0] |
|||
else if (B[1, 1] = B[2, 0]) and (B[0, 2] = B[1, 1]) then |
|||
CheckWinner := B[1, 1]; |
|||
end; |
|||
end; |
|||
{ Basic strategy test - is this te best solution we have seen } |
|||
function SaveBest(CurScore, CurBest: Contents): boolean; |
|||
begin |
|||
if CurScore = CurBest then |
|||
SaveBest := False |
|||
else if (CurScore = Unassigned) and (CurBest = Human) then |
|||
SaveBest := False |
|||
else if (CurScore = Computer) and ((CurBest = Unassigned) or |
|||
(CurBest = Human)) then |
|||
SaveBest := False |
|||
else |
|||
SaveBest := True; |
|||
end; |
|||
{ Basic strategy - recursive depth first search of possible moves |
|||
if computer can win save it, otherwise block if need be, else do deeper. |
|||
At each level modify the board for the next call, but clean up as go back up, |
|||
by remembering the modified position on the call stack. } |
|||
function TestMove(Val: Contents; Depth: integer): Contents; |
|||
var |
|||
I, J: integer; |
|||
Score, Best, Changed: Contents; |
|||
begin |
|||
Best := Computer; |
|||
Changed := Unassigned; |
|||
Score := CheckWinner; |
|||
if Score <> Unassigned then |
|||
begin |
|||
if Score = Val then |
|||
TestMove := Human |
|||
else |
|||
TestMove := Computer; |
|||
end |
|||
else |
|||
begin |
|||
for I := 0 to 2 do |
|||
for J := 0 to 2 do |
|||
begin |
|||
if B[I, J] = Unassigned then |
|||
begin |
|||
Changed := Val; |
|||
B[I, J] := Val; |
|||
{ the value for now and try wioth the other player } |
|||
Score := TestMove(SwapPlayer(Val), Depth + 1); |
|||
if Score <> Unassigned then |
|||
Score := SwapPlayer(Score); |
|||
B[I, J] := Unassigned; |
|||
if SaveBest(Score, Best) then |
|||
begin |
|||
if Depth = 0 then |
|||
begin { top level, so remember actual position } |
|||
BestI := I; |
|||
BestJ := J; |
|||
end; |
|||
Best := Score; |
|||
end; |
|||
end; |
|||
end; |
|||
if Changed <> Unassigned then |
|||
TestMove := Best |
|||
else |
|||
TestMove := Unassigned; |
|||
end; |
|||
end; |
|||
function PlayGame(Whom: Contents): string; |
|||
var |
|||
I, J, K, Move: integer; |
|||
Win: Contents; |
|||
begin |
|||
Win := Unassigned; |
|||
for I := 0 to 2 do |
|||
for J := 0 to 2 do |
|||
B[I, J] := Unassigned; |
|||
WriteLn('The board positions are numbered as follows:'); |
|||
WriteLn('1 | 2 | 3'); |
|||
WriteLn('---------'); |
|||
WriteLn('4 | 5 | 6'); |
|||
WriteLn('---------'); |
|||
WriteLn('7 | 8 | 9'); |
|||
WriteLn('You have O, I have X.'); |
|||
WriteLn; |
|||
K := 1; |
|||
repeat {rather a for loop but can not have two actions or early termination in Pascal} |
|||
if Whom = Human then |
|||
begin |
|||
repeat |
|||
Write('Your move: '); |
|||
ReadLn(Move); |
|||
if (Move < 1) or (Move > 9) then |
|||
WriteLn('Opps: enter a number between 1 - 9.'); |
|||
Dec(Move); |
|||
{humans do 1 -9, but the computer wants 0-8 for modulus to work} |
|||
I := Move div 3; { convert from range to corridinated of the array } |
|||
J := Move mod 3; |
|||
if B[I, J] <> Unassigned then |
|||
WriteLn('Opps: move ', Move + 1, ' was already done.') |
|||
until (Move >= 0) and (Move <= 8) and (B[I, J] = Unassigned); |
|||
B[I, J] := Human; |
|||
end; |
|||
if Whom = Computer then |
|||
begin |
|||
{ randomize if computer opens, so its not always the same game } |
|||
if K = 1 then |
|||
begin |
|||
BestI := Random(3); |
|||
BestJ := Random(3); |
|||
end |
|||
else |
|||
Win := TestMove(Computer, 0); |
|||
B[BestI, BestJ] := Computer; |
|||
WriteLn('My move: ', BestI * 3 + BestJ + 1); |
|||
end; |
|||
DisplayBoard; |
|||
Win := CheckWinner; |
|||
if Win <> Unassigned then |
|||
begin |
|||
if Win = Human then |
|||
PlayGame := 'You win.' |
|||
else |
|||
PlayGame := 'I win.'; |
|||
end |
|||
else |
|||
begin |
|||
Inc(K); { "for" loop counter actions } |
|||
Whom := SwapPlayer(Whom); |
|||
end; |
|||
until (Win <> Unassigned) or (K > 9); |
|||
if Win = Unassigned then |
|||
PlayGame := 'A draw.'; |
|||
end; |
|||
begin |
|||
Randomize; |
|||
Player := Human; |
|||
while True do |
|||
begin |
|||
WriteLn(PlayGame(Player)); |
|||
WriteLn; |
|||
Player := SwapPlayer(Player); |
|||
end |
|||
end.</syntaxhighlight> |
|||
=={{header|Perl}}== |
|||
A basic negamax search (with caching) is done to find the best move. |
|||
If there are several equally good moves, one of them is selected randomly. |
|||
The computer player is not perfect, and so a human player can sometimes win. |
|||
This is not perl's fault, but mine; it ought to always be a tie, or a win for |
|||
the computer. Anyone who can identify the mistake, is welcome to fix it. |
|||
<syntaxhighlight lang="perl">use warnings; |
|||
use strict; |
|||
my $initial = join ",", qw(abc def ghi); |
|||
my %reverse = qw(X O O X); |
|||
# In list context, returns best move, |
|||
# In scalar context, returns the score of best move. |
|||
my %cache; |
|||
sub best_move { |
|||
my ($b, $me) = @_; |
|||
if( exists $cache{$b,$me,wantarray} ) { |
|||
return $cache{$b,$me,wantarray}; |
|||
} elsif( my $s = score( $b, $me ) ) { |
|||
return $cache{$b,$me,wantarray} = (wantarray ? undef : $s); |
|||
} |
|||
my $him = $reverse{$me}; |
|||
my ($best, @best) = (-999); |
|||
for my $m (moves($b)) { |
|||
(my $with_m = $b) =~ s/$m/$me/ or die; |
|||
# The || operator supplies scalar context to best_move(...) |
|||
my $s = -(score($with_m, $him) || best_move($with_m, $him)); |
|||
if( $s > $best ) { |
|||
($best, @best) = ($s, $m); |
|||
} elsif( $s == $best ) { |
|||
push @best, $m; |
|||
} |
|||
} |
|||
$cache{$b,$me,wantarray} = wantarray ? $best[rand @best] : $best; |
|||
} |
|||
my $winner = q[([XOxo])(?:\1\1|...\1...\1|..\1..\1|....\1....\1)]; |
|||
sub score { |
|||
my ($b, $me) = @_; |
|||
$b =~ m/$winner/o or return 0; |
|||
return $1 eq $me ? +1 : -1; |
|||
} |
|||
sub moves { |
|||
my ($b) = @_; |
|||
$b =~ /([^xoXO,\n])/g; |
|||
} |
|||
sub print_board { |
|||
my ($b) = @_; |
|||
$b =~ s/\B/|/g; |
|||
$b =~ s/,/\n-+-+-\n/g; |
|||
print $b, "\n"; |
|||
} |
|||
sub prompt { |
|||
my ($b, $color) = @_; |
|||
my @moves = moves($b); |
|||
unless( @moves ) { |
|||
return; |
|||
} |
|||
while( 1 ) { |
|||
print "Place your $color on one of [@moves]: "; |
|||
defined(my $m = <>) or return; |
|||
chomp($m); |
|||
return $m if grep $m eq $_, @moves; |
|||
} |
|||
} |
|||
my @players = ( |
|||
{ whose => "your", name => "You", |
|||
verb => "You place", get_move => \&prompt }, |
|||
{ whose => "the computer's", name => "Computer", |
|||
verb => "The computer places", get_move => \&best_move }, |
|||
); |
|||
my $whose_turn = int rand 2; |
|||
my $color = "X"; |
|||
my $b = $initial; |
|||
while( 1 ) { |
|||
my $p = $players[$whose_turn]; |
|||
print_board($b); |
|||
print "It is $p->{whose} turn.\n"; |
|||
# The parens around $m supply list context to the right side |
|||
# or the = operator, which causes sub best_move to return the |
|||
# best move, rather than the score of the best move. |
|||
my ( $m ) = $p->{get_move}->($b, $color); |
|||
if( $m ) { |
|||
print "$p->{verb} an $color at $m\n"; |
|||
$b =~ s/$m/$color/; |
|||
my $s = score($b, $color) or next; |
|||
print_board($b); |
|||
print "$p->{name} ", $s > 0 ? "won!\n" : "lost!\n"; |
|||
} else { |
|||
print "$p->{name} cannot move.\n"; |
|||
} |
|||
print "Game over.\nNew Game...\n"; |
|||
($b, $color, $whose_turn) = ($initial, "X", int rand 2); |
|||
redo; |
|||
} continue { |
|||
$color = $reverse{$color}; |
|||
$whose_turn = !$whose_turn; |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>a|b|c |
|||
-+-+- |
|||
d|e|f |
|||
-+-+- |
|||
g|h|i |
|||
It is your turn. |
|||
Place your X on one of [a b c d e f g h i]: e |
|||
You place an X at e |
|||
a|b|c |
|||
-+-+- |
|||
d|X|f |
|||
-+-+- |
|||
g|h|i |
|||
It is the computer's turn. |
|||
The computer places an O at c |
|||
a|b|O |
|||
-+-+- |
|||
d|X|f |
|||
-+-+- |
|||
g|h|i |
|||
It is your turn. |
|||
Place your X on one of [a b d f g h i]: a |
|||
You place an X at a |
|||
X|b|O |
|||
-+-+- |
|||
d|X|f |
|||
-+-+- |
|||
g|h|i |
|||
It is the computer's turn. |
|||
The computer places an O at f |
|||
X|b|O |
|||
-+-+- |
|||
d|X|O |
|||
-+-+- |
|||
g|h|i |
|||
It is your turn. |
|||
Place your X on one of [b d g h i]: i |
|||
You place an X at i |
|||
X|b|O |
|||
-+-+- |
|||
d|X|O |
|||
-+-+- |
|||
g|h|X |
|||
You won! |
|||
Game over. |
|||
New Game... |
|||
a|b|c |
|||
-+-+- |
|||
d|e|f |
|||
-+-+- |
|||
g|h|i |
|||
It is your turn. |
|||
Place your X on one of [a b c d e f g h i]: |
|||
</pre> |
|||
===Alternate with GUI=== |
|||
<syntaxhighlight lang="perl">#!/usr/bin/perl |
|||
use strict; |
|||
use warnings; |
|||
use Tk; |
|||
use List::Util qw( shuffle ); |
|||
my $win = qr/(?| ^(\w)...\1...\1 | ^..(\w).\1.\1 # diagonals |
|||
| ^(?:...)*?(\w)\1\1 | (\w)..\1..\1 )/x; # row or column |
|||
my (%cache, $message, $game); |
|||
my $mw = MainWindow->new( -title => 'TicTacToe' ); |
|||
$mw->geometry('+1000+300'); |
|||
$mw->Label(-textvariable => \$message, -font => 'courierbold 16', |
|||
)->pack(-fill => 'x'); |
|||
my $grid = $mw->Frame( -borderwidth => 5, -relief => 'ridge' )->pack; |
|||
$mw->Button(-text => $_->[0], -command => $_->[1], |
|||
)->pack(-side => 'left', -fill => 'x', -expand => 1) for |
|||
['Restart X first' => sub { restart(1) }], |
|||
['Restart O first' => sub { restart(0) }], |
|||
[ 'Exit' => sub { $mw->destroy }]; |
|||
my @cells = map { my $me = $_; |
|||
$grid->Button( -command => sub { person($me) }, |
|||
-width => 1, -height => 1, -font => 'courierbold 40', |
|||
)->grid(-row => int $_ / 3, -column => $_ % 3) |
|||
} 0 .. 8; |
|||
restart(1); |
|||
MainLoop; |
|||
sub show { $cells[$_]->configure(-text => substr $game, $_, 1) for 0 .. 8 } |
|||
sub person |
|||
{ |
|||
$message =~ /O's turn/ or return; |
|||
pos($game) = shift(); |
|||
if( $game =~ s/\G /O/ ) |
|||
{ |
|||
$message = $game =~ $win ? "O Wins" : |
|||
$game !~ / / ? "Draw" : do { |
|||
$game = move( $game, 'X' )->[1]; |
|||
$game =~ $win ? 'X Wins' : |
|||
$game !~ / / ? 'Draw' : "O's turn to move" |
|||
}; |
|||
show; |
|||
} |
|||
} |
|||
sub restart |
|||
{ |
|||
%cache = (); |
|||
$game = shift() ? move( ' ' x 9, 'X' )->[1] : ' ' x 9; |
|||
show; |
|||
$message = "O's turn to move"; |
|||
} |
|||
sub move |
|||
{ |
|||
(local $_, my $who, my @moves) = @_; |
|||
/$win/ and return [ 2 * ($1 eq 'X'), $_ ]; |
|||
/ / or return [ 1, $_ ]; |
|||
$cache{$_ . $who} //= do |
|||
{ |
|||
while( / /g ) |
|||
{ |
|||
my $move = "$`$who$'"; |
|||
push @moves, [ move($move, $who ^ 'X' ^ 'O')->[0], $move ]; |
|||
} |
|||
(sort {$a->[0] <=> $b->[0]} shuffle @moves)[ -($who eq 'X') ] |
|||
}; |
|||
}</syntaxhighlight> |
|||
=={{header|Phix}}== |
|||
AI copied from C. User goes first, as does loser. After a draw the start player alternates. |
|||
{{libheader|Phix/pGUI}} |
|||
{{libheader|Phix/online}} |
|||
You can run this online [http://phix.x10.mx/p2js/Tic_tac_toe.htm here]. |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
-- demo\rosetta\Tic_tac_toe.exw |
|||
--</span> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">title</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Tic Tac Toe"</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">board</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {' '/'X'/'O'}</span> |
|||
<span style="color: #004080;">bool</span> <span style="color: #000000;">human</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> <span style="color: #000080;font-style:italic;">-- (flipped in new_game)</span> |
|||
<span style="color: #004080;">bool</span> <span style="color: #000000;">game_over</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">play_dumb</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">dlg</span> |
|||
<span style="color: #000080;font-style:italic;">-- saved in redraw_cb() for check_position():</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">cw</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- board centre</span> |
|||
<span style="color: #000000;">d</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- tile spacing</span> |
|||
<span style="color: #000000;">h</span> <span style="color: #000080;font-style:italic;">-- tile size/radius</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">redraw_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000080;font-style:italic;">/*posx*/</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">/*posy*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetIntInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ih</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"DRAWSIZE"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">8</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">h</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">/</span><span style="color: #000000;">3</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cw</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupGLMakeCurrent</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetAttributePtr</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ih</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"DBUFFER"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasActivate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasClear</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetForeground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #004600;">CD_BLUE</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetLineWidth</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">d3</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">*</span><span style="color: #000000;">d</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">-</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">d</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=+</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">my</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">y</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">d</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">=-</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">mx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">x</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">d</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">mark</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">pdx</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'X'</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">my</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">my</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">my</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">my</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'O'</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">cdCanvasCircle</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">my</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #7060A8;">cdCanvasFlush</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">map_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupGLMakeCurrent</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_IUP</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetDouble</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"SCREENDPI"</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">25.4</span> |
|||
<span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_GL</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"10x10 %g"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #7060A8;">IupSetAttributePtr</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"DBUFFER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetBackground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_PARCHMENT</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">canvas_resize_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetAttributePtr</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"DBUFFER"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">canvas_width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas_height</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetIntInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"DRAWSIZE"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetDouble</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"SCREENDPI"</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">25.4</span> |
|||
<span style="color: #7060A8;">cdCanvasSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"SIZE"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"%dx%d %g"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">canvas_width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas_height</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">wins</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">}}</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">check_winner</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wins</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">j</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">wins</span><span style="color: #0000FF;">[</span><span style="color: #000000;">w</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">mark</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">and</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">=</span><span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">and</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">=</span><span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">mark</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">best_i</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">test_move</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">mark</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">score</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">check_winner</span><span style="color: #0000FF;">(),</span> |
|||
<span style="color: #000000;">best</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">changed</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">score</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">score</span><span style="color: #0000FF;">=</span><span style="color: #000000;">mark</span><span style="color: #0000FF;">?</span><span style="color: #000000;">1</span><span style="color: #0000FF;">:-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">9</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">changed</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">mark</span> |
|||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">mark</span> |
|||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">test_move</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'O'</span><span style="color: #0000FF;">+</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">-</span><span style="color: #000000;">mark</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">depth</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">score</span><span style="color: #0000FF;">></span><span style="color: #000000;">best</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">best_i</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">;</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">best</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">score</span><span style="color: #0000FF;">;</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">changed</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">?</span><span style="color: #000000;">best</span><span style="color: #0000FF;">:</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">check_game_over</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">win</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">check_winner</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">win</span> <span style="color: #008080;">or</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">board</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">winner</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">win</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'O'</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"You win!"</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">win</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"Computer wins"</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #008000;">"Draw"</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s - %s"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">,</span><span style="color: #000000;">winner</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #000000;">game_over</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">play_move</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">move</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">move</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'O'</span> |
|||
<span style="color: #000000;">check_game_over</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">game_over</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">human</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">play_dumb</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find_all</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">board</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">best_i</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">))]</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">test_move</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">best_i</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">best_i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'X'</span> |
|||
<span style="color: #000000;">check_game_over</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #000000;">human</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">not</span> <span style="color: #000000;">game_over</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">new_game</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #000000;">board</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">human</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">not</span> <span style="color: #000000;">human</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">human</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'X'</span> |
|||
<span style="color: #000000;">human</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">check_position</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">px</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">py</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
-- check if x,y is on a legal move. |
|||
-- uses ch,cw,d,h as saved by redraw_cb(). |
|||
--</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=-</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">my</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">+</span><span style="color: #000000;">y</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">d</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">=-</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">mx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">cw</span><span style="color: #0000FF;">+</span><span style="color: #000000;">x</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">d</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">px</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span> <span style="color: #008080;">and</span> <span style="color: #000000;">px</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span> |
|||
<span style="color: #008080;">and</span> <span style="color: #000000;">py</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">my</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span> <span style="color: #008080;">and</span> <span style="color: #000000;">py</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">my</span><span style="color: #0000FF;">+</span><span style="color: #000000;">h</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">mark</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">pdx</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">mark</span><span style="color: #0000FF;">==</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">?</span><span style="color: #000000;">pdx</span><span style="color: #0000FF;">:</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">button_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*canvas*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">button</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pressed</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000080;font-style:italic;">/*pStatus*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">button</span><span style="color: #0000FF;">=</span><span style="color: #004600;">IUP_BUTTON1</span> <span style="color: #008080;">and</span> <span style="color: #008080;">not</span> <span style="color: #000000;">pressed</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">-- (left button released)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">game_over</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">game_over</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #000000;">new_game</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #000000;">play_move</span><span style="color: #0000FF;">(</span><span style="color: #000000;">check_position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #7060A8;">IupRedraw</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CONTINUE</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">exit_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">help_text</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""" |
|||
Tic Tac Toe, also known as Noughts and Crosses. |
|||
The aim is to get three Os (or Xs) in a row. |
|||
Human(O) plays first, as does loser. After a draw first player alternates. |
|||
Computer(X) plays a random move first, to make it more interesting. |
|||
Setting the constant play_dumb to true disables the internal AI. |
|||
Once a game is over click anywhere on the board to start a new game. |
|||
"""</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">help_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandln</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupMessage</span><span style="color: #0000FF;">(</span><span style="color: #000000;">title</span><span style="color: #0000FF;">,</span><span style="color: #000000;">help_text</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #000080;font-style:italic;">-- Other possible keys: |
|||
-- Q - quit (end program) [==X?] |
|||
-- C - concede (start new game)</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">key_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*dlg*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_ESC</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_F1</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">help_cb</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CONTINUE</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">canvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGLCanvas</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RASTERSIZE=800x800"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">dlg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">`TITLE="%s",MINSIZE=245x180`</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #7060A8;">IupSetCallbacks</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"MAP_CB"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"map_cb"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #008000;">"ACTION"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"redraw_cb"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #008000;">"RESIZE_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"canvas_resize_cb"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #008000;">"BUTTON_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"button_cb"</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"KEY_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"key_cb"</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #7060A8;">IupSetAttributeHandle</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"PARENTDIALOG"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">new_game</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RASTERSIZE"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|PHP}}== |
|||
A simple interactive version for the browser. |
|||
NOTE: While PHP can considered a super-set of HTML/JS, usage has been kept to a minimum to focus on the more specific PHP parts. |
|||
<syntaxhighlight lang="php"> |
|||
<?php |
|||
const BOARD_NUM = 9; |
|||
const ROW_NUM = 3; |
|||
$EMPTY_BOARD_STR = str_repeat('.', BOARD_NUM); |
|||
function isGameOver($board, $pin) { |
|||
$pat = |
|||
'/X{3}|' . //Horz |
|||
'X..X..X..|' . //Vert Left |
|||
'.X..X..X.|' . //Vert Middle |
|||
'..X..X..X|' . //Vert Right |
|||
'..X.X.X..|' . //Diag TL->BR |
|||
'X...X...X|' . //Diag TR->BL |
|||
'[^\.]{9}/i'; //Cat's game |
|||
if ($pin == 'O') $pat = str_replace('X', 'O', $pat); |
|||
return preg_match($pat, $board); |
|||
} |
|||
//Start |
|||
$boardStr = isset($_GET['b'])? $_GET['b'] : $EMPTY_BOARD_STR; |
|||
$turn = substr_count($boardStr, '.')%2==0? 'O' : 'X'; |
|||
$oppTurn = $turn == 'X'? 'O' : 'X'; |
|||
$gameOver = isGameOver($boardStr, $oppTurn); |
|||
//Display board |
|||
echo '<style>'; |
|||
echo 'td {width: 200px; height: 200px; text-align: center; }'; |
|||
echo '.pin {font-size:72pt; text-decoration:none; color: black}'; |
|||
echo '.pin.X {color:red}'; |
|||
echo '.pin.O {color:blue}'; |
|||
echo '</style>'; |
|||
echo '<table border="1">'; |
|||
$p = 0; |
|||
for ($r = 0; $r < ROW_NUM; $r++) { |
|||
echo '<tr>'; |
|||
for ($c = 0; $c < ROW_NUM; $c++) { |
|||
$pin = $boardStr[$p]; |
|||
echo '<td>'; |
|||
if ($gameOver || $pin != '.') echo '<span class="pin ', $pin, '">', $pin, '</span>'; //Occupied |
|||
else { //Available |
|||
$boardDelta = $boardStr; |
|||
$boardDelta[$p] = $turn; |
|||
echo '<a class="pin ', $pin, '" href="?b=', $boardDelta, '">'; |
|||
echo $boardStr[$p]; |
|||
echo '</a>'; |
|||
} |
|||
echo '</td>'; |
|||
$p++; |
|||
} |
|||
echo '</tr>'; |
|||
echo '<input type="hidden" name="b" value="', $boardStr, '"/>'; |
|||
} |
|||
echo '</table>'; |
|||
echo '<a href="?b=', $EMPTY_BOARD_STR, '">Reset</a>'; |
|||
if ($gameOver) echo '<h1>Game Over!</h1>'; |
|||
</syntaxhighlight> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
This solution doesn't bother about the game logic, but simply uses the alpha-beta-pruning 'game' function in the "simul" library. |
This solution doesn't bother about the game logic, but simply uses the alpha-beta-pruning 'game' function in the "simul" library. |
||
< |
<syntaxhighlight lang="picolisp">(load "@lib/simul.l") # for 'game' function |
||
(de display () |
(de display () |
||
Line 1,033: | Line 12,223: | ||
((find3 T) "Congratulation, you won!") |
((find3 T) "Congratulation, you won!") |
||
((not (myMove)) "No moves") |
((not (myMove)) "No moves") |
||
((find3 0) "Sorry, you lost!") ) )</ |
((find3 0) "Sorry, you lost!") ) )</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>: (main) |
<pre>: (main) |
||
+---+---+---+ |
+---+---+---+ |
||
Line 1,062: | Line 12,252: | ||
+---+---+---+ |
+---+---+---+ |
||
a b c</pre> |
a b c</pre> |
||
=={{header|PostScript}}== |
|||
<syntaxhighlight lang="postscript"> |
|||
%!PS |
|||
% |
|||
% Play Tic-Tac-Toe against your printer |
|||
% 2024-04 Nicolas Seriot https://github.com/nst/PSTicTacToe |
|||
% |
|||
% On GhostScript: |
|||
% gs -DNOSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="%d.pdf" ttt.ps |
|||
% |
|||
% On a real PostScript printer: |
|||
% cat ttt.ps - | nc 192.168.2.10 9100 |
|||
<</PageSize[595 842]>>setpagedevice/Courier findfont 48 scalefont setfont/b |
|||
(.........)def/f{/n 0 def b{46 eq{/n n 1 add def}if}forall n}def/w{/c exch def |
|||
[(O)(X)]{/p exch def[[0 1 2][3 4 5][6 7 8][0 3 6][1 4 7][2 5 8][0 4 8][2 4 6]]{ |
|||
/t exch def/o true def t{/pos exch def/o c pos 1 getinterval p eq o and def} |
|||
forall o{exit}if}forall o{exit}if}forall o}def/g{/s exch def/m null def f 0 eq{ |
|||
/m(TIE)def}if b w{/m(XXXXXXX WINS)def m 0 s putinterval}if()= 0 3 6{b exch 3 |
|||
getinterval =}for()= m null ne{m =}if 4 setlinewidth 200 700 moveto 200 400 |
|||
lineto 300 700 moveto 300 400 lineto 100 600 moveto 400 600 lineto 100 500 |
|||
moveto 400 500 lineto stroke 0 1 b length 1 sub{/i exch def b i 1 getinterval |
|||
(.)ne{gsave 0 0 moveto 100 i 3 mod 100 mul add 35 add 700 i 3 idiv 100 mul sub |
|||
65 sub moveto b i 1 getinterval show grestore}if}for m null ne{100 300 moveto m |
|||
show}if showpage m null ne{quit}if}def{/d false def 0 1 8{/i exch def/e b dup |
|||
length string cvs def e i 1 getinterval(.)eq{[(X)(O)]{/p exch def e i p |
|||
putinterval e w{b i(X)putinterval/d true def exit}if}forall}if d{exit}if}for d |
|||
not{/x rand f mod def/c 0 def 0 1 b length 1 sub{/i exch def b i 1 getinterval |
|||
(.)eq{c x eq{b i(X)putinterval exit}if/c c 1 add def}if}for}if(PRINTER)g b{ |
|||
(human turn (1-9) >)print flush(%lineedit)(r)file( )readline pop dup length |
|||
0 gt{0 1 getinterval}{pop( )}ifelse/o exch def(123456789)o search{pop pop pop b |
|||
o cvi 1 sub 1 getinterval(.)eq{o cvi 1 sub exit}if}{pop}ifelse(bad input) ==} |
|||
loop(O)putinterval(HUMAN )g}loop |
|||
</syntaxhighlight> |
|||
=={{header|Prolog}}== |
|||
Works with SWI-Prolog.<br> |
|||
Uses a minimax algorithm with no Alpha-beta pruning, as the max depth of the recursion is 8. Computer never loses.<br> |
|||
A GUI interface written in XPCE is given. |
|||
<syntaxhighlight lang="prolog">:- use_module('min-max.pl'). |
|||
:-dynamic box/2. |
|||
:- dynamic tic_tac_toe_window/1. |
|||
% Computer begins. |
|||
tic-tac-toe(computer) :- |
|||
V is random(9), |
|||
TTT = [_,_,_,_,_,_ ,_,_,_], |
|||
nth0(V, TTT, o), |
|||
display_tic_tac_toe(TTT). |
|||
% Player begins |
|||
tic-tac-toe(me) :- |
|||
TTT = [_,_,_,_,_,_ ,_,_,_], |
|||
display_tic_tac_toe(TTT). |
|||
display_tic_tac_toe(TTT) :- |
|||
retractall(box(_,_)), |
|||
retractall(tic_tac_toe_window(_)), |
|||
new(D, window('Tic-tac-Toe')), |
|||
send(D, size, size(170,170)), |
|||
X = 10, Y = 10, |
|||
display(D, X, Y, 0, TTT), |
|||
assert(tic_tac_toe_window(D)), |
|||
send(D, open). |
|||
display(_, _, _, _, []). |
|||
display(D, X, Y, N, [A,B,C|R]) :- |
|||
display_line(D, X, Y, N, [A,B,C]), |
|||
Y1 is Y+50, |
|||
N3 is N+3, |
|||
display(D, X, Y1, N3, R). |
|||
display_line(_, _, _, _, []). |
|||
display_line(D, X, Y, N, [C|R]) :- |
|||
( nonvar(C)-> C1 = C; C1 = ' '), |
|||
new(B, tic_tac_toe_box(C1)), |
|||
assertz(box(N, B)), |
|||
send(D, display, B, point(X, Y)), |
|||
X1 is X + 50, |
|||
N1 is N+1, |
|||
display_line(D, X1, Y, N1, R). |
|||
% class tic_tac_toe_box |
|||
% display an 'x' when the player clicks |
|||
% display an 'o' when the computer plays |
|||
:- pce_begin_class(tic_tac_toe_box, box, "Graphical window with text"). |
|||
variable(mess, any, both, "text to display"). |
|||
initialise(P, Lbl) :-> |
|||
send(P, send_super, initialise), |
|||
send(P, slot, mess, Lbl), |
|||
WS = 50, HS = 50, |
|||
send(P, size, size(WS,HS)), |
|||
send(P, recogniser, |
|||
handler_group(new(click_gesture(left, |
|||
'', |
|||
single, |
|||
message(@receiver, my_click))))). |
|||
% the box is clicked |
|||
my_click(B) :-> |
|||
send(B, set_val, x), |
|||
send(@prolog, play). |
|||
% only works when the box is "free" |
|||
set_val(B, Val) :-> |
|||
get(B, slot, mess, ' '), |
|||
send(B, slot, mess, Val), |
|||
send(B, redraw), |
|||
send(B, flush). |
|||
% redefined method to display custom graphical objects. |
|||
'_redraw_area'(P, A:area) :-> |
|||
send(P, send_super, '_redraw_area', A), |
|||
%we display the text |
|||
get(P, slot, mess, Lbl), |
|||
new(Str1, string(Lbl)), |
|||
get_object(P, area, area(X,Y,W,H)), |
|||
send(P, draw_box, X, Y, W, H), |
|||
send(P, draw_text, Str1, |
|||
font(times, normal, 30), |
|||
X, Y, W, H, center, center). |
|||
:- pce_end_class. |
|||
play :- |
|||
numlist(0, 8, L), |
|||
maplist(init, L, TTT), |
|||
finished(x, TTT, Val), |
|||
( Val = 2 -> send(@display, inform,'You win !'), |
|||
tic_tac_toe_window(D), |
|||
send(D, destroy) |
|||
; ( Val = 1 -> send(@display, inform,'Draw !'), |
|||
tic_tac_toe_window(D), |
|||
send(D, destroy) |
|||
; next_move(TTT, TT1), |
|||
maplist(display, L, TT1), |
|||
finished(o, TT1, V), |
|||
( V = 2 -> send(@display, inform,'I win !'), |
|||
tic_tac_toe_window(D), |
|||
send(D, destroy) |
|||
; ( V = 1 -> send(@display, inform,'Draw !'), |
|||
tic_tac_toe_window(D), |
|||
send(D, destroy) |
|||
; true)))). |
|||
% use minmax to compute the next move |
|||
next_move(TTT, TT1) :- |
|||
minimax(o, 0, 1024, TTT, _V1- TT1). |
|||
% we display the new board |
|||
display(I, V) :- |
|||
nonvar(V), |
|||
box(I, V1), |
|||
send(V1, set_val, V). |
|||
display(_I, _V). |
|||
% we create the board for minmax |
|||
init(I, V) :- |
|||
box(I, V1), |
|||
get(V1, slot, mess, V), |
|||
V \= ' '. |
|||
init(_I, _V). |
|||
% winning position for the player P ? |
|||
winned(P, [A1, A2, A3, A4, A5, A6, A7, A8, A9]) :- |
|||
(is_winning_line(P, [A1, A2, A3]); |
|||
is_winning_line(P, [A4, A5, A6]); |
|||
is_winning_line(P, [A7, A8, A9]); |
|||
is_winning_line(P, [A1, A4, A7]); |
|||
is_winning_line(P, [A2 ,A5, A8]); |
|||
is_winning_line(P, [A3, A6, A9]); |
|||
is_winning_line(P, [A1, A5, A9]); |
|||
is_winning_line(P, [A3, A5, A7])). |
|||
is_winning_line(P, [A, B, C]) :- |
|||
nonvar(A), A = P, |
|||
nonvar(B), B = P, |
|||
nonvar(C), C = P. |
|||
% Winning position for the player |
|||
eval(Player, Deep, TTT, V) :- |
|||
winned(Player, TTT), |
|||
( Player = o -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep). |
|||
% Loosing position for the player |
|||
eval(Player, Deep, TTT, V) :- |
|||
select(Player, [o,x], [Player1]), |
|||
winned(Player1, TTT), |
|||
( Player = x -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep). |
|||
% Draw position |
|||
eval(_Player, _Deep, TTT, 0) :- |
|||
include(var, TTT, []). |
|||
% we fetch the free positions of the board |
|||
possible_move(TTT, LMove) :- |
|||
new(C, chain), |
|||
forall(between(0,8, I), |
|||
( nth0(I, TTT, X), |
|||
( var(X) -> send(C, append, I); true))), |
|||
chain_list(C, LMove). |
|||
% we create the new position when the player P clicks |
|||
% the box "N" |
|||
assign_move(P, TTT, N, TT1) :- |
|||
copy_term(TTT, TT1), |
|||
nth0(N, TT1, P). |
|||
% We fetch all the possible boards obtained from board TTT |
|||
% for the player P |
|||
get_next(Player, Deep, TTT, Player1, Deep1, L):- |
|||
possible_move(TTT, LMove), |
|||
select(Player, [o,x], [Player1]), |
|||
Deep1 is Deep + 1, |
|||
maplist(assign_move(Player, TTT), LMove, L). |
|||
% The game is over ? |
|||
% Player P wins |
|||
finished(P, TTT, 2) :- |
|||
winned(P, TTT). |
|||
% Draw |
|||
finished(_P, TTT, 1) :- |
|||
include(var, TTT, []). |
|||
% the game is not over |
|||
finished(_P, _TTT, 0) . |
|||
% minmax must knows when the computer plays |
|||
% (o for ordinateur in French) |
|||
computer(o). |
|||
</syntaxhighlight> |
|||
Module min-max.pl defines minimax algorithm. |
|||
<syntaxhighlight lang="prolog">:- module('min-max.pl', [minimax/5]). |
|||
% minimax(Player, Deep, MaxDeep, B, V-B) |
|||
% @arg1 : current player at this level |
|||
% @arg2 : current level of recursion |
|||
% @arg3 : max level of recursion (in this version of the game no use : set to 1024 !) |
|||
% @arg4 : current board |
|||
% @arg5 : B is the evaluation of the board, the result is V-B to know the new board |
|||
% Here we get an evaluation |
|||
minimax(Player, Deep, MaxDeep, B, V-B) :- |
|||
( eval(Player, Deep, B, V) -> true |
|||
; % in this version of the game this second division always fails |
|||
( Deep > MaxDeep -> V is random(1000) - 1000)). |
|||
% here we must compute all the possible moves to know the evaluation of the board |
|||
minimax(Player, Deep, MaxDeep, B, V) :- |
|||
get_next(Player, Deep, B, Player1, Deep1, L), |
|||
maplist(minimax(Player1, Deep1, MaxDeep), L, LV), |
|||
maplist(lie, L, LV, TLV), |
|||
sort(TLV, SLVTmp), |
|||
( computer(Player) -> reverse(SLVTmp, SLV); SLV = SLVTmp), |
|||
SLV = [V | _R]. |
|||
lie(TTT, V-_, V-TTT). |
|||
</syntaxhighlight> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
The computer enforces the rules but plays a random game. |
The computer enforces the rules but plays a random game. |
||
< |
<syntaxhighlight lang="python"> |
||
''' |
''' |
||
Tic-tac-toe game player. |
Tic-tac-toe game player. |
||
Line 1,140: | Line 12,609: | ||
else: |
else: |
||
print('\nA draw') |
print('\nA draw') |
||
</syntaxhighlight> |
|||
</lang> |
|||
'''Sample Game''' |
'''Sample Game''' |
||
Line 1,183: | Line 12,652: | ||
In this version, The computer player will first complete a winning line of its own if it can, otherwise block a winning line of its opponent if they have two in a row, or then choose a random move. |
In this version, The computer player will first complete a winning line of its own if it can, otherwise block a winning line of its opponent if they have two in a row, or then choose a random move. |
||
< |
<syntaxhighlight lang="python"> |
||
''' |
''' |
||
Tic-tac-toe game player. |
Tic-tac-toe game player. |
||
Line 1,274: | Line 12,743: | ||
break |
break |
||
else: |
else: |
||
print('\nA draw')</ |
print('\nA draw')</syntaxhighlight> |
||
{{out}} |
|||
'''Sample output''' |
|||
<pre> |
<pre> |
||
Tic-tac-toe game player. |
Tic-tac-toe game player. |
||
Line 1,354: | Line 12,823: | ||
A draw</pre> |
A draw</pre> |
||
=={{header|R}}== |
|||
This program simulates a game of Tic-Tac-Toe inside of an interactive window. It includes three player modes, which are a two-player game, a human versus random AI, or human versus maximized AI. This implementation belongs to "X", and can also be found here. |
|||
<syntaxhighlight lang="r"> |
|||
rm(list=ls()) |
|||
library(RColorBrewer) |
|||
# Create tic.tac.toe function. |
|||
tic.tac.toe <- function(name="Name", mode=0, type=0){ |
|||
place.na <<- matrix(1:9, 3, 3) |
|||
value <<- matrix(-3, 3, 3) |
|||
k <<- 1 ; r <<- 0 |
|||
# Make game board. |
|||
image(1:3, 1:3, matrix(sample(9), 3, 3), asp=c(1, 1), |
|||
xaxt="n", yaxt="n", xlab="", ylab="", frame=F, col=brewer.pal(9, "Set3")) |
|||
segments(c(0.5,0.5,1.5,2.5), c(2.5,1.5,0.5,0.5), |
|||
c(3.5,3.5,1.5,2.5), c(2.5,1.5,3.5,3.5), lwd=8, col=gray(0.3)) |
|||
segments(c(0.5,0.5,0.5,3.5), c(0.52,3.47,0.5,0.5), |
|||
c(3.5,3.5,0.5,3.5), c(0.52,3.47,3.5,3.5), lwd=8, col=gray(0.3)) |
|||
# Allow player to choose between a human v. human, human v. random AI, or human vs. smart AI. |
|||
if(mode==0) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2), |
|||
"2P : Human v.s. Human", font.sub=2, cex.sub=2) |
|||
if(mode==1) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2), |
|||
"1P : Human v.s. AI (Easy)", font.sub=2, cex.sub=2) |
|||
if(mode==2) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2), |
|||
"1P : Human v.s. AI (Hard)", font.sub=2, cex.sub=2) |
|||
# Dole out symbols. |
|||
if(type==0){symbol <- "O" ; symbol.op <- "X"} |
|||
if(type==1){symbol <- "X" ; symbol.op <- "O"} |
|||
out <- list(name=name, mode=mode, type=type, symbol=symbol, symbol.op=symbol.op) |
|||
} |
|||
# Checks if the game has ended. |
|||
isGameOver <- function(){ |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==0 | total.2==0 | total.1==3 | total.2==3){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(total.1==0 | total.2==0 | total.3==0 | total.4==0 | total.1==3 | total.2==3 | total.3==3 | total.4==3){ |
|||
place.na[!is.na(place.na)] <<- NA |
|||
if(total.1==0 | total.2==0 | total.3==0 | total.4==0){ |
|||
title(sub=list("You Won ?! That's a first!", col="red", font=2, cex=2.5), line=2) |
|||
}else{ |
|||
title(sub=list("You Don't Get Tired of Losing ?!", col="darkblue", font=2, cex=2.5), line=2) |
|||
} |
|||
} |
|||
if(all(is.na(place.na))){ |
|||
if(total.1==0 | total.2==0 | total.3==0 | total.4==0 | total.1==3 | total.2==3 | total.3==3 | total.4==3){ |
|||
if(total.1==0 | total.2==0 | total.3==0 | total.4==0){ |
|||
title(sub=list("You Won ! Pigs Must Be Flying!", col="orange", font=2, cex=2.5), line=2) |
|||
}else{ |
|||
title(sub=list("You Lost ... Once Again !", col="darkblue", font=2, cex=2.5), line=2) |
|||
} |
|||
}else{ |
|||
title(sub=list("A measly tie! Try Again", col="blue", font=2, cex=2.5), line=2) |
|||
} |
|||
} |
|||
} |
|||
# AI attack function |
|||
attack <- function(){ |
|||
### Identify rows and columns |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==-1 | total.2==-1){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(total.1==-1){ |
|||
text(i, which(value[i,]!=1), symbol.op, cex=6, font=2) |
|||
place.na[i, which(value[i,]!=1)] <<- NA |
|||
value[i, which(value[i,]!=1)] <<- 1 |
|||
}else if(total.2==-1){ |
|||
text(which(value[,i]!=1), i, symbol.op, cex=6, font=2) |
|||
place.na[which(value[,i]!=1), i] <<- NA |
|||
value[which(value[,i]!=1), i] <<- 1 |
|||
}else if(total.3==-1){ |
|||
r.1 <- which(c(value[1, 1], value[2, 2], value[3, 3])!=1) |
|||
text(r.1, r.1, symbol.op, cex=6, font=2) |
|||
place.na[r.1, r.1] <<- NA |
|||
value[r.1, r.1] <<- 1 |
|||
}else if(total.4==-1){ |
|||
r.2 <- which(c(value[1, 3], value[2, 2], value[3, 1])!=1) |
|||
text(r.2, -r.2+4, symbol.op, cex=6, font=2) |
|||
place.na[r.2, -r.2+4] <<- NA |
|||
value[r.2, -r.2+4] <<- 1 |
|||
} |
|||
} |
|||
# AI defense function |
|||
defend <- function(){ |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==-3 | total.2==-3){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(total.1==-3){ |
|||
text(i, which(value[i,]!=0), symbol.op, cex=6, font=2) |
|||
place.na[i, which(value[i,]!=0)] <<- NA |
|||
value[i, which(value[i,]!=0)] <<- 1 |
|||
}else if(total.2==-3){ |
|||
text(which(value[,i]!=0), i, symbol.op, cex=6, font=2) |
|||
place.na[which(value[,i]!=0), i] <<- NA |
|||
value[which(value[,i]!=0), i] <<- 1 |
|||
}else if(total.3==-3){ |
|||
r.1 <- which(c(value[1, 1], value[2, 2], value[3, 3])!=0) |
|||
text(r.1, r.1, symbol.op, cex=6, font=2) |
|||
place.na[r.1, r.1] <<- NA |
|||
value[r.1, r.1] <<- 1 |
|||
}else if(total.4==-3){ |
|||
r.2 <- which(c(value[1, 3], value[2, 2], value[3, 1])!=0) |
|||
text(r.2, -r.2+4, symbol.op, cex=6, font=2) |
|||
place.na[r.2, -r.2+4] <<- NA |
|||
value[r.2, -r.2+4] <<- 1 |
|||
}else{ |
|||
rn <- sample(place.na[!is.na(place.na)], 1) |
|||
text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2) |
|||
place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA |
|||
value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1 |
|||
} |
|||
} |
|||
# Allow aim in program. |
|||
aim <- function(x, y, tic.tac.toe=ttt){ |
|||
mode <- tic.tac.toe$mode |
|||
symbol <<- tic.tac.toe$symbol |
|||
symbol.op <<- tic.tac.toe$symbol.op |
|||
x <<- x ; y <<- y |
|||
# Mode 0, Two Players |
|||
if(mode==0){ |
|||
turn <- rep(c(0, 1), length.out=9) |
|||
if(is.na(place.na[x, y])){ |
|||
cat("This square is taken !") |
|||
}else{ |
|||
if(turn[k]==0){ |
|||
text(x, y, symbol, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 0 |
|||
} |
|||
if(turn[k]==1){ |
|||
text(x, y, symbol.op, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 1 |
|||
} |
|||
k <<- k + 1 |
|||
} |
|||
} |
|||
# Mode 1, Random AI |
|||
if(mode==1){ |
|||
if(is.na(place.na[x, y])){ |
|||
cat("This square had been chosen !") |
|||
}else{ |
|||
text(x, y, symbol, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 0 |
|||
isGameOver() |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==-1 | total.2==-1){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(all(is.na(place.na))){ |
|||
isGameOver() |
|||
}else if(total.1==-1 | total.2==-1 | total.3==-1 | total.4==-1){ |
|||
attack() |
|||
}else{ |
|||
defend() |
|||
} |
|||
} |
|||
} |
|||
# Mode 2, Hard AI |
|||
if(mode==2){ |
|||
if(is.na(place.na[x, y])){ |
|||
cat("This square is taken!") |
|||
}else{ |
|||
# AI First Turn |
|||
if(sum(is.na(place.na))==0){ |
|||
text(x, y, symbol, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 0 |
|||
if(is.na(place.na[2, 2])==F){ |
|||
text(2, 2, symbol.op, cex=6, font=2) |
|||
place.na[2, 2] <<- NA |
|||
value[2, 2] <<- 1 |
|||
}else{ |
|||
corner.1 <- sample(c(1, 3), 1) ; corner.2 <- sample(c(1, 3), 1) |
|||
text(corner.1, corner.2, symbol.op, cex=6, font=2) |
|||
place.na[corner.1, corner.2] <<- NA |
|||
value[corner.1, corner.2] <<- 1 |
|||
} |
|||
# AI Second Turn |
|||
}else if(sum(is.na(place.na))==2){ |
|||
text(x, y, symbol, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 0 |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==-3 | total.2==-3){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(total.1==-3 | total.2==-3 | total.3==-3 | total.4==-3){ |
|||
defend() |
|||
}else{ |
|||
total.1 <- value[2, 1] + value[2, 2] + value[2, 3] |
|||
total.2 <- value[1, 2] + value[2, 2] + value[3, 2] |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(total.1==1 | total.2==1 | total.3==1 | total.4==1){ |
|||
if((value[2, 2]==1 & total.3==1) | (value[2, 2]==1 & total.4==1)){ |
|||
vector.side <- c(place.na[2, 1], place.na[1, 2], place.na[3, 2], place.na[2, 3]) |
|||
rn <- sample(vector.side[!is.na(vector.side)], 1) |
|||
text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2) |
|||
place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA |
|||
value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1 |
|||
}else{ |
|||
matrix.cor <- place.na[c(1, 3), c(1, 3)] |
|||
rn <- sample(matrix.cor[!is.na(matrix.cor)], 1) |
|||
text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2) |
|||
place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA |
|||
value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1 |
|||
} |
|||
}else{ |
|||
if((x==1 & y==2) | (x==3 & y==2)){ |
|||
rn <- sample(c(1, 3), 1) |
|||
text(x, rn, symbol.op, cex=6, font=2) |
|||
place.na[x, rn] <<- NA |
|||
value[x, rn] <<- 1 |
|||
}else if((x==2 & y==3) | (x==2 & y==1)){ |
|||
rn <- sample(c(1, 3), 1) |
|||
text(rn, y, symbol.op, cex=6, font=2) |
|||
place.na[rn, y] <<- NA |
|||
value[rn, y] <<- 1 |
|||
}else if((x==1 & y==1) | (x==1 & y==3) | (x==3 & y==1) | (x==3 & y==3)){ |
|||
text(-x+4, -y+4, symbol.op, cex=6, font=2) |
|||
place.na[-x+4, -y+4] <<- NA |
|||
value[-x+4, -y+4] <<- 1 |
|||
} |
|||
} |
|||
} |
|||
# AI Other Turn |
|||
}else{ |
|||
text(x, y, symbol, cex=6, font=2) |
|||
place.na[x, y] <<- NA |
|||
value[x, y] <<- 0 |
|||
isGameOver() |
|||
for(i in 1:3){ |
|||
total.1 <- 0 ; total.2 <- 0 |
|||
for(j in 1:3){ |
|||
total.1 <- total.1 + value[i, j] |
|||
total.2 <- total.2 + value[j, i] |
|||
} |
|||
if(total.1==-1 | total.2==-1){ |
|||
break |
|||
} |
|||
} |
|||
total.3 <- value[1, 1] + value[2, 2] + value[3, 3] |
|||
total.4 <- value[1, 3] + value[2, 2] + value[3, 1] |
|||
if(all(is.na(place.na))){ |
|||
isGameOver() |
|||
}else if(total.1==-1 | total.2==-1 | total.3==-1 | total.4==-1){ |
|||
attack() |
|||
}else{ |
|||
defend() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
isGameOver() |
|||
} |
|||
# Allow users to click on program. |
|||
click <- function(tic.tac.toe=ttt){ |
|||
name <- tic.tac.toe$name |
|||
mode <- tic.tac.toe$mode |
|||
type <- tic.tac.toe$type |
|||
while(length(place.na)==9){ |
|||
mouse.at <- locator(n = 1, type = "n") |
|||
#cat(mouse.at$x,"\t", mouse.at$y, "\n") |
|||
x.at <- round(mouse.at$x) |
|||
y.at <- round(mouse.at$y) |
|||
#cat(x.at,"\t", y.at, "\n") |
|||
if(all(is.na(place.na))){ |
|||
ttt <<- tic.tac.toe(name, mode, type) |
|||
}else if(x.at > 3.5 | x.at < 0.5 | y.at > 3.5 | y.at < 0.5){ |
|||
r <<- r + 1 |
|||
title(sub=list("Click outside:Quit / inside:Restart", col="deeppink", font=2, cex=2), line=2) |
|||
if(r==2){ |
|||
dev.off() |
|||
break |
|||
} |
|||
}else{ |
|||
if(r==1){ |
|||
ttt <<- tic.tac.toe(name, mode, type) |
|||
}else{ |
|||
aim(x.at, y.at) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
# Play the game |
|||
start <- function(name="Name", mode=0, type=0){ |
|||
x11() |
|||
ttt <<- tic.tac.toe(name, mode, type) |
|||
click() |
|||
} |
|||
#start("name", "mode" = 0 - 2, type = 0,1) |
|||
</syntaxhighlight> |
|||
=={{header|Racket}}== |
|||
The program provides standard interface for implementation of any zero–sum game with perfect information such as tick-tack-toe, Nim, the 21 game etc. It is possible to create interactive players (as objects) with different playing strategy (AI-driven, user-driven, random etc.) and let them play with each other through message-sending technique. |
|||
The optimal strategy is implemented via lazy minimax algorythm with α-β-pruning and arbitrary depth of the recursion. |
|||
The program consists of separate modules: |
|||
+ minimax.rkt -- Written in Lazy Racket, implements the general minimax algorythm as |
|||
| given in [http://en.wikipedia.org/wiki/Alpha-beta_pruning Wikipedia]. |
|||
| Knows nothing about games. |
|||
V |
|||
+ game.rkt -- Written in Lazy Racket, defines general classes for the game and players. |
|||
| Knows nothing about tick-tack-toe, only about zero-sum two-player |
|||
| turn-taking games with perfect information in general. |
|||
V |
|||
+ tick-tack.rkt -- Written in Racket, implements the tick-tack-toe game. |
|||
The <tt>minimax.rkt</tt> module: |
|||
<syntaxhighlight lang="racket"> |
|||
#lang lazy |
|||
(provide minimax) |
|||
(define (minimax tree) |
|||
(! (let minimax ([node tree] [α -inf.0] [β +inf.0] [max-player #f]) |
|||
(cond |
|||
[(number? node) node] |
|||
[(empty? node) 0.0] |
|||
[max-player |
|||
(let next ([x node] [α α]) |
|||
(if (or (empty? x) (<= β α)) |
|||
α |
|||
(next (cdr x) |
|||
(max α (minimax (car x) α β (not max-player))))))] |
|||
[else |
|||
(let next ([x node] [β β]) |
|||
(if (or (empty? x) (<= β α)) |
|||
β |
|||
(next (cdr x) |
|||
(min β (minimax (car x) α β (not max-player))))))])))) |
|||
</syntaxhighlight> |
|||
The <tt>game.rkt</tt> module: |
|||
<syntaxhighlight lang="racket"> |
|||
#lang lazy |
|||
(require racket/class |
|||
"minimax.rkt" |
|||
(only-in racket/list shuffle argmax)) |
|||
(provide game% |
|||
interactive-player |
|||
define-partners) |
|||
;;-------------------------------------------------------------------- |
|||
;; Class representing the logics and optimal strategy |
|||
;; for a zero-sum game with perfect information. |
|||
(define game% |
|||
(class object% |
|||
(super-new) |
|||
;; virtual methods which set up the game rules |
|||
(init-field my-win? ; State -> Bool |
|||
my-loss? ; State -> Bool |
|||
draw-game? ; State -> Bool |
|||
my-move ; State Move -> State |
|||
opponent-move ; State Move -> State |
|||
possible-moves ; State -> (list Move) |
|||
show-state) ; State -> Any |
|||
;; optimal-move :: State -> Move |
|||
;; Choses the optimal move. |
|||
;; If several equivalent moves exist -- choses one randomly. |
|||
(define/public ((optimal-move look-ahead) S) |
|||
(! (argmax (λ (m) (! (minimax (game-tree S m look-ahead)))) |
|||
(shuffle (possible-moves S))))) |
|||
;; game-tree :: State -> (Move -> (Treeof Real)) |
|||
(define (game-tree S m look-ahead) |
|||
(let new-ply ([moves (cycle opponent-move my-move)] |
|||
[i 1] |
|||
[s (my-move S m)]) |
|||
(cond |
|||
[(my-win? s) (/ 1 i)] ; more close wins and loses |
|||
[(my-loss? s) (/ -1 i)] ; have bigger weights |
|||
[(draw-game? s) 0] |
|||
[(>= i look-ahead) (/ 1 i)] |
|||
[else (map (λ (x) (new-ply (cdr moves) (+ 1 i) ((car moves) s x))) |
|||
(possible-moves s))]))) |
|||
;; make-move :: State (State -> Move) -> (Move State Symbol) |
|||
(define/public (make-move S move) |
|||
(cond |
|||
[(my-loss? S) (values '() S 'loss)] |
|||
[(draw-game? S) (values '() S 'draw)] |
|||
[else (let* ([m* (! (move S))] |
|||
[S* (my-move S m*)]) |
|||
(cond |
|||
[(my-win? S*) (values m* S* 'win)] |
|||
[(draw-game? S*) (values m* S* 'draw)] |
|||
[else (values m* S* 'next)]))])))) |
|||
;;-------------------------------------------------------------------- |
|||
;; Mixin representing an interactive game player. |
|||
;; The parameter `game` defines a game which is played. |
|||
(define (interactive-player game) |
|||
(class game |
|||
(super-new) |
|||
(inherit-field show-state) |
|||
(inherit make-move optimal-move) |
|||
(init-field name |
|||
[look-ahead 4] |
|||
[opponent 'undefined] |
|||
[move-method (optimal-move look-ahead)]) |
|||
(define/public (your-turn S) |
|||
(define-values (m S* status) (make-move S move-method)) |
|||
(! (printf "\n~a makes move ~a\n" name m)) |
|||
(! (show-state S*)) |
|||
(! (case status |
|||
['stop (displayln "The game was interrupted.")] |
|||
['win (printf "~a wins!" name)] |
|||
['loss (printf "~a wins!" name)] |
|||
['draw (printf "Draw!")] |
|||
[else (send opponent your-turn S*)]))))) |
|||
;;-------------------------------------------------------------------- |
|||
;; a simple macro for initialization of game partners |
|||
(define-syntax-rule |
|||
(define-partners game (A #:win A-wins #:move A-move) |
|||
(B #:win B-wins #:move B-move)) |
|||
(begin |
|||
(define A (class game |
|||
(super-new |
|||
[my-win? A-wins] |
|||
[my-loss? B-wins] |
|||
[my-move A-move] |
|||
[opponent-move B-move]))) |
|||
(define B (class game |
|||
(super-new |
|||
[my-win? B-wins] |
|||
[my-loss? A-wins] |
|||
[my-move B-move] |
|||
[opponent-move A-move]))))) |
|||
;;-------------------------------------------------------------------- |
|||
;; the main procedure which initiates the game |
|||
(define (start-game p1 p2 initial-state) |
|||
(set-field! opponent p1 p2) |
|||
(set-field! opponent p2 p1) |
|||
(send p1 your-turn initial-state)) |
|||
</syntaxhighlight> |
|||
The <tt>tick-tack.rkt</tt> module: |
|||
<syntaxhighlight lang="racket">#lang racket |
|||
(require "game.rkt" |
|||
racket/set |
|||
lazy/force) |
|||
;;-------------------------------------------------------------------- |
|||
;; Tick-tack-toe game implementation |
|||
;; the structure representing a board |
|||
(struct board (x o)) |
|||
;; sets of X's and O's |
|||
(define xs board-x) |
|||
(define os board-o) |
|||
(define empty-board (board (set) (set))) |
|||
(define all-cells |
|||
(set '(1 1) '(1 2) '(1 3) |
|||
'(2 1) '(2 2) '(2 3) |
|||
'(3 1) '(3 2) '(3 3))) |
|||
(define (free-cells b) |
|||
(set-subtract all-cells (xs b) (os b))) |
|||
(define winning-positions |
|||
(list (set '(1 1) '(2 2) '(3 3)) |
|||
(set '(1 3) '(2 2) '(3 1)) |
|||
(set '(1 1) '(1 2) '(1 3)) |
|||
(set '(2 1) '(2 2) '(2 3)) |
|||
(set '(3 1) '(3 2) '(3 3)) |
|||
(set '(1 1) '(2 1) '(3 1)) |
|||
(set '(1 2) '(2 2) '(3 2)) |
|||
(set '(1 3) '(2 3) '(3 3)))) |
|||
;; a predicate for winning state on the board |
|||
(define ((wins? s) b) |
|||
(ormap (curryr subset? (s b)) winning-positions)) |
|||
;; player moves |
|||
(define (x-move b m) (board (set-add (xs b) m) (os b))) |
|||
(define (o-move b m) (board (xs b) (set-add (os b) m))) |
|||
;; textual representation of the board |
|||
(define (show-board b) |
|||
(for ([i '(3 2 1)]) |
|||
(printf "~a " i) |
|||
(for ([j '(1 2 3)]) |
|||
(display (cond |
|||
[(set-member? (os b) (list j i)) "|o"] |
|||
[(set-member? (xs b) (list j i)) "|x"] |
|||
[else "| "]))) |
|||
(display "|\n")) |
|||
(display " 1 2 3 ")) |
|||
;;-------------------------------------------------------------------- |
|||
;; The definition of the game |
|||
;; general properties |
|||
(define tic-tac% |
|||
(class game% |
|||
(super-new |
|||
[draw-game? (compose set-empty? free-cells)] |
|||
[possible-moves (compose set->list free-cells)] |
|||
[show-state show-board]))) |
|||
;; players |
|||
(define-partners tic-tac% |
|||
(x% #:win (wins? xs) #:move x-move) |
|||
(o% #:win (wins? os) #:move o-move)) |
|||
;; Computer players |
|||
(define player-A (new (interactive-player x%) [name "A"] [look-ahead 6])) |
|||
(define player-B (new (interactive-player o%) [name "B"] [look-ahead 6])) |
|||
; The interactive user |
|||
(define User |
|||
(new (interactive-player x%) |
|||
[name "User"] |
|||
[move-method |
|||
(λ (b) (let make-move ([m (read)]) |
|||
(match m |
|||
['q (exit)] |
|||
[(list (or 1 2 3) (or 1 2 3)) m] |
|||
[else (make-move (read))])))])) |
|||
;; The dummy player plays randomly |
|||
(define Dummy |
|||
(new (interactive-player o%) [name "Dummy"] [look-ahead 0])) |
|||
</syntaxhighlight> |
|||
Sample games: |
|||
Computer plays with the computer: |
|||
<pre> |
|||
> (!(start-game player-A player-B empty-board)) |
|||
A makes move (3 1) |
|||
3 | | | | |
|||
2 | | | | |
|||
1 | | |x| |
|||
1 2 3 |
|||
B makes move (2 2) |
|||
3 | | | | |
|||
2 | |o| | |
|||
1 | | |x| |
|||
1 2 3 |
|||
A makes move (1 1) |
|||
3 | | | | |
|||
2 | |o| | |
|||
1 |x| |x| |
|||
1 2 3 |
|||
B makes move (2 1) |
|||
3 | | | | |
|||
2 | |o| | |
|||
1 |x|o|x| |
|||
1 2 3 |
|||
A makes move (2 3) |
|||
3 | |x| | |
|||
2 | |o| | |
|||
1 |x|o|x| |
|||
1 2 3 |
|||
B makes move (3 2) |
|||
3 | |x| | |
|||
2 | |o|o| |
|||
1 |x|o|x| |
|||
1 2 3 |
|||
A makes move (1 2) |
|||
3 | |x| | |
|||
2 |x|o|o| |
|||
1 |x|o|x| |
|||
1 2 3 |
|||
B makes move (1 3) |
|||
3 |o|x| | |
|||
2 |x|o|o| |
|||
1 |x|o|x| |
|||
1 2 3 |
|||
A makes move (3 3) |
|||
3 |o|x|x| |
|||
2 |x|o|o| |
|||
1 |x|o|x| |
|||
1 2 3 Draw! |
|||
</pre> |
|||
Computer plays with the dummy: |
|||
<pre> |
|||
> (!(start-game player-A Dummy empty-board)) |
|||
A makes move (3 1) |
|||
3 | | | | |
|||
2 | | | | |
|||
1 | | |x| |
|||
1 2 3 |
|||
Dummy makes move (2 3) |
|||
3 | |o| | |
|||
2 | | | | |
|||
1 | | |x| |
|||
1 2 3 |
|||
A makes move (1 1) |
|||
3 | |o| | |
|||
2 | | | | |
|||
1 |x| |x| |
|||
1 2 3 |
|||
Dummy makes move (3 3) |
|||
3 | |o|o| |
|||
2 | | | | |
|||
1 |x| |x| |
|||
1 2 3 |
|||
A makes move (2 1) |
|||
3 | |o|o| |
|||
2 | | | | |
|||
1 |x|x|x| |
|||
1 2 3 A wins! |
|||
</pre> |
|||
User plays with the dummy: |
|||
<pre> |
|||
> (!(start-game Dummy User empty-board)) |
|||
Dummy makes move (2 3) |
|||
3 | |o| | |
|||
2 | | | | |
|||
1 | | | | |
|||
1 2 3 (1 2) |
|||
User makes move (1 2) |
|||
3 | |o| | |
|||
2 |x| | | |
|||
1 | | | | |
|||
1 2 3 |
|||
Dummy makes move (3 2) |
|||
3 | |o| | |
|||
2 |x| |o| |
|||
1 | | | | |
|||
1 2 3 (1 3) |
|||
User makes move (1 3) |
|||
3 |x|o| | |
|||
2 |x| |o| |
|||
1 | | | | |
|||
1 2 3 |
|||
Dummy makes move (3 3) |
|||
3 |x|o|o| |
|||
2 |x| |o| |
|||
1 | | | | |
|||
1 2 3 (1 1) |
|||
User makes move (1 1) |
|||
3 |x|o|o| |
|||
2 |x| |o| |
|||
1 |x| | | |
|||
1 2 3 User wins! |
|||
</pre> |
|||
As an example of another zero-sum game consider the classical [http://en.wikipedia.org/wiki/Nim Nim] game: |
|||
<syntaxhighlight lang="racket"> |
|||
#lang racket |
|||
(require "game.rkt" |
|||
lazy/force) |
|||
;;-------------------------------------------------------------------- |
|||
;; The definition of the game |
|||
(define initial-state '(3 5 7)) |
|||
(define (move s m) (map - s m)) |
|||
(define (win? s) (= 1 (apply + s))) |
|||
(define (show-state s) (displayln (map (λ (n) (make-list n '●)) s))) |
|||
(define (possible-moves S) |
|||
(append-map |
|||
(λ (heap n) |
|||
(map (λ (x) (map (curry * x) heap)) |
|||
(range 1 (+ 1 (min 3 n))))) |
|||
'((1 0 0) (0 1 0) (0 0 1)) S)) |
|||
(define Nim% (class game% |
|||
(super-new |
|||
[draw-game? (const #f)] |
|||
[possible-moves possible-moves] |
|||
[show-state show-state]))) |
|||
(define-partners Nim% |
|||
(first% #:win win? #:move move) |
|||
(second% #:win win? #:move move)) |
|||
;; players |
|||
(define player-A |
|||
(new (interactive-player first%) [name "A"] [look-ahead 4])) |
|||
(define player-B |
|||
(new (interactive-player second%) [name "B"] [look-ahead 4])) |
|||
</syntaxhighlight> |
|||
Computer plays with the computer: |
|||
<pre> |
|||
> (!(start-game player-A player-B initial-state)) |
|||
A makes move (0 0 2) |
|||
((● ● ●) (● ● ● ● ●) (● ● ● ● ●)) |
|||
B makes move (1 0 0) |
|||
((● ●) (● ● ● ● ●) (● ● ● ● ●)) |
|||
A makes move (2 0 0) |
|||
(() (● ● ● ● ●) (● ● ● ● ●)) |
|||
B makes move (0 2 0) |
|||
(() (● ● ●) (● ● ● ● ●)) |
|||
A makes move (0 3 0) |
|||
(() () (● ● ● ● ●)) |
|||
B makes move (0 0 1) |
|||
(() () (● ● ● ●)) |
|||
A makes move (0 0 3) |
|||
(() () (●)) |
|||
A wins! |
|||
</pre> |
|||
With use of memoization it is easy to train automatic players so that they would never lose and play very fast. |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2018.03}} |
|||
The computer plays a random game. |
|||
<syntaxhighlight lang="raku" line>my @board = 1..9; |
|||
my @winning-positions = [0..2], [3..5], [6..8], [0,3,6], [1,4,7], [2,5,8], |
|||
[0,4,8], [6,4,2]; |
|||
sub get-winner() { |
|||
for @winning-positions { |
|||
return (@board[|$_][0], $_) if [eq] @board[|$_]; |
|||
} |
|||
} |
|||
sub free-indexes() { |
|||
@board.keys.grep: { @board[$_] eq any(1..9) } |
|||
} |
|||
sub ai-move() { |
|||
given free-indexes.pick { |
|||
@board[$_] = 'o'; |
|||
say "I go at: { $_ + 1 }\n"; |
|||
} |
|||
} |
|||
sub print-board() { |
|||
print "\e[2J"; |
|||
say @board.map({ "$^a | $^b | $^c" }).join("\n--+---+--\n"), "\n"; |
|||
} |
|||
sub human-move() { |
|||
my $pos = prompt "Choose one of { (free-indexes() »+» 1).join(",") }: "; |
|||
if $pos eq any(free-indexes() »+» 1) { |
|||
@board[$pos - 1] = 'x'; |
|||
} else { |
|||
say "Sorry, you want to put your 'x' where?"; |
|||
human-move(); |
|||
} |
|||
} |
|||
for flat (&ai-move, &human-move) xx * { |
|||
print-board; |
|||
last if get-winner() or not free-indexes; |
|||
.(); |
|||
} |
|||
if get-winner() -> ($player, $across) { |
|||
say "$player wins across [", ($across »+» 1).join(", "), "]."; |
|||
} else { |
|||
say "How boring, a draw!"; |
|||
}</syntaxhighlight> |
|||
=={{header|REXX}}== |
|||
This REXX program uses an analytical solution instead of hard─fast choices that can be |
|||
assumed for a 3×3 game board. |
|||
Options used within the REXX program: |
|||
::* a separate numbered grid is used instead of coördinates for easier marker specification |
|||
::* the game board is separated from the numbered grid, this makes it much easier to see the playing field |
|||
::* straight lines (wins) are handled dynamically instead of hard─coding them |
|||
::* allows the human player to: |
|||
::::* specify any '''N'''×'''N''' size tic─tac─toe board (a square grid) |
|||
::::* specify who plays first (default is the human) |
|||
::::* specify what markers (symbols) to be used for both players (can use hexadecimal pairs) |
|||
::::* quit (exit) the game at any time |
|||
::::* win (if the human goes first ''and'' makes a certain move) |
|||
<br>A fair amount of code was dedicated to error detection and the displaying of error messages, and |
|||
<br>also the presentation of the tic─tac─toe game boards (grids). |
|||
<syntaxhighlight lang="rexx">/*REXX program plays (with a human) the tic─tac─toe game on an NxN grid. */ |
|||
$= copies('─', 9) /*eyecatcher for error messages, prompt*/ |
|||
oops = $ '***error*** ' /*literal for when an error happens. */ |
|||
single = '│─┼'; jam= "║"; bar= '═'; junc= "╬"; dbl=jam || bar || junc |
|||
sw = linesize() - 1 /*obtain width of the terminal (less 1)*/ |
|||
parse arg N hm cm .,@. /*obtain optional arguments from the CL*/ |
|||
if N=='' | N=="," then N=3; oN=N /*N not specified? Then use default.*/ |
|||
N = abs(N) /*if N < 0. then computer goes first. */ |
|||
NN = N*N /*calculate the square of N. */ |
|||
middle = NN % 2 + N % 2 /* " " middle " the grid. */ |
|||
if N<2 then do; say oops 'tic─tac─toe grid is too small: ' N; exit 13; end |
|||
pad= left('', sw % NN) /*display padding: 6x6 in 80 columns.*/ |
|||
if hm=='' then hm= "X"; /*define the marker for a human. */ |
|||
if cm=='' then cm= "O" /* " " " " the computer. */ |
|||
hm= aChar(hm, 'human') /*determine if the marker is legitimate*/ |
|||
cm= aChar(cm, 'computer') /* " " " " " " */ |
|||
parse upper value hm cm with uh uc /*use uppercase values is markers: X x*/ |
|||
if uh==uc then cm= word('O X', 1 + (uh=="O") ) /*The human wants Hal's marker? Swap. */ |
|||
if oN<0 then call Hmove middle /*Hal moves first? Then choose middling*/ |
|||
else call showGrid /*showGrid also checks for wins & draws*/ |
|||
/*tic─tac─toe game───►*/ do forever /*'til the cows come home (or QUIT). */ |
|||
/*tic─tac─toe game───►*/ call CBLF /*process carbon─based lifeform's move.*/ |
|||
/*tic─tac─toe game───►*/ call Hal /*determine Hal's (the computer) move.*/ |
|||
/*tic─tac─toe game───►*/ end /*forever*/ /*showGrid subroutine does wins & draws*/ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
aChar: parse arg x,whoseX; L=length(x) /*process markers.*/ |
|||
if L==1 then return testB( x ) /*1 char, as is. */ |
|||
if L==2 & datatype(x, 'X') then return testB( x2c(x) ) /*2 chars, hex. */ |
|||
if L==3 & datatype(x, 'W') & , /*3 chars, decimal*/ |
|||
x>=0 & x<256 then return testB( d2c(x) ) /*···and in range.*/ |
|||
say oops 'illegal character or character code for' whoseX "marker: " x |
|||
exit 13 /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
CBLF: prompt='Please enter a cell number to place your next marker ['hm"] (or Quit):" |
|||
do forever; say $ prompt |
|||
parse pull x 1 ux 1 ox; upper ux /*get versions of answer; uppercase ux*/ |
|||
if datatype(ox, 'W') then ox=ox / 1 /*normalize cell number: +0007 ───► 7 */ |
|||
/*(division by unity normalizes a num.)*/ |
|||
select /*perform some validations of X (cell#)*/ |
|||
when abbrev('QUIT',ux,1) then call tell 'quitting.' |
|||
when x='' then iterate /*Nada? Try again.*/ |
|||
when words(x)\==1 then say oops "too many" cell# 'specified:' x |
|||
when \datatype(x, 'N') then say oops "cell number isn't numeric: " x |
|||
when \datatype(x, 'W') then say oops "cell number isn't an integer: " x |
|||
when x=0 then say oops "cell number can't be zero: " x |
|||
when x<0 then say oops "cell number can't be negative: " x |
|||
when x>NN then say oops "cell number can't exceed " NN |
|||
when @.ox\=='' then say oops "cell number is already occupied: " x |
|||
otherwise leave /*forever*/ |
|||
end /*select*/ |
|||
end /*forever*/ |
|||
/* [↓] OX is a normalized version of X*/ |
|||
@.ox= hm /*place a marker for the human (CLBF). */ |
|||
call showGrid /*and display the tic─tac─toe grid. */ |
|||
return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Hal: select /*Hal tries various moves. */ |
|||
when win(cm, N-1) then call Hmove , ec /*is this the winning move?*/ |
|||
when win(hm, N-1) then call Hmove , ec /* " " a blocking " */ |
|||
when @.middle== '' then call Hmove middle /*pick the center cell. */ |
|||
when @.N.N == '' then call Hmove , N N /*bottom right corner cell.*/ |
|||
when @.N.1 == '' then call Hmove , N 1 /* " left " " */ |
|||
when @.1.N == '' then call Hmove , 1 N /* top right " " */ |
|||
when @.1.1 == '' then call Hmove , 1 1 /* " left " " */ |
|||
otherwise call Hmove , ac /*pick a blank cell in grid*/ |
|||
end /*select*/ |
|||
return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Hmove: parse arg Hplace,dr dc; if Hplace=='' then Hplace = (dr - 1)*N + dc |
|||
@.Hplace= cm /*place computer's marker. */ |
|||
say; say $ 'computer places a marker ['cm"] at cell number " Hplace |
|||
call showGrid |
|||
return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
showGrid: _= 0; cW= 5; cH= 3; open= 0 /*cell width, cell height.*/ |
|||
do r=1 for N /*construct array of cells.*/ |
|||
do c=1 for N; _= _ + 1; @.r.c= @._; open= open | @._=='' |
|||
end /*c*/ |
|||
end /*r*/ /* [↑] OPEN≡a cell is open*/ |
|||
say /* [↑] create grid coörds.*/ |
|||
z= 0; do j=1 for N /* [↓] show grids&markers.*/ |
|||
do t=1 for cH; _=; __= /*MK is a marker in a cell.*/ |
|||
do k=1 for N; if t==2 then z= z + 1; mk=; c#= |
|||
if t==2 then do; mk= @.z; c#= z /*c# is cell number*/ |
|||
end |
|||
_= _ || jam || center(mk, cW) |
|||
__= __ || jam || center(c#, cW) |
|||
end /*k*/ |
|||
say pad substr(_, 2) pad translate( substr(__, 2), single, dbl) |
|||
end /*t*/ /* [↑] show a line*/ |
|||
if j==N then leave |
|||
_= |
|||
do b=1 for N; _= _ || junc || copies(bar, cW) |
|||
end /*b*/ /* [↑] a grid part*/ |
|||
say pad substr(_, 2) pad translate( substr(_, 2), single, dbl) |
|||
end /*j*/ |
|||
say |
|||
if win(hm) then call tell 'You ('hm") won"copies('!',random(1, 5) ) |
|||
if win(cm) then call tell 'The computer ('cm") won." |
|||
if \open then call tell 'This tic─tac─toe game is a draw (a cat scratch).' |
|||
return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
tell: do 4; say; end; say center(' 'arg(1)" ", sw, '─'); do 5; say; end; exit |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
testB: parse arg bx; if bx\==' ' then return bx /*test if the marker isn't a blank.*/ |
|||
say oops 'character code for' whoseX "marker can't be a blank." |
|||
exit 13 /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
win: parse arg wm,w; if w=='' then w= N /* [↓] see if there is a win. */ |
|||
ac= /* [↓] EC ≡ means Empty Cell. */ |
|||
do r=1 for N; _= 0; ec= /*see if any rows are a winner*/ |
|||
do c=1 for N; _= _ + (@.r.c==wm) /*count the # of markers in col*/ |
|||
if @.r.c=='' then ec= r c /*Cell empty? Then remember it*/ |
|||
end /*c*/ /* [↓] AC≡means available cell*/ |
|||
if ec\=='' then ac=ec /*Found an empty? Then use it.*/ |
|||
if _==N | (_>=w & ec\=='') then return 1==1 /*a winner has been determined.*/ |
|||
end /*r*/ /*w=N-1? Checking for near win*/ |
|||
do c=1 for N; _= 0; ec= /*see if any cols are a winner*/ |
|||
do r=1 for N; _= _ + (@.r.c==wm) /*count the # of markers in row*/ |
|||
if @.r.c=='' then ec= r c /*Cell empty? Then remember it*/ |
|||
end /*r*/ |
|||
if ec\=='' then ac= ec /*Found an empty? Then remember*/ |
|||
if _==N | (_>=w & ec\=='') then return 1==1 /*a winner has been determined.*/ |
|||
end /*c*/ |
|||
_= 0; ec= /*EC≡location of an empty cell.*/ |
|||
do d=1 for N; _= _ + (@.d.d==wm) /*A winning descending diag. ? */ |
|||
if @.d.d=='' then ec= d d /*Empty cell? Then note cell #*/ |
|||
end /*d*/ |
|||
if _==N | (_>=w & ec\=='') then return 1==1 /*a winner has been determined.*/ |
|||
_= 0; r= 1 |
|||
do c=N for N by -1; _=_ + (@.r.c==wm) /*A winning ascending diagonal?*/ |
|||
if @.r.c=='' then ec= r c /*Empty cell? Then note cell #*/ |
|||
r= r + 1 /*bump the counter for the rows*/ |
|||
end /*c*/ |
|||
if _==N | (_>=w & ec\=='') then return 1==1 /*a winner has been determined.*/ |
|||
return 0==1 /*no winner " " " */</syntaxhighlight> |
|||
This REXX program makes use of '''LINESIZE''' REXX program (or BIF) which is used to determine the screen width (or linesize) of the terminal (console); this is used to determine the amount of padding for a centered display of the two grids. |
|||
The '''LINESIZE.REX''' REXX program is included here ──► [[LINESIZE.REX]]. <br><br> |
|||
{{out|output|text= when using the input of: <tt> -3 </tt>}} |
|||
(a negative '''3''' indicates a grid of '''3<small>x</small>3''' and that the computer should play first.) |
|||
<br><br>Note: the user input is shown along with the program output. |
|||
<pre style="height:153ex"> |
|||
───────── computer places a marker [O] at cell number 5 |
|||
║ ║ │ │ |
|||
║ ║ 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ ║ 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── Please enter a cell number to place your next marker [X] (or Quit): |
|||
2 ◄■■■■■■■■■■ human player's move. |
|||
║ ║ │ │ |
|||
║ X ║ 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ ║ 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── computer places a marker [O] at cell number 9 |
|||
║ ║ │ │ |
|||
║ X ║ 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ ║ O 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── Please enter a cell number to place your next marker [X] (or Quit): |
|||
1 ◄■■■■■■■■■■ human player's move. |
|||
║ ║ │ │ |
|||
X ║ X ║ 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ ║ O 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── computer places a marker [O] at cell number 3 |
|||
║ ║ │ │ |
|||
X ║ X ║ O 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ ║ O 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── Please enter a cell number to place your next marker [X] (or Quit): |
|||
7 ◄■■■■■■■■■■ human player's move. |
|||
║ ║ │ │ |
|||
X ║ X ║ O 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
X ║ ║ O 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
───────── computer places a marker [O] at cell number 6 |
|||
║ ║ │ │ |
|||
X ║ X ║ O 1 │ 2 │ 3 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
║ O ║ O 4 │ 5 │ 6 |
|||
║ ║ │ │ |
|||
═════╬═════╬═════ ─────┼─────┼───── |
|||
║ ║ │ │ |
|||
X ║ ║ O 7 │ 8 │ 9 |
|||
║ ║ │ │ |
|||
────────────────────────────── The computer (O) won. ────────────────────────────── |
|||
</pre> |
|||
'''output''' when using the input of: <tt> 5 db </tt> |
|||
<br>(which indicates a grid of '''5x5''' and that the marker for the human is a hexadecimal '''db''' [█].) |
|||
<pre style="height:153ex"> |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 1 │ 2 │ 3 │ 4 │ 5 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 6 │ 7 │ 8 │ 9 │ 10 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 11 │ 12 │ 13 │ 14 │ 15 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 16 │ 17 │ 18 │ 19 │ 20 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 21 │ 22 │ 23 │ 24 │ 25 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
───────── Please enter a cell number to place your next marker [█] (or Quit): |
|||
5 ◄■■■■■■■■■■ human player's move. |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ █ 1 │ 2 │ 3 │ 4 │ 5 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 6 │ 7 │ 8 │ 9 │ 10 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 11 │ 12 │ 13 │ 14 │ 15 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 16 │ 17 │ 18 │ 19 │ 20 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 21 │ 22 │ 23 │ 24 │ 25 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
───────── computer places a marker [O] at cell number 14 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ █ 1 │ 2 │ 3 │ 4 │ 5 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 6 │ 7 │ 8 │ 9 │ 10 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ O ║ 11 │ 12 │ 13 │ 14 │ 15 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 16 │ 17 │ 18 │ 19 │ 20 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
═════╬═════╬═════╬═════╬═════ ─────┼─────┼─────┼─────┼───── |
|||
║ ║ ║ ║ │ │ │ │ |
|||
║ ║ ║ ║ 21 │ 22 │ 23 │ 24 │ 25 |
|||
║ ║ ║ ║ │ │ │ │ |
|||
───────── Please enter a cell number to place your next marker [█] (or Quit): |
|||
q ◄■■■■■■■■■■ human player's response. |
|||
────────────────────────────────── quitting. ────────────────────────────────── |
|||
</pre> |
|||
=={{header|Ring}}== |
|||
Easy and simple implementation of tecTacToe in ring programming language with human-human type of game(for now). |
|||
This implementation is a gui implementation using the default gui provided by the language |
|||
The tecTacToe.ring is provided [https://github.com/AbdelrahmanGIT/RingSamples/blob/master/src/TecTacToe.ring here] |
|||
<syntaxhighlight lang="ring"> |
|||
Load "guilib.ring" |
|||
#Provide a list to save each button status in numeric readable format |
|||
#0=nothing 1=X 2=O |
|||
lst=[] |
|||
#Provide onScreen button status and style |
|||
btns=[] |
|||
#Define who has the turn |
|||
isXTurn=true |
|||
app=new qApp |
|||
{ |
|||
frmMain=new qMainWindow() |
|||
{ |
|||
setWindowTitle("TicTacToe!") |
|||
resize(300,320) |
|||
move(200,200) |
|||
//buttons |
|||
pos=0 |
|||
for y=0 to 2 |
|||
for x=0 to 2 |
|||
//Creating Buttons on the screen |
|||
pos++ |
|||
Add(lst,0) |
|||
Add(btns,new qPushButton(frmMain) |
|||
{ |
|||
setGeometry(x*100,y*100,100,100) |
|||
setText("-") |
|||
setclickevent("Disp(" + pos +")") |
|||
setstylesheet("font-size:24pt ; font: bold ; color:yellow ; background-color: green") |
|||
}) |
|||
next |
|||
next |
|||
//StatusBar |
|||
status=new qStatusBar(frmMain) |
|||
{ |
|||
showMessage("Ready",0) |
|||
} |
|||
setwindowflags(Qt_dialog) |
|||
setStatusbar(status) |
|||
show() |
|||
} |
|||
exec() |
|||
} |
|||
//Restart the game by re init buttons status |
|||
func reStart |
|||
for i=1 to 9 |
|||
lst[i]=0 |
|||
btns[i].setText("-") |
|||
next |
|||
isXTurn=true |
|||
func Disp x |
|||
if isXTurn=true and lst[x]=0 |
|||
btns[x].setText("X") |
|||
lst[x]=1 |
|||
isXTurn=false |
|||
but isXTurn=false and lst[x]=0 |
|||
btns[x].setText("O") |
|||
lst[x]=2 |
|||
isXTurn=true |
|||
ok |
|||
winner = CheckWinner() |
|||
#if there is no Winner and still there is ability to winner |
|||
#continue playing. |
|||
if winner<1 return ok |
|||
//Who is the winner! |
|||
switch winner |
|||
on 1 |
|||
new qMessagebox(frmMain) |
|||
{ |
|||
SetWindowTitle("We have a winner!") |
|||
SetText("Good job X you won!") |
|||
show() |
|||
} |
|||
on 2 |
|||
new qMessagebox(frmMain) |
|||
{ |
|||
SetWindowTitle("We have a winner!") |
|||
SetText("Good job O you won!") |
|||
show() |
|||
} |
|||
on 3 |
|||
new qMessagebox(frmMain) |
|||
{ |
|||
SetWindowTitle("Oh no it's a tie") |
|||
SetText("Oh no it's a tie!") |
|||
show() |
|||
} |
|||
off |
|||
reStart() |
|||
func CheckWinner |
|||
//vertical check |
|||
for v=1 to 9 step 3 |
|||
if lst[v]!=0 and lst[v+1]!=0 and lst[v+2]!=0 |
|||
if lst[v]=lst[v+1] and lst[v+1]=lst[v+2] |
|||
return lst[v] |
|||
ok |
|||
ok |
|||
next |
|||
//horzintal |
|||
for h=1 to 3 |
|||
if lst[h]!=0 and lst[h+3]!=0 and lst[h+6]!=0 |
|||
if lst[h]=lst[h+3] and lst[h+3]=lst[h+6] |
|||
return lst[h] |
|||
ok |
|||
ok |
|||
next |
|||
//Cross |
|||
if lst[1]!=0 and lst[5]!=0 and lst[9]!=0 |
|||
if lst[1]=lst[5] and lst[5]=lst[9] return lst[1] ok |
|||
ok |
|||
if lst[3]!=0 and lst[5]!=0 and lst[7]!=0 |
|||
if lst[3]=lst[5] and lst[5]=lst[7] return lst[3] ok |
|||
ok |
|||
//tie |
|||
tie=true |
|||
for i=1 to 9 |
|||
if lst[i]=0 tie=false exit ok |
|||
next |
|||
if tie=true return 3 ok return 0 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
[[http://ring-lang.sourceforge.net/tictactoe.jpg image]] |
|||
=={{header|Ruby}}== |
|||
This implementation splits functionality into four classes: <code>Game</code>, <code>Player</code>, <code>HumanPlayer</code>, and <code>ComputerPlayer</code>. A game can be played between any combination of <code>HumanPlayer</code>s and <code>ComputerPlayer</code>s. |
|||
The AI for the computer uses certain heuristics to ensure that it never loses, but the AI is not as aggressive as it could be. The AI defends against the opening corner trap, but does not exploit it in its offense. |
|||
This implementation stores the board as a one-dimensional array and hardcodes all possible straight lines in <code>LINES</code>, rather than storing the board as a two-dimensional matrix and identifying straight lines dynamically. |
|||
<syntaxhighlight lang="ruby">module TicTacToe |
|||
LINES = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]] |
|||
class Game |
|||
def initialize(player_1_class, player_2_class) |
|||
@board = Array.new(10) # we ignore index 0 for convenience |
|||
@current_player_id = 0 |
|||
@players = [player_1_class.new(self, "X"), player_2_class.new(self, "O")] |
|||
puts "#{current_player} goes first." |
|||
end |
|||
attr_reader :board, :current_player_id |
|||
def play |
|||
loop do |
|||
place_player_marker(current_player) |
|||
if player_has_won?(current_player) |
|||
puts "#{current_player} wins!" |
|||
print_board |
|||
return |
|||
elsif board_full? |
|||
puts "It's a draw." |
|||
print_board |
|||
return |
|||
end |
|||
switch_players! |
|||
end |
|||
end |
|||
def free_positions |
|||
(1..9).select {|position| @board[position].nil?} |
|||
end |
|||
def place_player_marker(player) |
|||
position = player.select_position! |
|||
puts "#{player} selects #{player.marker} position #{position}" |
|||
@board[position] = player.marker |
|||
end |
|||
def player_has_won?(player) |
|||
LINES.any? do |line| |
|||
line.all? {|position| @board[position] == player.marker} |
|||
end |
|||
end |
|||
def board_full? |
|||
free_positions.empty? |
|||
end |
|||
def other_player_id |
|||
1 - @current_player_id |
|||
end |
|||
def switch_players! |
|||
@current_player_id = other_player_id |
|||
end |
|||
def current_player |
|||
@players[current_player_id] |
|||
end |
|||
def opponent |
|||
@players[other_player_id] |
|||
end |
|||
def turn_num |
|||
10 - free_positions.size |
|||
end |
|||
def print_board |
|||
col_separator, row_separator = " | ", "--+---+--" |
|||
label_for_position = lambda{|position| @board[position] ? @board[position] : position} |
|||
row_for_display = lambda{|row| row.map(&label_for_position).join(col_separator)} |
|||
row_positions = [[1,2,3], [4,5,6], [7,8,9]] |
|||
rows_for_display = row_positions.map(&row_for_display) |
|||
puts rows_for_display.join("\n" + row_separator + "\n") |
|||
end |
|||
end |
|||
class Player |
|||
def initialize(game, marker) |
|||
@game = game |
|||
@marker = marker |
|||
end |
|||
attr_reader :marker |
|||
end |
|||
class HumanPlayer < Player |
|||
def select_position! |
|||
@game.print_board |
|||
loop do |
|||
print "Select your #{marker} position: " |
|||
selection = gets.to_i |
|||
return selection if @game.free_positions.include?(selection) |
|||
puts "Position #{selection} is not available. Try again." |
|||
end |
|||
end |
|||
def to_s |
|||
"Human" |
|||
end |
|||
end |
|||
class ComputerPlayer < Player |
|||
DEBUG = false # edit this line if necessary |
|||
def group_positions_by_markers(line) |
|||
markers = line.group_by {|position| @game.board[position]} |
|||
markers.default = [] |
|||
markers |
|||
end |
|||
def select_position! |
|||
opponent_marker = @game.opponent.marker |
|||
winning_or_blocking_position = look_for_winning_or_blocking_position(opponent_marker) |
|||
return winning_or_blocking_position if winning_or_blocking_position |
|||
if corner_trap_defense_needed? |
|||
return corner_trap_defense_position(opponent_marker) |
|||
end |
|||
# could make this smarter by sometimes doing corner trap offense |
|||
return random_prioritized_position |
|||
end |
|||
def look_for_winning_or_blocking_position(opponent_marker) |
|||
for line in LINES |
|||
markers = group_positions_by_markers(line) |
|||
next if markers[nil].length != 1 |
|||
if markers[self.marker].length == 2 |
|||
log_debug "winning on line #{line.join}" |
|||
return markers[nil].first |
|||
elsif markers[opponent_marker].length == 2 |
|||
log_debug "could block on line #{line.join}" |
|||
blocking_position = markers[nil].first |
|||
end |
|||
end |
|||
if blocking_position |
|||
log_debug "blocking at #{blocking_position}" |
|||
return blocking_position |
|||
end |
|||
end |
|||
def corner_trap_defense_needed? |
|||
corner_positions = [1, 3, 7, 9] |
|||
opponent_chose_a_corner = corner_positions.any?{|pos| @game.board[pos] != nil} |
|||
return @game.turn_num == 2 && opponent_chose_a_corner |
|||
end |
|||
def corner_trap_defense_position(opponent_marker) |
|||
# if you respond in the center or the opposite corner, the opponent can force you to lose |
|||
log_debug "defending against corner start by playing adjacent" |
|||
# playing in an adjacent corner could also be safe, but would require more logic later on |
|||
opponent_position = @game.board.find_index {|marker| marker == opponent_marker} |
|||
safe_responses = {1=>[2,4], 3=>[2,6], 7=>[4,8], 9=>[6,8]} |
|||
return safe_responses[opponent_position].sample |
|||
end |
|||
def random_prioritized_position |
|||
log_debug "picking random position, favoring center and then corners" |
|||
([5] + [1,3,7,9].shuffle + [2,4,6,8].shuffle).find do |pos| |
|||
@game.free_positions.include?(pos) |
|||
end |
|||
end |
|||
def log_debug(message) |
|||
puts "#{self}: #{message}" if DEBUG |
|||
end |
|||
def to_s |
|||
"Computer#{@game.current_player_id}" |
|||
end |
|||
end |
|||
end |
|||
include TicTacToe |
|||
Game.new(ComputerPlayer, ComputerPlayer).play |
|||
puts |
|||
players_with_human = [HumanPlayer, ComputerPlayer].shuffle |
|||
Game.new(*players_with_human).play</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Computer0 goes first. |
|||
Computer0 selects X position 5 |
|||
Computer1 selects O position 9 |
|||
Computer0 selects X position 3 |
|||
Computer1 selects O position 7 |
|||
Computer0 selects X position 8 |
|||
Computer1 selects O position 2 |
|||
Computer0 selects X position 1 |
|||
Computer1 selects O position 4 |
|||
Computer0 selects X position 6 |
|||
It's a draw. |
|||
X | O | X |
|||
--+---+-- |
|||
O | X | X |
|||
--+---+-- |
|||
O | X | O |
|||
Human goes first. |
|||
1 | 2 | 3 |
|||
--+---+-- |
|||
4 | 5 | 6 |
|||
--+---+-- |
|||
7 | 8 | 9 |
|||
Select your X position: 3 |
|||
Human selects X position 3 |
|||
Computer1 selects O position 6 |
|||
1 | 2 | X |
|||
--+---+-- |
|||
4 | 5 | O |
|||
--+---+-- |
|||
7 | 8 | 9 |
|||
Select your X position: 7 |
|||
Human selects X position 7 |
|||
Computer1 selects O position 5 |
|||
1 | 2 | X |
|||
--+---+-- |
|||
4 | O | O |
|||
--+---+-- |
|||
X | 8 | 9 |
|||
Select your X position: 4 |
|||
Human selects X position 4 |
|||
Computer1 selects O position 1 |
|||
O | 2 | X |
|||
--+---+-- |
|||
X | O | O |
|||
--+---+-- |
|||
X | 8 | 9 |
|||
Select your X position: 9 |
|||
Human selects X position 9 |
|||
Computer1 selects O position 8 |
|||
O | 2 | X |
|||
--+---+-- |
|||
X | O | O |
|||
--+---+-- |
|||
X | O | X |
|||
Select your X position: 2 |
|||
Human selects X position 2 |
|||
It's a draw. |
|||
O | X | X |
|||
--+---+-- |
|||
X | O | O |
|||
--+---+-- |
|||
X | O | X</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust"> |
|||
use GameState::{ComputerWin, Draw, PlayerWin, Playing}; |
|||
use rand::prelude::*; |
|||
#[derive(PartialEq, Debug)] |
|||
enum GameState { |
|||
PlayerWin, |
|||
ComputerWin, |
|||
Draw, |
|||
Playing, |
|||
} |
|||
type Board = [[char; 3]; 3]; |
|||
fn main() { |
|||
let mut rng = StdRng::from_entropy(); |
|||
let mut board: Board = [['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']]; |
|||
draw_board(board); |
|||
loop { |
|||
player_turn(&mut board); |
|||
if check_win(board) != Playing { |
|||
break; |
|||
} |
|||
computer_turn(&mut rng, &mut board); |
|||
if check_win(board) != Playing { |
|||
break; |
|||
} |
|||
draw_board(board); |
|||
} |
|||
draw_board(board); |
|||
let announcement = match check_win(board) { |
|||
PlayerWin => "The Player has won!", |
|||
ComputerWin => "The Computer has won!", |
|||
Draw => "Draw!", |
|||
Playing => unreachable!(), |
|||
}; |
|||
println!("{}", announcement); |
|||
} |
|||
fn is_empty(cell: &char) -> bool { |
|||
*cell != 'X' && *cell != 'O' |
|||
} |
|||
fn check_win(board: Board) -> GameState { |
|||
// check for win |
|||
for (i, row) in board.iter().enumerate() { |
|||
if row[0] == row[1] && row[0] == row[2] { |
|||
return which_win(row[0]); |
|||
} else if board[0][i] == board[1][i] && board[0][i] == board[2][i] { |
|||
return which_win(board[0][i]); |
|||
} |
|||
} |
|||
if board[0][0] == board[1][1] && board[0][0] == board[2][2] { |
|||
return which_win(board[0][0]); |
|||
} else if board[0][2] == board[1][1] && board[0][2] == board[2][0] { |
|||
return which_win(board[0][2]); |
|||
} |
|||
// check if it's not a draw |
|||
let is_draw = board.iter().flatten().any(is_empty); |
|||
if is_draw { |
|||
Playing |
|||
} else { |
|||
Draw |
|||
} |
|||
} |
|||
fn which_win(s: char) -> GameState { |
|||
match s { |
|||
'X' => PlayerWin, |
|||
'O' => ComputerWin, |
|||
_ => unreachable!(), |
|||
} |
|||
} |
|||
fn player_turn(board: &mut Board) { |
|||
use std::io; |
|||
println!("Player, enter your field of choice!: "); |
|||
let mut ln = String::new(); |
|||
io::stdin() |
|||
.read_line(&mut ln) |
|||
.expect("Failed to read stdin"); |
|||
let choice = ln.trim().parse::<usize>().expect("Failed to parse input"); |
|||
let row = (choice - 1) / 3; |
|||
let col = (choice - 1) % 3; |
|||
if board[row][col] == 'X' || board[row][col] == 'O' { |
|||
println!("Someone already took this field!"); |
|||
player_turn(board); |
|||
} else { |
|||
board[row][col] = 'X'; |
|||
} |
|||
} |
|||
fn computer_turn<R: Rng>(rng: &mut R, board: &mut Board) { |
|||
let possible_choices: Vec<_> = board |
|||
.iter() |
|||
.flatten() |
|||
.enumerate() |
|||
.filter(|&(_, c)| is_empty(c)) |
|||
.map(|(i, _)| i) |
|||
.collect(); |
|||
let choice = possible_choices.choose(rng).unwrap(); |
|||
println!("Computer chose: {}", choice); |
|||
let row = choice / 3; |
|||
let col = choice % 3; |
|||
board[row][col] = 'O'; |
|||
} |
|||
fn draw_board(board: Board) { |
|||
for row in &board { |
|||
println!("{} {} {}", row[0], row[1], row[2]); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
Player, enter your field of choice!: |
|||
1 |
|||
Computer chose: 6 |
|||
X 2 3 |
|||
4 5 6 |
|||
O 8 9 |
|||
Player, enter your field of choice!: |
|||
5 |
|||
Computer chose: 7 |
|||
X 2 3 |
|||
4 X 6 |
|||
O O 9 |
|||
Player, enter your field of choice!: |
|||
9 |
|||
X 2 3 |
|||
4 X 6 |
|||
O O X |
|||
The Player has won! |
|||
</pre> |
|||
=={{header|Scala}}== |
|||
===Functional implementation.=== |
|||
Computer vs. human. Human starts. Computer plays 'O' and human plays 'X'. |
|||
Computer moves are legal, but random. |
|||
<syntaxhighlight lang="scala">package object tictactoe { |
|||
val Human = 'X' |
|||
val Computer = 'O' |
|||
val BaseBoard = ('1' to '9').toList |
|||
val WinnerLines = List((0,1,2), (3,4,5), (6,7,8), (0,3,6), (1,4,7), (2,5,8), (0,4,8), (2,4,6)) |
|||
val randomGen = new util.Random(System.currentTimeMillis) |
|||
} |
|||
package tictactoe { |
|||
class Board(aBoard : List[Char] = BaseBoard) { |
|||
def availableMoves = aBoard.filter(c => c != Human && c != Computer) |
|||
def availableMovesIdxs = for ((c,i) <- aBoard.zipWithIndex if c != Human && c != Computer) yield i |
|||
def computerPlays = new Board(aBoard.updated(availableMovesIdxs(randomGen.nextInt(availableMovesIdxs.length)), Computer)) |
|||
def humanPlays(move : Char) = new Board(aBoard.updated(aBoard.indexOf(move), Human)) |
|||
def isDraw = aBoard.forall(c => c == Human || c == Computer) |
|||
def isWinner(winner : Char) = |
|||
WinnerLines.exists{case (i,j,k) => aBoard(i) == winner && aBoard(j) == winner && aBoard(k) == winner} |
|||
def isOver = isWinner(Computer) || isWinner(Human) || isDraw |
|||
def print { |
|||
aBoard.grouped(3).foreach(row => println(row(0) + " " + row(1) + " " + row(2))) |
|||
} |
|||
def printOverMessage { |
|||
if (isWinner(Human)) println("You win.") |
|||
else if (isWinner(Computer)) println("Computer wins.") |
|||
else if (isDraw) println("It's a draw.") |
|||
else println("Not over yet, or something went wrong.") |
|||
} |
|||
} |
|||
object TicTacToe extends App { |
|||
def play(board : Board, turn : Char) { |
|||
// Reads a char from input until it is one of |
|||
// the available moves in the current board |
|||
def readValidMove() : Char = { |
|||
print("Choose a move: ") |
|||
val validMoves = board.availableMoves |
|||
val move = readChar |
|||
if (validMoves.contains(move)) { |
|||
move |
|||
} else { |
|||
println("Invalid move. Choose another one in " + validMoves) |
|||
readValidMove() |
|||
} |
|||
} |
|||
board.print |
|||
if (board.isOver) { |
|||
board.printOverMessage |
|||
return |
|||
} |
|||
if (turn == Human) { // Human plays |
|||
val nextBoard = board.humanPlays(readValidMove) |
|||
play(nextBoard, Computer) |
|||
} else { // Computer plays |
|||
println("Computer plays: ") |
|||
val nextBoard = board.computerPlays |
|||
play(nextBoard, Human) |
|||
} |
|||
} |
|||
play(new Board(),Human) |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}}(human is always first) |
|||
<pre> |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
Choose a move: 1 |
|||
X 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
Computer plays: |
|||
X 2 O |
|||
4 5 6 |
|||
7 8 9 |
|||
Choose a move: 3 |
|||
Invalid move. Choose another one in List(2, 4, 5, 6, 7, 8, 9) |
|||
Choose a move: 4 |
|||
X 2 O |
|||
X 5 6 |
|||
7 8 9 |
|||
Computer plays: |
|||
X 2 O |
|||
X 5 6 |
|||
7 8 O |
|||
Choose a move: 7 |
|||
X 2 O |
|||
X 5 6 |
|||
X 8 O |
|||
You win.</pre> |
|||
=={{header|Scilab}}== |
|||
Can be a game of human v. human, human v. machine, or machine v. machine. Machine moves have a hierarchy: firstly, it looks for a winning move; secondly, it looks for a way to block the opponent's victory; lastly, it makes a random move. |
|||
<syntaxhighlight lang="text">function [] = startGame() |
|||
//Board size and marks |
|||
N = 3; |
|||
marks = ["X" "O"]; |
|||
//Creating empty board |
|||
board = string(zeros(N,N)); |
|||
for i = 1:(N*N) |
|||
board(i) = ""; |
|||
end |
|||
//Initialising players |
|||
clc(); |
|||
players= [%F %F]; |
|||
players = playerSetup(marks); |
|||
//Console header |
|||
header = [strsplit(marks(1)+" is ----")';... |
|||
strsplit(marks(2)+" is ----")']; |
|||
for i = 1:2 |
|||
if players(i) then |
|||
header(i,6:10) = strsplit("P"+string(i)+". "); |
|||
else |
|||
header(i,6:10) = strsplit("COMP."); |
|||
end |
|||
end |
|||
//Game loop |
|||
sleep(1000); |
|||
win_flag = %F; |
|||
count = 0; |
|||
while count<N*N |
|||
//Clear console, and print header and board |
|||
clc(); |
|||
printf("%s\n %s\n",strcat(header(1,:)),strcat(header(2,:))); |
|||
dispBoard(board); |
|||
//Find which player should move |
|||
player_n = modulo(count,2) + 1; |
|||
if players(player_n) == %T then |
|||
//Human plays |
|||
pos = []; |
|||
valid_move = %F; |
|||
disp(marks(player_n)+"''s turn."); |
|||
while valid_move ~= %T |
|||
[pos,valid_move] = readHumanMove(board); |
|||
if ~valid_move then |
|||
disp("You should input a valid cell number."); |
|||
end |
|||
end |
|||
if valid_move then |
|||
board = updateBoard(board,pos,marks(player_n)); |
|||
else |
|||
error("Invalid move."); |
|||
end |
|||
else |
|||
//Computer plays |
|||
disp("Computer is playing."); |
|||
board = ComputerMove(board,marks(player_n),marks); |
|||
sleep(800); |
|||
end |
|||
//Count number of movements |
|||
count = count + 1; |
|||
//Check if the game has finished |
|||
[win_flag,winning_mark] = detectWin(board) |
|||
if win_flag then |
|||
break |
|||
end |
|||
end |
|||
//Clear screen at the end of game |
|||
clc(); |
|||
disp("Game finished:"); |
|||
dispBoard(board); |
|||
//Print results |
|||
if win_flag then |
|||
disp(winning_mark+" won!"); |
|||
else |
|||
disp("It''s a tie."); |
|||
end |
|||
//Play again? |
|||
play_again = ""; |
|||
while play_again ~= "Y" & play_again ~= "N" |
|||
play_again = input("Would you like to play again? (Y/N)","s"); |
|||
play_again = strsplit(play_again); |
|||
play_again = convstr(play_again(1),"u"); |
|||
if play_again ~= "Y" & play_again ~= "N" then |
|||
disp("Invalid answer."); |
|||
end |
|||
end |
|||
if play_again == "Y" then |
|||
startGame(); |
|||
else |
|||
disp("Quit game."); |
|||
end |
|||
endfunction |
|||
function players = playerSetup(marks) |
|||
//Determines who plays which mark |
|||
players = [%F %F]; //True for human, Flase for computer |
|||
printf("\n%s always starts.\n",marks(1)); |
|||
for i = 1:2 |
|||
user_input = ""; |
|||
while user_input ~= "Y" & user_input ~= "N" |
|||
user_input = input("Would you like to play as "+marks(i)+"? (Y/N)","s"); |
|||
user_input = strsplit(user_input); |
|||
user_input = convstr(user_input(1),"u"); |
|||
if user_input ~= "Y" & user_input ~= "N" then |
|||
disp("Invalid answer."); |
|||
end |
|||
end |
|||
//Print choice |
|||
if user_input == "Y" then |
|||
players(i) = %T; |
|||
printf("%s shall be player %d (P%d).\n\n",marks(i),i,i); |
|||
else |
|||
printf("%s shall be the computer (COMP).\n\n",marks(i)); |
|||
end |
|||
end |
|||
endfunction |
|||
function [] = dispBoard(board) |
|||
//Print ASCII board on console |
|||
//Get board marks |
|||
marks = [" " " "]; |
|||
mark_inds = find(board ~= ""); |
|||
if mark_inds ~= [] then |
|||
marks(1) = board(mark_inds(1)); |
|||
mark_inds = find( (board ~= "") & (board ~= marks(1)) ); |
|||
if mark_inds ~= [] then |
|||
marks(2) = board(mark_inds(1)); |
|||
end |
|||
end |
|||
//Transpose to display for humans |
|||
//[compatibility with readHumanMove()] |
|||
disp_board = board'; |
|||
rows = 3*size(board,'r'); |
|||
cols = 4*size(board,'c'); |
|||
ascii_board = string(zeros(rows, cols)); |
|||
mark_1=[... |
|||
strsplit(" |")';... |
|||
strsplit(" "+marks(1)+" |")';... |
|||
strsplit("___|")']; |
|||
mark_2=[... |
|||
strsplit(" |")';... |
|||
strsplit(" "+marks(2)+" |")';... |
|||
strsplit("___|")']; |
|||
Blank_mark=[... |
|||
strsplit(" |")';... |
|||
strsplit(" |")';... |
|||
strsplit("___|")']; |
|||
for r = ([1:size(board,'r')] - 1 ) |
|||
for c = ([1:size(board,'c')] - 1) |
|||
if disp_board(r+1,c+1) == marks(1) then |
|||
ascii_board((r*3 + 1):((r+1)*3),... |
|||
(c*4 + 1):((c+1)*4)) = mark_1; |
|||
elseif disp_board(r+1,c+1) == marks(2) then |
|||
ascii_board((r*3 + 1):((r+1)*3),... |
|||
(c*4 + 1):((c+1)*4)) = mark_2; |
|||
else |
|||
ascii_board((r*3 + 1):((r+1)*3),... |
|||
(c*4 + 1):((c+1)*4)) = Blank_mark; |
|||
end |
|||
end |
|||
end |
|||
for i = 1:cols |
|||
if modulo(i,4)>0 then |
|||
ascii_board(rows,i) = " "; |
|||
end |
|||
end |
|||
for i = 1:rows |
|||
ascii_board(i,cols) = " "; |
|||
end |
|||
printf("\n"); |
|||
for i = 1:size(ascii_board,'r') |
|||
printf("%s\n",strcat(ascii_board(i,:))) |
|||
end |
|||
endfunction |
|||
function moves_board = availableMoves(board) |
|||
//Find empty cells on the board |
|||
moves_board = board; |
|||
for i = 1:(size(board,'r')*size(board,'c')) |
|||
if board(i) == "" then |
|||
moves_board(i) = string(i); |
|||
else |
|||
moves_board(i) = "_"; |
|||
end |
|||
end |
|||
endfunction |
|||
function varargout = readHumanMove(board) |
|||
//Read human input |
|||
printf("\nAvailable cells:"); |
|||
moves_board = availableMoves(board); |
|||
disp(moves_board'); |
|||
x = input("\nEnter a move (0 to quit game): "); |
|||
valid = %F; |
|||
pos = 0; |
|||
total = size(moves_board,'r') * size(moves_board,'c'); |
|||
//Check if it is a valid move |
|||
if x == 0 then |
|||
disp("Quit game.") |
|||
abort |
|||
elseif (x>=1 & x<=total) then |
|||
if (moves_board(x) == string(x)) then |
|||
valid = %T; |
|||
pos = x; |
|||
end |
|||
end |
|||
varargout = list(pos,valid); |
|||
endfunction |
|||
function varargout = updateBoard(board,pos,player) |
|||
//Add move to the board |
|||
if board(pos) ~= "" then |
|||
error('Error: Invalid move.'); |
|||
end |
|||
board(pos) = player |
|||
varargout = list(board); |
|||
endfunction |
|||
function varargout = detectWin(board) |
|||
//Detect if there is a winner or not |
|||
win_flag = %F; |
|||
winner = ""; |
|||
//Get board marks |
|||
marks = ["" ""]; |
|||
mark_inds = find(board ~= ""); |
|||
marks(1) = board(mark_inds(1)) |
|||
mark_inds = find( (board ~= "") & (board ~= marks(1)) ); |
|||
marks(2) = board(mark_inds(1)); |
|||
//If there is a minimum number of moves, check if there is a winner |
|||
n_moves = find(~(board == "")); |
|||
n_moves = length(n_moves) |
|||
if n_moves >= size(board,'r') then |
|||
board_X = (board == marks(1)); |
|||
board_O = (board == marks(2)); |
|||
for i = 1:size(board,'r') |
|||
//Check rows |
|||
if find(~board_X(i,:)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(1); |
|||
break |
|||
end |
|||
if find(~board_O(i,:)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(2); |
|||
break |
|||
end |
|||
//Check columns |
|||
if find(~board_X(:,i)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(1); |
|||
break |
|||
end |
|||
if find(~board_O(:,i)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(2); |
|||
break |
|||
end |
|||
end |
|||
//Check diagonal |
|||
if ~win_flag then |
|||
if find(~diag(board_X)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(1); |
|||
elseif find(~diag(board_O)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(2); |
|||
end |
|||
end |
|||
//Check anti-diagonal |
|||
if ~win_flag then |
|||
board_X = board_X(:,$:-1:1); |
|||
board_O = board_O(:,$:-1:1); |
|||
if find(~diag(board_X)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(1); |
|||
elseif find(~diag(board_O)) == [] then |
|||
win_flag = %T; |
|||
winner = marks(2); |
|||
end |
|||
end |
|||
end |
|||
varargout = list(win_flag,winner) |
|||
endfunction |
|||
function threat_pos = findThreat(board,player) |
|||
//Returns a list of moves that can finish the game |
|||
//Available moves |
|||
move_inds = find(~( availableMoves(board) == "_" )); |
|||
//If there is a minimum number of moves, check if there is a threat |
|||
threat_pos = []; |
|||
if (size(board,'r')*size(board,'c')) - length(move_inds) >... |
|||
(size(board,'r') - 1) then |
|||
for i = 1:length(move_inds) |
|||
temp_board = updateBoard(board,move_inds(i),player); |
|||
[win_flag,winner] = detectWin(temp_board); |
|||
if win_flag & winner == player then |
|||
threat_pos = [threat_pos move_inds(i)]; |
|||
end |
|||
end |
|||
end |
|||
endfunction |
|||
function varargout = ComputerMove(board,mark,all_marks) |
|||
//Atomatically add a move to the board with no human input |
|||
//Find winning moves moves |
|||
move_inds = findThreat(board,mark); |
|||
//If there are no winning moves, find opponent's winning moves |
|||
//to block opponent's victory |
|||
if move_inds == [] then |
|||
if mark == all_marks(1) then |
|||
opponent = all_marks(2); |
|||
elseif mark == all_marks(2) then |
|||
opponent = all_marks(1); |
|||
end |
|||
move_inds = findThreat(board,opponent); |
|||
end |
|||
//If there are no winning moves or threats, find all possible moves |
|||
if move_inds == [] then |
|||
move_inds = find(~( availableMoves(board) == "_" )); |
|||
end |
|||
//Choose a random move among the selected possible moves |
|||
pos = grand(1,"prm",move_inds); |
|||
pos = pos(1); |
|||
//Update board by adding a new mark |
|||
board(pos) = mark; |
|||
varargout = list(board); |
|||
endfunction |
|||
startGame()</syntaxhighlight> |
|||
=={{header|SQL}}== |
|||
Basic playable TicTacToe in SQLite using only the sqlite3 CLI. |
|||
The CLI wasn't really designed for this, making the interactivity a little bit clunky. However, some of the higher-level constructs in SQL allow for some intriguingly elegant game logic. |
|||
<syntaxhighlight lang="sql"> |
|||
--Setup |
|||
drop table if exists board; |
|||
create table board (p char, r integer, c integer); |
|||
insert into board values('.', 0, 0),('.', 0, 1),('.', 0, 2),('.', 1, 0),('.', 1, 1),('.', 1, 2),('.', 2, 0),('.', 2, 1),('.', 2, 2); |
|||
-- Use a trigger for move events |
|||
drop trigger if exists after_moved; |
|||
create trigger after_moved after update on board for each row when new.p <> '.' and new.p <> 'O' |
|||
begin |
|||
-- Verify move is valid |
|||
select |
|||
case |
|||
when (select v from msg) like '%Wins!' then raise(ABORT, 'The game is already over.') |
|||
when (select old.p from board where rowid = rid) <> '.' then raise(ABORT, 'That position has already been taken. Please choose an available position.') |
|||
when new.p <> 'X' then raise(ABORT, 'Please place an ''X''') |
|||
end |
|||
from ( |
|||
select rowid rid from board |
|||
where p = new.p |
|||
except |
|||
select p from board where p = old.p |
|||
); |
|||
-- Check for game over |
|||
update msg set v = ( |
|||
select |
|||
case |
|||
when max(num) >= 3 then 'X Wins!' |
|||
when (select count(*) from board where p = '.') = 0 then 'Cat Wins!' |
|||
else 'Move made' |
|||
end |
|||
from ( -- Is Game Over |
|||
select count(*) num from board where p = 'X' group by r union -- Horz |
|||
select count(*) num from board where p = 'X' group by c union -- Vert |
|||
select count(*) num from board where p = 'X' and r = c union -- Diag TL->BR |
|||
select count(*) num from board where p = 'X' and (2-r) = c -- Diag TR->BL |
|||
) |
|||
); |
|||
--Have computer player make a random move |
|||
update board set p = 'O' |
|||
where rowid = (select rid from (select max(rnd),rid from (select rowid rid, random() rnd from board where p = '.'))) |
|||
and (select v from msg) not like '%Wins!'; |
|||
--NOTE: SQLite doesn't allow update order by in triggers, otherwise we could just use this beautiful line: |
|||
-- update board set p = 'O' where p = '.' order by random() limit 1; |
|||
--Check to see if the computer player won |
|||
update msg set v = ( |
|||
select |
|||
case |
|||
when max(num) >= 3 then 'O Wins!' |
|||
else v |
|||
end |
|||
from ( -- Is Game Over |
|||
select count(*) num from board where p = 'O' group by r union -- Horz |
|||
select count(*) num from board where p = 'O' group by c union -- Vert |
|||
select count(*) num from board where p = 'O' and r = c union -- Diag TL->BR |
|||
select count(*) num from board where p = 'O' and (2-r) = c -- Diag TR->BL |
|||
) |
|||
); |
|||
end; |
|||
-- UI to display the logical board as a grid |
|||
drop view if exists ui; |
|||
create view ui as |
|||
select case when p = '.' then col0.rowid else p end c0, c1, c2 |
|||
from board as col0 |
|||
join (select case when p = '.' then board.rowid else p end c1, r from board where c = 1) as col1 on col0.r = col1.r |
|||
join (select case when p = '.' then board.rowid else p end c2, r from board where c = 2) as col2 on col0.r = col2.r |
|||
where c = 0; |
|||
drop table if exists msg; |
|||
create table msg (v text); |
|||
insert into msg values(''); |
|||
-- Readme |
|||
select * from ui; |
|||
.print "Use this to play:" |
|||
.print "->update board set p = 'X' where rowid = ?; select * from ui; select * from msg;"' |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
(Assuming the above code is in a file called ttt.sql) |
|||
sqlite>.read ttt.sql |
|||
1|2|3 |
|||
4|5|6 |
|||
7|8|9 |
|||
Use this to play: |
|||
update board set p = 'X' where rowid = ?; select * from ui; select * from msg; |
|||
sqlite> update board set p = 'X' where rowid = 1; select * from ui; select * from msg; |
|||
X|2|3 |
|||
4|5|O |
|||
7|8|9 |
|||
Move made |
|||
sqlite> update board set p = 'X' where rowid = 2; select * from ui; select * from msg; |
|||
X|X|3 |
|||
4|5|O |
|||
7|O|9 |
|||
Move made |
|||
sqlite> update board set p = 'X' where rowid = 3; select * from ui; select * from msg; |
|||
X|X|X |
|||
4|5|O |
|||
7|O|9 |
|||
X Wins! |
|||
</pre> |
|||
=={{header|Swift}}== |
|||
Some Basic AI for obvious losing and winning conditions |
|||
<syntaxhighlight lang="swift"> |
|||
import Darwin |
|||
enum Token : CustomStringConvertible { |
|||
case cross, circle |
|||
func matches(tokens: [Token?]) -> Bool { |
|||
for token in tokens { |
|||
guard let t = token, t == self else { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
func emptyCell(in tokens: [Token?]) -> Int? { |
|||
if tokens[0] == nil |
|||
&& tokens[1] == self |
|||
&& tokens[2] == self { |
|||
return 0 |
|||
} else |
|||
if tokens[0] == self |
|||
&& tokens[1] == nil |
|||
&& tokens[2] == self { |
|||
return 1 |
|||
} else |
|||
if tokens[0] == self |
|||
&& tokens[1] == self |
|||
&& tokens[2] == nil { |
|||
return 2 |
|||
} |
|||
return nil |
|||
} |
|||
var description: String { |
|||
switch self { |
|||
case .cross: return "x" |
|||
case .circle: return "o" |
|||
} |
|||
} |
|||
} |
|||
struct Board { |
|||
var cells: [Token?] = [nil, nil, nil, nil, nil, nil, nil, nil, nil] |
|||
func cells(atCol col: Int) -> [Token?] { |
|||
return [cells[col], cells[col + 3], cells[col + 6]] |
|||
} |
|||
func cells(atRow row: Int) -> [Token?] { |
|||
return [cells[row * 3], cells[row * 3 + 1], cells[row * 3 + 2]] |
|||
} |
|||
func cellsTopLeft() -> [Token?] { |
|||
return [cells[0], cells[4], cells[8]] |
|||
} |
|||
func cellsBottomLeft() -> [Token?] { |
|||
return [cells[6], cells[4], cells[2]] |
|||
} |
|||
func winner() -> Token? { |
|||
let r0 = cells(atRow: 0) |
|||
let r1 = cells(atRow: 1) |
|||
let r2 = cells(atRow: 2) |
|||
let c0 = cells(atCol: 0) |
|||
let c1 = cells(atCol: 1) |
|||
let c2 = cells(atCol: 2) |
|||
let tl = cellsTopLeft() |
|||
let bl = cellsBottomLeft() |
|||
if Token.cross.matches(tokens: r0) |
|||
|| Token.cross.matches(tokens: r1) |
|||
|| Token.cross.matches(tokens: r2) |
|||
|| Token.cross.matches(tokens: c0) |
|||
|| Token.cross.matches(tokens: c1) |
|||
|| Token.cross.matches(tokens: c2) |
|||
|| Token.cross.matches(tokens: tl) |
|||
|| Token.cross.matches(tokens: bl) { |
|||
return .cross |
|||
} else |
|||
if Token.circle.matches(tokens: r0) |
|||
|| Token.circle.matches(tokens: r1) |
|||
|| Token.circle.matches(tokens: r2) |
|||
|| Token.circle.matches(tokens: c0) |
|||
|| Token.circle.matches(tokens: c1) |
|||
|| Token.circle.matches(tokens: c2) |
|||
|| Token.circle.matches(tokens: tl) |
|||
|| Token.circle.matches(tokens: bl) { |
|||
return .circle |
|||
} |
|||
return nil |
|||
} |
|||
func atCapacity() -> Bool { |
|||
return cells.filter { $0 == nil }.count == 0 |
|||
} |
|||
mutating func play(token: Token, at location: Int) { |
|||
cells[location] = token |
|||
} |
|||
func findBestLocation(for player: Token) -> Int? { |
|||
let r0 = cells(atRow: 0) |
|||
let r1 = cells(atRow: 1) |
|||
let r2 = cells(atRow: 2) |
|||
let c0 = cells(atCol: 0) |
|||
let c1 = cells(atCol: 1) |
|||
let c2 = cells(atCol: 2) |
|||
let tl = cellsTopLeft() |
|||
let bl = cellsBottomLeft() |
|||
if let cell = player.emptyCell(in: r0) { |
|||
return cell |
|||
} else if let cell = player.emptyCell(in: r1) { |
|||
return cell + 3 |
|||
} else if let cell = player.emptyCell(in: r2) { |
|||
return cell + 6 |
|||
} else if let cell = player.emptyCell(in: c0) { |
|||
return cell * 3 |
|||
} else if let cell = player.emptyCell(in: c1) { |
|||
return cell * 3 + 1 |
|||
} else if let cell = player.emptyCell(in: c2) { |
|||
return cell * 3 + 2 |
|||
} else if let cell = player.emptyCell(in: tl) { |
|||
return cell == 0 ? 0 : (cell == 1 ? 4 : 8) |
|||
} else if let cell = player.emptyCell(in: bl) { |
|||
return cell == 0 ? 6 : (cell == 1 ? 4 : 2) |
|||
} |
|||
return nil |
|||
} |
|||
func findMove() -> Int { |
|||
let empties = cells.enumerated().filter { $0.1 == nil } |
|||
let r = Int(arc4random()) % empties.count |
|||
return empties[r].0 |
|||
} |
|||
} |
|||
extension Board : CustomStringConvertible { |
|||
var description: String { |
|||
var result = "\n---------------\n" |
|||
for (idx, cell) in cells.enumerated() { |
|||
if let cell = cell { |
|||
result += "| \(cell) |" |
|||
} else { |
|||
result += "| \(idx) |" |
|||
} |
|||
if (idx + 1) % 3 == 0 { |
|||
result += "\n---------------\n" |
|||
} |
|||
} |
|||
return result |
|||
} |
|||
} |
|||
while true { |
|||
var board = Board() |
|||
print("Who do you want to play as ('o' or 'x'): ", separator: "", terminator: "") |
|||
let answer = readLine()?.characters.first ?? "x" |
|||
var player: Token = answer == "x" ? .cross : .circle |
|||
var pc: Token = player == .cross ? .circle : .cross |
|||
print(board) |
|||
while true { |
|||
print("Choose cell to play on: ", separator: "", terminator: "") |
|||
var pos = Int(readLine() ?? "0") ?? 0 |
|||
while !board.atCapacity() && board.cells[pos] != nil { |
|||
print("Invalid move. Choose cell to play on: ", separator: "", terminator: "") |
|||
pos = Int(readLine() ?? "0") ?? 0 |
|||
} |
|||
if board.atCapacity() { |
|||
print("Draw") |
|||
break |
|||
} |
|||
board.play(token: player, at: pos) |
|||
print(board) |
|||
if let winner = board.winner() { |
|||
print("winner is \(winner)") |
|||
break |
|||
} else if board.atCapacity() { |
|||
print("Draw") |
|||
break |
|||
} |
|||
if let win = board.findBestLocation(for: pc) { |
|||
board.play(token: pc, at: win) |
|||
} else if let def = board.findBestLocation(for: player) { |
|||
board.play(token: pc, at: def) |
|||
} else { |
|||
board.play(token: pc, at: board.findMove()) |
|||
} |
|||
print(board) |
|||
if let winner = board.winner() { |
|||
print("winner is \(winner)") |
|||
break |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Who do you want to play as ('o' or 'x'): x |
|||
--------------- |
|||
| 0 || 1 || 2 | |
|||
--------------- |
|||
| 3 || 4 || 5 | |
|||
--------------- |
|||
| 6 || 7 || 8 | |
|||
--------------- |
|||
Choose cell to play on: 4 |
|||
--------------- |
|||
| 0 || 1 || 2 | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| 6 || 7 || 8 | |
|||
--------------- |
|||
--------------- |
|||
| o || 1 || 2 | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| 6 || 7 || 8 | |
|||
--------------- |
|||
Choose cell to play on: 6 |
|||
--------------- |
|||
| o || 1 || 2 | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| x || 7 || 8 | |
|||
--------------- |
|||
--------------- |
|||
| o || 1 || o | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| x || 7 || 8 | |
|||
--------------- |
|||
Choose cell to play on: 1 |
|||
--------------- |
|||
| o || x || o | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| x || 7 || 8 | |
|||
--------------- |
|||
--------------- |
|||
| o || x || o | |
|||
--------------- |
|||
| 3 || x || 5 | |
|||
--------------- |
|||
| x || o || 8 | |
|||
--------------- |
|||
Choose cell to play on: 5 |
|||
--------------- |
|||
| o || x || o | |
|||
--------------- |
|||
| 3 || x || x | |
|||
--------------- |
|||
| x || o || 8 | |
|||
--------------- |
|||
--------------- |
|||
| o || x || o | |
|||
--------------- |
|||
| o || x || x | |
|||
--------------- |
|||
| x || o || 8 | |
|||
--------------- |
|||
Choose cell to play on: 8 |
|||
--------------- |
|||
| o || x || o | |
|||
--------------- |
|||
| o || x || x | |
|||
--------------- |
|||
| x || o || x | |
|||
--------------- |
|||
Draw |
|||
Who do you want to play as ('o' or 'x'): |
|||
</pre> |
|||
=={{header|Tailspin}}== |
|||
<syntaxhighlight lang="tailspin"> |
|||
processor Tic-Tac-Toe |
|||
@: [position´1..position´9]; |
|||
source isWonOrDone |
|||
[$@Tic-Tac-Toe(1..3) -> #, $@Tic-Tac-Toe(4..6) -> #, $@Tic-Tac-Toe(7..9) -> #, |
|||
$@Tic-Tac-Toe(1..9:3) -> #, $@Tic-Tac-Toe(2..9:3) -> #, $@Tic-Tac-Toe(3..9:3) -> #, |
|||
$@Tic-Tac-Toe([1,5,9]) -> #, $@Tic-Tac-Toe([3,5,7]) -> # |
|||
] -> \( |
|||
when <=[]?($@Tic-Tac-Toe <~[<position>]>)> do 'draw' ! |
|||
when <~=[]> do $(1) ! |
|||
\) ! |
|||
when <[<mark>+ VOID]?($ <[<=$(first)>+ VOID]>)> do '$(first); wins!'! |
|||
end isWonOrDone |
|||
source validMoves |
|||
$@Tic-Tac-Toe -> \[i](<position> $ !\) ! |
|||
end validMoves |
|||
templates move |
|||
when <?($@Tic-Tac-Toe($.position::raw) <position>)> do @Tic-Tac-Toe($.position::raw): $.mark; |
|||
$ ! |
|||
otherwise |
|||
'Incorrect move$#10;' -> !OUT::write |
|||
end move |
|||
source showString |
|||
'$:1..9:3 -> '$#10;$@Tic-Tac-Toe($..$+2)...;';$#10;' ! |
|||
end showString |
|||
end Tic-Tac-Toe |
|||
composer toInt |
|||
<INT> |
|||
end toInt |
|||
source play |
|||
def board: $Tic-Tac-Toe; |
|||
@: mark´'X'; |
|||
templates getMove |
|||
[] -> # |
|||
when <=[]> do |
|||
$board::showString -> !OUT::write |
|||
'$@play; to move $board::validMoves;:$#10;' -> !OUT::write |
|||
[{mark: $@play, position: $IN::readline -> toInt} -> board::move] -> # |
|||
otherwise |
|||
$(1) ! |
|||
end getMove |
|||
$getMove -> # |
|||
when <{}> do |
|||
'$.mark; played $.position;$#10;' -> !OUT::write |
|||
@: $@ -> \(<=mark´'X'> mark´'O'! <=mark´'O'> mark´'X' !\); |
|||
[$board::isWonOrDone] -> \( |
|||
when <=[]> do $getMove! |
|||
otherwise '$(1);$#10;' -> !OUT::write |
|||
\) -> # |
|||
end play |
|||
$play -> !VOID</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
123 |
|||
456 |
|||
789 |
|||
X to move [1, 2, 3, 4, 5, 6, 7, 8, 9]: |
|||
5 |
|||
X played 5 |
|||
123 |
|||
4X6 |
|||
789 |
|||
O to move [1, 2, 3, 4, 6, 7, 8, 9]: |
|||
4 |
|||
O played 4 |
|||
123 |
|||
OX6 |
|||
789 |
|||
X to move [1, 2, 3, 6, 7, 8, 9]: |
|||
1 |
|||
X played 1 |
|||
X23 |
|||
OX6 |
|||
789 |
|||
O to move [2, 3, 6, 7, 8, 9]: |
|||
9 |
|||
O played 9 |
|||
X23 |
|||
OX6 |
|||
78O |
|||
X to move [2, 3, 6, 7, 8]: |
|||
2 |
|||
X played 2 |
|||
XX3 |
|||
OX6 |
|||
78O |
|||
O to move [3, 6, 7, 8]: |
|||
8 |
|||
O played 8 |
|||
XX3 |
|||
OX6 |
|||
7OO |
|||
X to move [3, 6, 7]: |
|||
3 |
|||
X played 3 |
|||
X wins! |
|||
</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{trans|Python}} |
{{trans|Python}} |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.6 |
||
# This code splits the players from the core game engine |
# This code splits the players from the core game engine |
||
Line 1,477: | Line 15,818: | ||
# Assemble the pieces |
# Assemble the pieces |
||
set ttt [TicTacToe new HumanPlayer RandomRoboPlayer] |
set ttt [TicTacToe new HumanPlayer RandomRoboPlayer] |
||
$ttt game</ |
$ttt game</syntaxhighlight> |
||
Sample game: |
Sample game: |
||
<pre> |
<pre> |
||
Line 1,527: | Line 15,868: | ||
X wins across [1, 4, 7] |
X wins across [1, 4, 7] |
||
</pre> |
</pre> |
||
=={{header|UNIX Shell}}== |
|||
{{works with|Bourne Again SHell}} |
|||
The computer plays randomly. |
|||
<syntaxhighlight lang="sh">#!/usr/bin/env bash |
|||
# play tic-tac-toe |
|||
main() { |
|||
local play_again=1 |
|||
while (( play_again )); do |
|||
local board=(1 2 3 4 5 6 7 8 9) tokens=(X O) |
|||
show_board "${board[@]}" |
|||
# player colors: computer is red, human is green |
|||
local computer=31 human=32 |
|||
if (( RANDOM % 2 )); then |
|||
players=(computer human) |
|||
colors=($computer $human) |
|||
printf 'I go first!\n' |
|||
else |
|||
players=(human computer) |
|||
colors=($human $computer) |
|||
printf 'You go first!\n' |
|||
fi |
|||
local token winner |
|||
local -i turn=0 |
|||
while ! winner=$(winner "${board[@]}"); do |
|||
token=${tokens[turn%2]} |
|||
case "${players[turn%2]}" in |
|||
human) board=($(human_turn "$token" "${board[@]}"));; |
|||
computer) board=($(computer_turn "$token" "${board[@]}"));; |
|||
*) printf 'Unknown player "%s"\n' "${players[turn%2]}"; exit 1;; |
|||
esac |
|||
show_board "${board[@]}" | sed -e "s/${tokens[0]}/"$'\e['"${colors[0]}"$'m&\e[0m/g' \ |
|||
-e "s/${tokens[1]}/"$'\e['"${colors[1]}"$'m&\e[0m/g' |
|||
(( turn=turn+1 )) |
|||
done |
|||
case "$winner${players[0]}" in |
|||
Ohuman|Xcomputer) printf 'I win!\n';; |
|||
Xhuman|Ocomputer) printf 'You win!\n';; |
|||
cat*) printf 'The cat wins!\n';; |
|||
esac |
|||
yorn 'Play again' |
|||
play_again=$(( ! $? )) |
|||
done |
|||
} |
|||
show_board() { |
|||
printf '\n' |
|||
printf '%s %s %s\n' "$@" |
|||
printf '\n' |
|||
} |
|||
winner() { |
|||
local board=("$@") i j k |
|||
local lines=("0 1 2" "0 3 6" "0 4 8" "1 4 7" |
|||
"2 4 6" "2 5 8" "3 4 5" "6 7 8") |
|||
local line i j k |
|||
for line in "${lines[@]}"; do |
|||
read i j k <<<"$line" |
|||
local token=${board[i]} |
|||
if [[ "${board[j]}" == $token && "${board[k]}" == $token ]]; then |
|||
printf '%s\n' "$token" |
|||
return 0 |
|||
fi |
|||
done |
|||
case "${board[*]}" in |
|||
*[1-9]*) return 1;; |
|||
*) printf 'cat\n'; return 0;; |
|||
esac |
|||
} |
|||
human_turn() { |
|||
local token=$1 |
|||
shift |
|||
local board=("$@") |
|||
local n=0 |
|||
while (( n < 1 || n > 9 )) || [[ "${board[n-1]}" != $n ]]; do |
|||
printf 'Enter space number: ' "$n" "${board[n-1]}" >/dev/tty |
|||
read n </dev/tty >/dev/tty 2>&1 |
|||
done |
|||
board[n-1]=$token |
|||
printf '%s\n' "${board[@]}" |
|||
} |
|||
computer_turn() { |
|||
local token=$1 |
|||
shift |
|||
local board=("$@") |
|||
local i=0 blanks=() choice |
|||
for (( i=0; i<9; ++i )); do |
|||
if [[ "${board[i]}" == [1-9] ]]; then |
|||
blanks+=("$i") |
|||
fi |
|||
done |
|||
choice=${blanks[RANDOM % ${#blanks[@]}]} |
|||
board[choice]=$token |
|||
printf '%s\n' "${board[@]}" |
|||
} |
|||
yorn() { |
|||
local yorn= |
|||
while [[ $yorn != [Yy]* && $yorn != [Nn]* ]]; do |
|||
printf '%s? ' "$*" |
|||
read yorn |
|||
done |
|||
[[ $yorn == [Yy]* ]] |
|||
} |
|||
main "$@"</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
I go first! |
|||
I go in space 5. |
|||
1 2 3 |
|||
4 X 6 |
|||
7 8 9 |
|||
Enter space number: 1 |
|||
O 2 3 |
|||
4 X 6 |
|||
7 8 9 |
|||
I go in space 8. |
|||
O 2 3 |
|||
4 X 6 |
|||
7 X 9 |
|||
Enter space number: 2 |
|||
O O 3 |
|||
4 X 6 |
|||
7 X 9 |
|||
I go in space 7. |
|||
O O 3 |
|||
4 X 6 |
|||
X X 9 |
|||
Enter space number: 3 |
|||
O O O |
|||
4 X 6 |
|||
X X 9 |
|||
You win! |
|||
Play again? n |
|||
</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="v (vlang)"> |
|||
import rand |
|||
import os |
|||
__global ( |
|||
b = [3][3]int{} |
|||
best_i = 0 |
|||
best_j = 0 |
|||
) |
|||
fn main() { |
|||
mut user := false |
|||
mut yn :='' |
|||
for { |
|||
user = !user |
|||
print(game(user)) |
|||
for (yn != "y" ) && (yn != "n") { |
|||
yn = os.input('Play again y/n: ').str().to_lower() |
|||
} |
|||
if yn != "y" {exit(0)} else {yn =''} |
|||
println('') |
|||
} |
|||
} |
|||
fn game(user bool) string { |
|||
mut u := user |
|||
mut move, mut my_move, mut i, mut j, mut win := 0, 0, 0, 0, 0 |
|||
for k in 0..3 { |
|||
for l in 0..3 { |
|||
if b[k][l] != 0 {b[k][l] = 0} |
|||
} |
|||
} |
|||
print("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n") |
|||
print("You have O, I have X.\n\n") |
|||
for k in 1..9 { |
|||
if u { |
|||
for move !in [1,2,3,4,5,6,7,8,9] { |
|||
move = os.input('your move: ').int() |
|||
} |
|||
move-- |
|||
i = move / 3 |
|||
j = move % 3 |
|||
if b[i][j] != 0 {continue} |
|||
b[i][j] = 1 |
|||
move = 0 |
|||
} |
|||
if !u { |
|||
if k == 1 { |
|||
best_i = (rand.int_in_range(1, 9) or {exit(1)}) % 3 |
|||
best_j = (rand.int_in_range(1, 9) or {exit(1)}) % 3 |
|||
} |
|||
else {test_move(-1, 0)} |
|||
b[best_i][best_j] = -1 |
|||
my_move = best_i * 3 + best_j + 1 |
|||
println("My move: $my_move") |
|||
} |
|||
show_board() |
|||
win = check_winner() |
|||
println("win res: $win") |
|||
if win != 0 { |
|||
if win == 1 {return "You win" + ".\n\n"} |
|||
else {return "I win" + ".\n\n"} |
|||
} |
|||
u = !u |
|||
} |
|||
return "A draw.\n\n" |
|||
} |
|||
fn check_winner() int { |
|||
for i in 0..3 { |
|||
if b[i][0] != 0 && b[i][1] == b[i][0] && b[i][2] == b[i][0] {return b[i][0]} |
|||
if b[0][i] != 0 && b[1][i] == b[0][i] && b[2][i] == b[0][i] {return b[0][i]} |
|||
} |
|||
if b[1][1] == 0 {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 |
|||
} |
|||
fn show_board() { |
|||
mut t := "X O" |
|||
for i in 0..3 { |
|||
for j in 0..3 { |
|||
print("${t[b[i][j] + 1].ascii_str()} ") |
|||
} |
|||
println('') |
|||
} |
|||
println("-----") |
|||
} |
|||
fn test_move(value int, depth int) int { |
|||
mut best := -1 |
|||
mut changed := 0 |
|||
mut score := check_winner() |
|||
if score != 0 { |
|||
if score == value {return 1} else {return -1} |
|||
} |
|||
for i in 0..3 { |
|||
for j in 0..3 { |
|||
if b[i][j] != 0 {continue} |
|||
b[i][j] = value |
|||
changed = value |
|||
score = -test_move(-value, depth + 1) |
|||
b[i][j] = 0 |
|||
if score <= best {continue} |
|||
if depth == 0 { |
|||
best_i = i |
|||
best_j = j |
|||
} |
|||
best = score |
|||
} |
|||
} |
|||
if changed != 0 {return best} |
|||
return 0 |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
{{libheader|Wren-ioutil}} |
|||
<syntaxhighlight lang="wren">import "random" for Random |
|||
import "./ioutil" for Input |
|||
var r = Random.new() |
|||
var b = List.filled(3, null) |
|||
for (i in 0..2) b[i] = List.filled(3, 0) // board -> 0: blank; -1: computer; 1: human |
|||
var bestI = 0 |
|||
var bestJ = 0 |
|||
var checkWinner = Fn.new { |
|||
for (i in 0..2) { |
|||
if (b[i][0] != 0 && b[i][1] == b[i][0] && b[i][2] == b[i][0]) return b[i][0] |
|||
if (b[0][i] != 0 && b[1][i] == b[0][i] && b[2][i] == b[0][i]) return b[0][i] |
|||
} |
|||
if (b[1][1] == 0) 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 |
|||
} |
|||
var showBoard = Fn.new { |
|||
var t = "X O" |
|||
for (i in 0..2) { |
|||
for (j in 0..2) System.write("%(t[b[i][j] + 1]) ") |
|||
System.print() |
|||
} |
|||
System.print("-----") |
|||
} |
|||
var testMove // recursive |
|||
testMove = Fn.new { |value, depth| |
|||
var best = -1 |
|||
var changed = 0 |
|||
var score = checkWinner.call() |
|||
if (score != 0) return (score == value) ? 1 : -1 |
|||
for (i in 0..2) { |
|||
for (j in 0..2) { |
|||
if (b[i][j] == 0) { |
|||
b[i][j] = value |
|||
changed = value |
|||
score = -testMove.call(-value, depth + 1) |
|||
b[i][j] = 0 |
|||
if (score > best) { |
|||
if (depth == 0) { |
|||
bestI = i |
|||
bestJ = j |
|||
} |
|||
best = score |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return (changed != 0) ? best : 0 |
|||
} |
|||
var game = Fn.new { |u| |
|||
for (i in 0..2) { |
|||
for (j in 0..2) b[i][j] = 0 |
|||
} |
|||
System.print("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9") |
|||
System.print("You have O, I have X.\n") |
|||
for (k in 0..8) { |
|||
while (u) { |
|||
var move = Input.integer("Your move: ", 1, 9) - 1 |
|||
var i = (move/3).floor |
|||
var j = move % 3 |
|||
if (b[i][j] == 0) { |
|||
b[i][j] = 1 |
|||
break |
|||
} |
|||
} |
|||
if (!u) { |
|||
if (k == 0) { // randomize if computer opens, less boring |
|||
bestI = r.int(1e6) % 3 |
|||
bestJ = r.int(1e6) % 3 |
|||
} else { |
|||
testMove.call(-1, 0) |
|||
} |
|||
b[bestI][bestJ] = -1 |
|||
var myMove = bestI * 3 + bestJ + 1 |
|||
System.print("My move: %(myMove)") |
|||
} |
|||
showBoard.call() |
|||
var win = checkWinner.call() |
|||
if (win != 0) return ((win == 1) ? "You win" : "I win") + ".\n\n" |
|||
u = !u |
|||
} |
|||
return "A draw.\n\n" |
|||
} |
|||
var user = false |
|||
while (true) { |
|||
user = !user |
|||
System.write(game.call(user)) |
|||
var yn = Input.option("Play again y/n: ", "yYnN") |
|||
if (yn == "n" || yn == "N") return |
|||
System.print() |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
Sample game: |
|||
<pre> |
|||
Board postions are numbered so: |
|||
1 2 3 |
|||
4 5 6 |
|||
7 8 9 |
|||
You have O, I have X. |
|||
Your move: 1 |
|||
O |
|||
----- |
|||
My move: 5 |
|||
O |
|||
X |
|||
----- |
|||
Your move: 3 |
|||
O O |
|||
X |
|||
----- |
|||
My move: 2 |
|||
O X O |
|||
X |
|||
----- |
|||
Your move: 8 |
|||
O X O |
|||
X |
|||
O |
|||
----- |
|||
My move: 4 |
|||
O X O |
|||
X X |
|||
O |
|||
----- |
|||
Your move: 6 |
|||
O X O |
|||
X X O |
|||
O |
|||
----- |
|||
My move: 9 |
|||
O X O |
|||
X X O |
|||
O X |
|||
----- |
|||
Your move: 7 |
|||
O X O |
|||
X X O |
|||
O O X |
|||
----- |
|||
A draw. |
|||
Play again y/n: n |
|||
</pre> |
|||
=={{header|XPL0}}== |
|||
[[File:TTTXPL0.GIF|right]] |
|||
<syntaxhighlight lang="xpl0">\The computer marks its moves with an "O" and the player uses an "X". The |
|||
\ numeric keypad is used to make the player's move. |
|||
\ |
|||
\ 7 | 8 | 9 |
|||
\ ---+---+--- |
|||
\ 4 | 5 | 6 |
|||
\ ---+---+--- |
|||
\ 1 | 2 | 3 |
|||
\ |
|||
\The player always goes first, but the 0 key is used to skip a move. Thus |
|||
\ it can be used to let the computer play first. Esc terminates program. |
|||
inc c:\cxpl\codes; \intrinsic routine declarations |
|||
def X0=16, Y0=10; \coordinates of character in upper-left square |
|||
int I0, |
|||
PMove, \player's move (^0..^9) |
|||
Key; \keystroke |
|||
int X, O; \bit arrays for player and computer |
|||
\ bit 0 corresponds to playing square 1, etc. |
|||
proc HLine(X, Y); \Draw a horizontal line |
|||
int X, Y; |
|||
int I; |
|||
[Cursor(X, Y); |
|||
for I:= 0 to 10 do ChOut(0, ^Ä); |
|||
]; \HLine |
|||
proc VLine(X, Y); \Draw a vertical line over the above horizontal line |
|||
int X, Y; |
|||
int I; |
|||
[for I:= 0 to 4 do |
|||
[Cursor(X, Y+I); |
|||
ChOut(0, if I&1 then ^Å else ^³); |
|||
]; |
|||
]; \VLine |
|||
func Won(p); \Return 'true' if player P has won |
|||
int P; |
|||
int T, I; |
|||
[T:= [$007, $038, $1C0, $049, $092, $124, $111, $054]; |
|||
for I:= 0 to 7 do \check if player matches a bit pattern for 3 in a row |
|||
if (P & T(I)) = T(I) then return true; |
|||
return false; |
|||
]; \Won |
|||
func Cats; \Return 'true' if no more moves available (Cat's game) |
|||
[if (X ! O) = $1FF then \all bit positions played |
|||
[Cursor(17, 20); |
|||
Text(0, "A draw!"); |
|||
return true; |
|||
]; |
|||
return false; |
|||
]; \Cats |
|||
proc DoMove(P, M, Ch); \Make move in player's bit array and display it |
|||
int P, \address of player's bit array |
|||
M, \index 0..8 where bit is placed |
|||
Ch; |
|||
int I, X, Y; |
|||
[P(0):= P(0) ! 1<<M; \make move |
|||
I:= M / 3; \display move |
|||
X:= Rem(0) * 4; |
|||
Y:= (2-I) * 2; |
|||
Cursor(X+X0, Y+Y0); |
|||
ChOut(0, Ch); |
|||
]; \DoMove |
|||
func Try(P); \Return the value of the best node for player P |
|||
int P; \address of player's bit array |
|||
int P1, I, I0, V, V0; |
|||
[P1:= if P = addr X then addr O else addr X; |
|||
if Won(P1(0)) then return -1; |
|||
if (X ! O) = $1FF then return 0; |
|||
V0:= -1; \assume the worst |
|||
for I:= 0 to 8 do \for all of the squares... |
|||
if ((O!X) & 1<<I) = 0 then \if square is unused |
|||
[P(0):= P(0) ! 1<<I; \make tenative move |
|||
V:= -(extend(Try(P1))); \get value |
|||
if V > V0 then \save best value |
|||
[V0:= V; I0:= I]; |
|||
P(0):= P(0) & ~(1<<I); \undo tenative move |
|||
]; |
|||
return V0 & $FF ! I0<<8; |
|||
]; \Try |
|||
proc PlayGame; \Play one game |
|||
[ChOut(0, $0C\FF\); \clear screen with a form feed |
|||
HLine(X0-1, Y0+1); \draw grid (#) |
|||
HLine(X0-1, Y0+3); |
|||
VLine(X0+2, Y0); |
|||
VLine(X0+6, Y0); |
|||
X:= 0; O:= 0; \initialize player's bit arrays to empty |
|||
loop [loop [PMove:= ChIn(1); \GET PLAYER'S MOVE (X) |
|||
if PMove = $1B\Esc\ then |
|||
[SetVid(3); exit]; \restore display and end program |
|||
if PMove = ^0 then quit; |
|||
if PMove>=^1 & PMove<=^9 & \check for legal move |
|||
((X!O) & 1<<(PMove-^1)) = 0 then quit; |
|||
ChOut(0, 7\Bel\); \beep the dude |
|||
]; |
|||
if PMove # ^0 then |
|||
[DoMove(addr X, PMove-^1, ^X); |
|||
if Won(X) then |
|||
[Cursor(17, 20); |
|||
Text(0, "X wins!"); |
|||
quit; |
|||
]; |
|||
]; |
|||
if Cats then quit; |
|||
I0:= Try(addr O) >>8; \GET COMPUTER'S MOVE (O) |
|||
DoMove(addr O, I0, ^O); \do best move |
|||
if Won(O) then |
|||
[Cursor(17, 20); |
|||
Text(0, "O wins!"); |
|||
quit; |
|||
]; |
|||
if Cats then quit; |
|||
]; |
|||
]; \PlayGame |
|||
int CpuReg; |
|||
[SetVid(1); \set 40x25 text mode |
|||
CpuReg:= GetReg; \turn off annoying flashing cursor |
|||
CpuReg(0):= $0100; \ with BIOS interrupt 10h, function 01h |
|||
CpuReg(2):= $2000; \set cursor type to disappear |
|||
SoftInt($10); |
|||
loop [PlayGame; |
|||
Key:= ChIn(1); \keep playing games until Esc key is hit |
|||
if Key = $1B\Esc\ then |
|||
[SetVid(3); exit]; \clear screen & restore normal text mode |
|||
]; |
|||
]</syntaxhighlight> |
|||
{{omit from|GUISS}} |