Tic-tac-toe: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
(→{{header|Pascal}}: Changed the input loop lest the program accept the move 0 or a move to assigned position. In addition, formatted the code according to JEDI Code Format.) |
||
Line 9,582: | Line 9,582: | ||
I would expect this version should compile with most Pascal variants including Delphi, but YMMR. |
I would expect this version should compile with most Pascal variants including Delphi, but YMMR. |
||
<syntaxhighlight lang="pascal">program |
<syntaxhighlight lang="pascal">program Tic(Input, Output); |
||
type |
type |
||
Contents = (Unassigned, Human, Computer); |
|||
var |
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 |
procedure DisplayBoard; |
||
var |
var |
||
I, J: integer; |
|||
T: array [Contents] of char; |
|||
begin |
begin |
||
T[Unassigned] := ' '; |
|||
T[Human] := 'O'; |
|||
T[Computer] := 'X'; |
|||
for |
for I := 0 to 2 do |
||
begin |
|||
for J := 0 to 2 do |
|||
begin |
|||
if j <> 2 then write(' | '); |
|||
Write(T[B[I, J]]); |
|||
if J <> 2 then |
|||
Write(' | '); |
|||
end; |
|||
WriteLn; |
|||
if I < 2 then |
|||
WriteLn('---------'); |
|||
end; |
end; |
||
WriteLn; |
|||
WriteLn; |
|||
end; |
end; |
||
function SwapPlayer(Player: Contents): Contents; |
|||
begin |
begin |
||
if Player = Computer then |
if Player = Computer then |
||
SwapPlayer := Human |
|||
end; |
|||
else |
|||
SwapPlayer := Computer; |
|||
end; |
|||
function |
function CheckWinner: Contents; |
||
var |
var |
||
I: integer; |
|||
begin |
begin |
||
CheckWinner := Unassigned; { no winner yet } |
|||
for |
for I := 0 to 2 do |
||
begin |
|||
(* first horizontal solution *) |
|||
{ 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 |
|||
if (CheckWinner = Unassigned) and (B[I, 0] <> Unassigned) and |
|||
checkWinner := b[i][0] |
|||
(B[I, 1] = B[I, 0]) and (B[I, 2] = B[I, 0]) then |
|||
else |
|||
CheckWinner := B[I, 0] |
|||
else |
|||
if ((checkWinner = Unassigned) and (b[0][i] <> Unassigned)) and ((b[1][i] = b[0][i]) and (b[2][i] = b[0][i])) then |
|||
{ now vertical solution } |
|||
checkWinner := b[0][i]; |
|||
if (CheckWinner = Unassigned) and (B[0, I] <> Unassigned) and |
|||
end; (* now check the paths of the two cross line slants that share the middle position *) |
|||
(B[1, I] = B[0, I]) and (B[2, I] = B[0, I]) then |
|||
CheckWinner := B[0, I]; |
|||
if ((b[1][1] = b[0][0]) and (b[2][2] = b[0][0])) then checkWinner := b[0][0] |
|||
end; |
|||
else if ((b[1][1] = b[2][0]) and (b[0][2] = b[1][1])) then checkWinner := b[1][1]; |
|||
{ 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; |
||
end; |
end; |
||
{ Basic strategy test - is this te best solution we have seen } |
{ Basic strategy test - is this te best solution we have seen } |
||
function |
function SaveBest(CurScore, CurBest: Contents): boolean; |
||
begin |
begin |
||
if CurScore = CurBest then |
if CurScore = CurBest then |
||
SaveBest := False |
|||
else if (Curscore = Unassigned) and (CurBest = Human) then saveBest := false |
|||
else if ( |
else if (CurScore = Unassigned) and (CurBest = Human) then |
||
SaveBest := False |
|||
else if (CurScore = Computer) and ((CurBest = Unassigned) or |
|||
end; |
|||
(CurBest = Human)) then |
|||
SaveBest := False |
|||
else |
|||
SaveBest := True; |
|||
end; |
|||
{ Basic strategy - recursive depth first |
{ Basic strategy - recursive depth first search of possible moves |
||
if computer can win save it, otherwise block if need be, else do deeper. |
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, |
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. } |
by remembering the modified position on the call stack. } |
||
function TestMove(Val: Contents; Depth: integer): Contents; |
|||
var |
var |
||
I, J: integer; |
|||
Score, Best, Changed: Contents; |
|||
begin |
begin |
||
Best := Computer; |
|||
Changed := Unassigned; |
|||
Score := CheckWinner; |
|||
if |
if Score <> Unassigned then |
||
begin |
|||
if score = val then test_move := Human else test_move := Computer; |
|||
if Score = Val then |
|||
end else begin |
|||
TestMove := Human |
|||
else |
|||
if b[i][j] = Unassigned then begin |
|||
TestMove := Computer; |
|||
end |
|||
b[i][j] := val; (* the value for now and try wioth the other player *) |
|||
else |
|||
score := test_move(swapPlayer(val), depth + 1); |
|||
begin |
|||
if (score <> Unassigned) then score := swapPlayer(score); |
|||
for I := 0 to 2 do |
|||
for J := 0 to 2 do |
|||
begin |
|||
if depth = 0 then begin { top level, so remember actual position } |
|||
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; |
||
best := score; |
|||
end; |
end; |
||
end; |
end; |
||
if Changed <> Unassigned then |
|||
end; |
|||
TestMove := Best |
|||
if (changed <> Unassigned) then test_move := best else test_move := Unassigned; |
|||
else |
|||
TestMove := Unassigned; |
|||
end; |
end; |
||
end; |
end; |
||
function |
function PlayGame(Whom: Contents): string; |
||
var |
var |
||
I, J, K, Move: integer; |
|||
Win: Contents; |
|||
begin |
begin |
||
Win := Unassigned; |
|||
for |
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('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(); |
|||
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 |
begin |
||
BestI := Random(3); |
|||
BestJ := Random(3); |
|||
end |
|||
else |
|||
Win := TestMove(Computer, 0); |
|||
if (move < 1) or (move > 9) then writeln('Opps: enter a number between 1 - 9') |
|||
B[BestI, BestJ] := Computer; |
|||
else move := pred(move); {humans do 1 -9, but the computer wants 0-8 for modulus to work} |
|||
WriteLn('My move: ', BestI * 3 + BestJ + 1); |
|||
end; |
|||
i := move div 3; { convert from range to corridinated of the array } |
|||
DisplayBoard; |
|||
Win := CheckWinner; |
|||
if b[i][j] = Unassigned then b[i][j] := Human; |
|||
if Win <> Unassigned then |
|||
begin |
|||
if Win = Human then |
|||
(* randomize if computer opens, so its not always the same game *) |
|||
PlayGame := 'You win.' |
|||
else |
|||
best_i := random(3); {Pascal random returns positive integers from 0 to func arg} |
|||
PlayGame := 'I win.'; |
|||
end |
|||
else |
|||
win := test_move(Computer, 0); |
|||
begin |
|||
b[best_i][best_j] := Computer; |
|||
Inc(K); { "for" loop counter actions } |
|||
Whom := SwapPlayer(Whom); |
|||
end; |
|||
until (Win <> Unassigned) or (K > 9); |
|||
if Win = Unassigned then |
|||
win := checkWinner(); |
|||
PlayGame := 'A draw.'; |
|||
if win <> Unassigned then begin |
|||
end; |
|||
if win = Human then playGame := 'You win.' else playGame := 'I win.'; |
|||
end else begin |
|||
k := succ(k); { "for" loop counter actions } |
|||
whom := swapPlayer(whom); |
|||
end; |
|||
end; |
|||
until (win <> Unassigned) or (k > 9); |
|||
if win = Unassigned then playGame := 'A draw.'; |
|||
end; |
|||
begin |
begin |
||
Randomize; |
|||
randomize(); |
|||
Player := Human; |
|||
while True do |
|||
begin |
|||
WriteLn(PlayGame(Player)); |
|||
WriteLn; |
|||
Player := SwapPlayer(Player); |
|||
end |
|||
end.</syntaxhighlight> |
end.</syntaxhighlight> |
||