TIC TAC TOE

28
maximizing('x'). %%% the player playing x is always trying to maximize the utility of the board position minimizing('o'). %%% the player playing o is always trying to minimize the utility of the board position corner_square(1, 1). %%% map corner squares to board squares corner_square(2, 3). corner_square(3, 7). corner_square(4, 9). run :- hello, %%% Display welcome message, initialize game play(1), %%% Play the game starting with player 1 goodbye. %%% Display end of game message run :- goodbye. hello :- initialize, cls, nl, nl, nl, write('Welcome to Tic-Tac-Toe.'), read_players, output_players. initialize :- random_seed, %%% use current time to initialize random number generator blank_mark(E), asserta( board([E,E,E, E,E,E, E,E,E]) ). %%% create a blank board

Transcript of TIC TAC TOE

maximizing('x'). minimizing('o').

%%% the player playing x is always trying to maximize the utility of the board position %%% the player playing o is always trying to minimize the utility of the board position

corner_square(1, 1). %%% map corner squares to board squares corner_square(2, 3). corner_square(3, 7). corner_square(4, 9). run :hello, play(1), goodbye. run :goodbye. hello :initialize, cls, nl, nl, nl, write('Welcome to Tic-Tac-Toe.'), read_players, output_players. initialize :random_seed, blank_mark(E), asserta( board([E,E,E, E,E,E, E,E,E]) ). %%% create a blank board %%% use current time to initialize random number generator %%% Display welcome message, initialize game %%% Play the game starting with player 1 %%% Display end of game message

goodbye :board(B), nl,

nl, write('Game over: '), output_winner(B), retract(board(_)), retract(player(_,_)), read_play_again(V), !, (V == 'Y' ; V == 'y'), !, run. 8 read_play_again(V) :nl, nl, write('Play again (Y/N)? '), read(V), (V == 'y' ; V == 'Y' ; V == 'n' ; V == 'N'), !. read_play_again(V) :nl, nl, write('Please enter Y or N.'), read_play_again(V). read_players :nl, nl, write('Number of human players? '), read(N), ayers(0) :asserta( player(1, computer) ), asserta( player(2, computer) ), !. set_players(1) :nl,

write('Is human playing X or O (X moves first)? '), read(M), human_playing(M), !. set_players(2) :asserta( player(1, human) ), asserta( player(2, human) ), !. set_players(N) :nl, write('Please enter 0, 1, or 2.'), read_players. human_playing(M) :(M == 'x' ; M == 'X'), asserta( player(1, human) ), asserta( player(2, computer) ), !. human_playing(M) :(M == 'o' ; M == 'O'), asserta( player(1, computer) ), asserta( player(2, human) ), !. human_playing(M) :nl, write('Please enter X or O.'), set_players(1). play(P) :board(B), !, output_board(B), !, not(game_over(P, B)), !, make_move(P, B), !, next_player(P, P2), !, play(P2), !.

215 %.......................................

216 % square

217 %.......................................

218 % The mark in a square(N) corresponds to an item in a list, as follows:

219

220 square([M,_,_,_,_,_,_,_,_],1,M).

221 square([_,M,_,_,_,_,_,_,_],2,M).

222 square([_,_,M,_,_,_,_,_,_],3,M).

223 square([_,_,_,M,_,_,_,_,_],4,M).

224 square([_,_,_,_,M,_,_,_,_],5,M).

225 square([_,_,_,_,_,M,_,_,_],6,M).

226 square([_,_,_,_,_,_,M,_,_],7,M).

227 square([_,_,_,_,_,_,_,M,_],8,M).

228 square([_,_,_,_,_,_,_,_,M],9,M).

231 %.......................................

232 % win

233 %.......................................

234 % Players win by having their mark in one of the following square configurations:

235 % win([M,M,M, _,_,_, _,_,_],M). win([_,_,_, M,M,M, _,_,_],M). win([_,_,_, _,_,_, M,M,M],M). win([M,_,_, M,_,_, M,_,_],M). win([_,M,_, _,M,_, _,M,_],M). win([_,_,M, _,_,M, _,_,M],M). win([M,_,_, _,M,_, _,_,M],M). win([_,_,M, _,M,_, M,_,_],M).

247 %.......................................

248 % move

249 %.......................................

250 % applies a move on the given board

251 % (put mark M in square S on board B and return the resulting board B2)

252 % move(B,S,M,B2) :set_item(B,S,M,B2).

259 %.......................................

260 % game_over

261 %.......................................

262 % determines when the game is over

263 % game_over(P, B) :game_over2(P, B). game_over2(P, B) :opponent_mark(P, M), %%% game is over if opponent wins win(B, M). game_over2(P, B) :blank_mark(E), not(square(B,S,E)). %%% game is over if opponent wins

279 %.......................................

280 % make_move

281 %.......................................

282 % requests next move from human/computer,

283 % then applies that move to the given board

284 %

make_move(P, B) :player(P, Type), make_move2(Type, P, B, B2),

retract( board(_) ), asserta( board(B2) ). make_move2(human, P, B, B2) :nl, nl, write('Player '), write(P), write(' move? '), read(S),

blank_mark(E), square(B, S, E), player_mark(P, M), move(B, S, M, B2), !.

make_move2(human, P, B, B2) :nl, nl, write('Please select a numbered square.'), make_move2(human,P,B,B2). make_move2(computer, P, B, B2) :nl, nl, write('Computer is thinking about next move...'), player_mark(P, M), minimax(0, B, M, S, U),

move(B,S,M,B2),

nl,

nl,

write('Computer places '),

write(M),

write(' in square '),

write(S),

write('.').

334 %.......................................

335 % moves

336 %.......................................

337 % retrieves a list of available moves (empty squares) on a board.

338 % moves(B,L) :not(win(B,x)), %%% if either player already won, then there are no available moves

not(win(B,o)), blank_mark(E), findall(N, square(B,N,E), L), L \= [].

349 %.......................................

350 % utility

351 %.......................................

352 % determines the value of a given board position

353 % utility(B,U) :win(B,'x'), U = 1,!. utility(B,U) :win(B,'o'), U = (-1), !. utility(B,U) :U = 0.

372 %.......................................

373 % minimax

374 %.......................................

375 % The minimax algorithm always assumes an optimal opponent.

376 % For tic-tac-toe, optimal play will always result in a tie, so the algorithm is effectively playing not-to-lose.

377

378 % For the opening move against an optimal player, the best minimax can ever hope for is a tie.

379 % So, technically speaking, any opening move is acceptable.

380 % Save the user the trouble of waiting for the computer to search the entire minimax tree

381 % by simply selecting a random square. minimax(D,[E,E,E, E,E,E, E,E,E],M,S,U) :blank_mark(E), random_int_1n(9,S), !. minimax(D,B,M,S,U) :D2 is D + 1, moves(B,L), !, %%% get the list of available moves

best(D2,B,M,L,S,U), %%% recursively determine the best available move !.

397 % if there are no more available moves,

398 % then the minimax value is the utility of the given board position minimax(D,B,M,S,U) :utility(B,U).

405 %.......................................

406 % best

407 %.......................................

408 % determines the best move in a given list of moves by recursively calling minimax

409 %

410

411 % if there is only one move left in the list... best(D,B,M,[S1],S,U) :move(B,S1,M,B2), inverse_mark(M,M2), !, minimax(D,B2,M2,_S,U), %%% then recursively search for the utility value of that move. S = S1, !, output_value(D,S,U), %%% apply that move to the board,

420

!.

423 % if there is more than one move in the list... best(D,B,M,[S1|T],S,U) :move(B,S1,M,B2), inverse_mark(M,M2), !, minimax(D,B2,M2,_S,U1), %%% recursively search for the utility value of that move, %%% apply the first move (in the list) to the board,

best(D,B,M,T,S2,U2), output_value(D,S1,U1),

%%% determine the best move of the remaining moves,

better(D,M,S1,U1,S2,U2,S,U) %%% and choose the better of the two moves (based on their respective utility values).

436 %.......................................

437 % better

438 %.......................................

439 % returns the better of two moves based on their respective utility values.

440 %

441 % if both moves have the same utility value, then one is chosen at random.

better(D,M,S1,U1,S2,U2, maximizing(M), U1 > U2, S = S1, U = U1, !. better(D,M,S1,U1,S2,U2, minimizing(M), U1 < U2, S = S1, U = U1, !. better(D,M,S1,U1,S2,U2, U1 == U2,

S,U) :%%% if the player is maximizing %%% then greater is better.

S,U) :%%% if the player is minimizing, %%% then lesser is better.

S,U) :%%% if moves have equal utility,

random_int_1n(10,R),

%%% then pick one of them at random

better2(D,R,M,S1,U1,S2,U2,S,U), !.

466 better(D,M,S1,U1,S2,U2, S = S2, U = U2, !.

S,U) :-

%%% otherwise, second move is better

473 %.......................................

474 % better2

475 %.......................................

476 % randomly selects two squares of the same utility value given a single probability

477 % 9 better2(D,R,M,S1,U1,S2,U2, S,U) :R < 6, S = S1, U = U1,

!. better2(D,R,M,S1,U1,S2,U2, S,U) :S = S2, U = U2, !.

494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

495 %%% OUTPUT

496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

output_players :-

nl,

player(1, V1),

write('Player 1 is '), %%% either human or computer

write(V1), nl,

player(2, V2),

write('Player 2 is '), %%% either human or computer

write(V2),

!.

output_winner(B) :-

win(B,x),

write('X wins.'), !.

output_winner(B) :win(B,o),

write('O wins.'), !.

output_winner(B) :-

write('No winner.').

output_board(B) :-

nl,

nl,

output_square(B,1),

write('|'),

output_square(B,2),

write('|'),

output_square(B,3),

nl,

write('-----------'),

nl, output_square(B,4),

write('|'),

output_square(B,5),

write('|'),

output_square(B,6),

nl,

write('-----------'),

nl,

output_square(B,7),

write('|'),

output_square(B,8),

write('|'),

output_square(B,9), !.

output_board :-

board(B),

output_board(B), !.

output_square(B,S) :-

square(B,S,M),

write(' '),

output_square2(S,M),

write(' '), !.

output_square2(S, E) :-

blank_mark(E),

write(S), !.

%%% if square is empty, output the square number

output_square2(S, M) :-

write(M), !.

%%% if square is marked, output the mark

output_value(D,S,U) :-

D == 1,

nl,

write('Square '),

write(S),

write(', utility: '),

write(U), !.

585 output_value(D,S,U) :-

586

true.

591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

592 %%% PSEUDO-RANDOM NUMBERS

593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

594

595 %.......................................

596 % random_seed

597 %.......................................

598 % Initialize the random number generator...

599 % If no seed is provided, use the current time

600 %

random_seed :-

random_seed(_),

!.

random_seed(N) :-

nonvar(N),

% Do nothing, SWI-Prolog does not support seeding the random number generator

!.

random_seed(N) :-

var(N),

615 % Do nothing, SWI-Prolog does not support seeding the random number generator

616

!.

619 /*****************************************

620 OTHER COMPILER SUPPORT

621 ******************************************

622

623 arity_prolog___random_seed(N) :-

624

nonvar(N),

625

randomize(N),

626

!

627

.

628

629 arity_prolog___random_seed(N) :-

630

var(N),

631

time(time(Hour,Minute,Second,Tick)),

632

N is ( (Hour+1) * (Minute+1) * (Second+1) * (Tick+1)),

633

randomize(N),

634

!

635

.

636

637 ******************************************/

638

639

640

641 %.......................................

642 % random_int_1n

643 %.......................................

644 % returns a random integer from 1 to N

645 %

random_int_1n(N, V) :-

V is random(N) + 1,

!.

651 /*****************************************

652 OTHER COMPILER SUPPORT

653 ******************************************

654

655 arity_prolog___random_int_1n(N, V) :-

656

R is random,

657

V2 is (R * N) - 0.5,

658

float_text(V2,V3,fixed(0)),

659

int_text(V4,V3),

660

V is V4 + 1,

661

!.

664 ******************************************/

665

666

667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

668 %%% LIST PROCESSING

669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

member([V|T], V).

member([_|T], V) :- member(T,V).

append([], L, L).

append([H|T1], L2, [H|T3]) :- append(T1, L2, T3).

678 %.......................................

679 % set_item

680 %.......................................

681 % Given a list L, replace the item at position N with V

682 % return the new list in list L2

683 %

set_item(L, N, V, L2) :-

set_item2(L, N, V, 1, L2).

set_item2( [], N, V, A, L2) :-

N == -1,

L2 = []. set_item2( [_|T1], N, V, A, [V|T2] ) :-

A = N, A1 is N + 1, set_item2( T1, -1, V, A1, T2 ).

set_item2( [H|T1], N, V, A, [H|T2] ) :-

A1 is A + 1,

set_item2( T1, N, V, A1, T2 ).

get_item(L, N, V) :get_item2(L, N, 1, V).

get_item2( [], _N, _A, V) :-

V = [], !, fail. get_item2( [H|_T], N, A, V) :A = N, V = H.

get_item2( [_|T], N, A, V) :A1 is A + 1,

get_item2( T, N, A1, V).