Tic-tac-toe: Difference between revisions

20,500 bytes added ,  1 month ago
m
Fixed the name of the GitHub repo of the PostScript version
m (Fixed the name of the GitHub repo of the PostScript version)
 
(10 intermediate revisions by 6 users not shown)
Line 10:
 
''Tic-tac-toe''   is also known as:
::*   ''naughtsnoughts and crosses''
::*   ''tic tac toe''
::*   ''tick tack toe''
Line 4,445:
 
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}}==
Line 4,450 ⟶ 4,741:
It uses minimax with alpha-beta pruning. Therefore, the computer never loses.
 
[https://easylang.onlinedev/apps/tictactoe.html Run it]
 
<syntaxhighlight lang="text">
len f[] 9
state = 0
textsize 14
#
funcproc init . .
linewidth 2
clear
Line 4,477 ⟶ 4,768:
.
.
funcproc draw ind . .
c = (ind - 1) mod 3
r = (ind - 1) div 3
Line 4,496 ⟶ 4,787:
.
.
funcproc sum3 a d . st .
for i = 1 to 3
s += f[a]
Line 4,507 ⟶ 4,798:
.
.
funcproc rate . res done .
res = 0
for i = 1 step 3 to 7
call sum3 i 1 res
.
for i = 1 to 3
call sum3 i 3 res
.
call sum3 1 4 res
call sum3 3 2 res
cnt = 1
for i = 1 to 9
Line 4,529 ⟶ 4,820:
.
.
funcproc minmax player alpha beta . rval rmov .
call rate rval done
if done = 1
if player = 1
Line 4,537 ⟶ 4,828:
else
rval = alpha
start = randomrandint 9
mov = start
repeat
if f[mov] = 0
f[mov] = player
call minmax (5 - player) (-beta) (-rval) val h
val = -val
f[mov] = 0
Line 4,555 ⟶ 4,846:
.
.
funcproc show_result val . .
color 555
move 16 4
Line 4,568 ⟶ 4,859:
state += 2
.
funcproc computer . .
call minmax 4 -11 11 val mov
f[mov] = 4
call draw mov
call rate val done
state = 0
if done = 1
call show_result val
.
.
funcproc human . .
mov = floor ((mouse_x - 6) / 28) + 3 * floor ((mouse_y - 16) / 28) + 1
if f[mov] = 0
f[mov] = 1
call draw mov
state = 1
timer 0.5
Line 4,588 ⟶ 4,879:
.
on timer
call rate val done
if done = 1
call show_result val
else
call computer
.
.
Line 4,598 ⟶ 4,889:
if state = 0
if mouse_x > 6 and mouse_x < 90 and mouse_y > 16
call human
.
elif state >= 2
state -= 2
call init
.
.
call init
</syntaxhighlight>
 
Line 8,601 ⟶ 8,892:
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}}==
Line 11,382 ⟶ 12,085:
+---+---+---+
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}}==
Line 15,243 ⟶ 15,981:
{{trans|Kotlin}}
{{libheader|Wren-ioutil}}
<syntaxhighlight lang="ecmascriptwren">import "random" for Random
import "./ioutil" for Input
 
var r = Random.new()
2

edits