305 lines
7.6 KiB
Plaintext
305 lines
7.6 KiB
Plaintext
{ App to prove you can't win at Tic-Tac-Toe }
|
|
{ Written to target MT+86 - Pascal V3.1.1 }
|
|
|
|
program ttt;
|
|
|
|
const
|
|
scoreWin = 6;
|
|
scoreTie = 5;
|
|
scoreLose = 4;
|
|
scoreMax = 9;
|
|
scoreMin = 2;
|
|
scoreInvalid = 0;
|
|
|
|
pieceBlank = 0;
|
|
pieceX = 1;
|
|
pieceO = 2;
|
|
|
|
iterations = 1;
|
|
|
|
type
|
|
boardType = array[ 0..8 ] of integer;
|
|
PSTRING = ^STRING;
|
|
|
|
var
|
|
evaluated: integer;
|
|
board: boardType;
|
|
|
|
var
|
|
i, loops, code: integer;
|
|
startTicks, endTicks, elapsedTicks : longint;
|
|
|
|
external function @cmd : PSTRING;
|
|
external procedure @GETTIME( var hour,minute,second,hund : integer );
|
|
|
|
procedure dumpBoard;
|
|
var
|
|
i : integer;
|
|
begin
|
|
Write( '{' );
|
|
for i := 0 to 8 do
|
|
Write( board[i] );
|
|
Write( '}' );
|
|
end;
|
|
|
|
function lookForWinner : integer;
|
|
var
|
|
t, p : integer;
|
|
begin
|
|
{dumpBoard;}
|
|
p := pieceBlank;
|
|
t := board[ 0 ];
|
|
if pieceBlank <> t then
|
|
begin
|
|
if ( ( ( t = board[1] ) and ( t = board[2] ) ) or
|
|
( ( t = board[3] ) and ( t = board[6] ) ) ) then
|
|
p := t;
|
|
end;
|
|
|
|
if pieceBlank = p then
|
|
begin
|
|
t := board[1];
|
|
if ( t = board[4] ) and ( t = board[7] ) then
|
|
p := t
|
|
else
|
|
begin
|
|
t := board[2];
|
|
if ( t = board[5] ) and ( t = board[8] ) then
|
|
p := t
|
|
else
|
|
begin
|
|
t := board[3];
|
|
if ( t = board[4] ) and ( t = board[5] ) then
|
|
p := t
|
|
else
|
|
begin
|
|
t := board[6];
|
|
if ( t = board[7] ) and ( t = board[8] ) then
|
|
p := t
|
|
else
|
|
begin
|
|
t := board[4];
|
|
if ( ( ( t = board[0] ) and ( t = board[8] ) ) or
|
|
( ( t = board[2] ) and ( t = board[6] ) ) ) then
|
|
p := t
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
lookForWinner := p;
|
|
end;
|
|
|
|
function winner2( move: integer ) : integer;
|
|
var
|
|
x : integer;
|
|
begin
|
|
case move of
|
|
0: begin
|
|
x := board[ 0 ];
|
|
if not ( ( ( x = board[1] ) and ( x = board[2] ) ) or
|
|
( ( x = board[3] ) and ( x = board[6] ) ) or
|
|
( ( x = board[4] ) and ( x = board[8] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
1: begin
|
|
x := board[ 1 ];
|
|
if not ( ( ( x = board[0] ) and ( x = board[2] ) ) or
|
|
( ( x = board[4] ) and ( x = board[7] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
2: begin
|
|
x := board[ 2 ];
|
|
if not ( ( ( x = board[0] ) and ( x = board[1] ) ) or
|
|
( ( x = board[5] ) and ( x = board[8] ) ) or
|
|
( ( x = board[4] ) and ( x = board[6] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
3: begin
|
|
x := board[ 3 ];
|
|
if not ( ( ( x = board[4] ) and ( x = board[5] ) ) or
|
|
( ( x = board[0] ) and ( x = board[6] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
4: begin
|
|
x := board[ 4 ];
|
|
if not ( ( ( x = board[0] ) and ( x = board[8] ) ) or
|
|
( ( x = board[2] ) and ( x = board[6] ) ) or
|
|
( ( x = board[1] ) and ( x = board[7] ) ) or
|
|
( ( x = board[3] ) and ( x = board[5] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
5: begin
|
|
x := board[ 5 ];
|
|
if not ( ( ( x = board[3] ) and ( x = board[4] ) ) or
|
|
( ( x = board[2] ) and ( x = board[8] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
6: begin
|
|
x := board[ 6 ];
|
|
if not ( ( ( x = board[7] ) and ( x = board[8] ) ) or
|
|
( ( x = board[0] ) and ( x = board[3] ) ) or
|
|
( ( x = board[4] ) and ( x = board[2] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
7: begin
|
|
x := board[ 7 ];
|
|
if not ( ( ( x = board[6] ) and ( x = board[8] ) ) or
|
|
( ( x = board[1] ) and ( x = board[4] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
8: begin
|
|
x := board[ 8 ];
|
|
if not ( ( ( x = board[6] ) and ( x = board[7] ) ) or
|
|
( ( x = board[2] ) and ( x = board[5] ) ) or
|
|
( ( x = board[0] ) and ( x = board[4] ) ) )
|
|
then x := PieceBlank;
|
|
end;
|
|
end;
|
|
|
|
winner2 := x;
|
|
end;
|
|
|
|
function minmax( alpha: integer; beta: integer; depth: integer; move: integer ): integer;
|
|
var
|
|
p, value, pieceMove, score : integer;
|
|
begin
|
|
evaluated := evaluated + 1;
|
|
value := scoreInvalid;
|
|
if depth >= 4 then
|
|
begin
|
|
p := winner2( move ); { lookForWinner; }
|
|
if p <> pieceBlank then
|
|
begin
|
|
if p = pieceX then
|
|
value := scoreWin
|
|
else
|
|
value := scoreLose
|
|
end
|
|
else if depth = 8 then
|
|
value := scoreTie;
|
|
end;
|
|
|
|
if value = scoreInvalid then
|
|
begin
|
|
if Odd( depth ) then
|
|
begin
|
|
value := scoreMin;
|
|
pieceMove := pieceX;
|
|
end
|
|
else
|
|
begin
|
|
value := scoreMax;
|
|
pieceMove := pieceO;
|
|
end;
|
|
|
|
p := 0;
|
|
repeat
|
|
if board[ p ] = pieceBlank then
|
|
begin
|
|
board[ p ] := pieceMove;
|
|
score := minmax( alpha, beta, depth + 1, p );
|
|
board[ p ] := pieceBlank;
|
|
|
|
if Odd( depth ) then
|
|
begin
|
|
if ( score > value ) then
|
|
begin
|
|
value := score;
|
|
if ( value = scoreWin ) or ( value >= beta ) then p := 10
|
|
else if ( value > alpha ) then alpha := value;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if ( score < value ) then
|
|
begin
|
|
value := score;
|
|
if ( value = scoreLose ) or ( value <= alpha ) then p := 10
|
|
else if ( value < beta ) then beta := value;
|
|
end;
|
|
end;
|
|
end;
|
|
p := p + 1;
|
|
until p > 8;
|
|
end;
|
|
|
|
minmax := value;
|
|
end;
|
|
|
|
function argAsInt : integer;
|
|
var
|
|
offset, x, len, result : integer;
|
|
CommandString : STRING[ 127 ];
|
|
PTR : PSTRING;
|
|
begin
|
|
result := 0;
|
|
PTR := @CMD;
|
|
CommandString := PTR^;
|
|
len := ORD( CommandString[ 0 ] );
|
|
if 0 <> len then
|
|
begin
|
|
offset := 2;
|
|
x := ORD( CommandString[ 2 ] );
|
|
while ( ( x >= 48 ) and ( x <= 57 ) ) do
|
|
begin
|
|
result := result * 10;
|
|
result := result + x - 48;
|
|
offset := offset + 1;
|
|
x := ORD( CommandString[ offset ] );
|
|
end;
|
|
end;
|
|
|
|
argAsInt := result;
|
|
end;
|
|
|
|
function tickCount : longint;
|
|
var
|
|
h, m, s, hu : integer;
|
|
lh, lm, ls, lhu, result : longint;
|
|
begin
|
|
@gettime( h, m, s, hu );
|
|
lh := long( h );
|
|
lm := long( m );
|
|
ls := long( s );
|
|
lhu := long( hu );
|
|
result := lhu + ( ls * #100 ) + ( lm * #6000 ) + ( lh * #360000 );
|
|
tickCount := result;
|
|
end;
|
|
|
|
procedure runit( move : integer );
|
|
var
|
|
score: integer;
|
|
begin
|
|
board[move] := pieceX;
|
|
score := minmax( scoreMin, scoreMax, 0, move );
|
|
board[move] := pieceBlank;
|
|
end;
|
|
|
|
begin
|
|
loops := argAsInt;
|
|
if 0 = loops then loops := Iterations;
|
|
|
|
WriteLn( 'begin' );
|
|
|
|
for i := 0 to 8 do
|
|
board[i] := pieceBlank;
|
|
|
|
startTicks := tickCount;
|
|
|
|
for i := 1 to loops do
|
|
begin
|
|
evaluated := 0; { once per loop to prevent overflow }
|
|
runit( 0 );
|
|
runit( 1 );
|
|
runit( 4 );
|
|
end;
|
|
|
|
endTicks := tickCount;
|
|
elapsedTicks := endTicks - startTicks;
|
|
WriteLn( 'hundredths of a second: ', short( elapsedTicks ) );
|
|
WriteLn( 'moves evaluated: ', evaluated );
|
|
WriteLn( 'iterations: ', loops );
|
|
end.
|