How to resolve the algorithm Tic-tac-toe step by step in the Prolog programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Tic-tac-toe step by step in the Prolog programming language

Table of Contents

Problem Statement

Play a game of tic-tac-toe. Ensure that legal moves are played and that a winning position is notified.

Tic-tac-toe   is also known as:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Tic-tac-toe step by step in the Prolog programming language

Source code in the prolog programming language

:- 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).


:- 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).


  

You may also check:How to resolve the algorithm Count the coins step by step in the REXX programming language
You may also check:How to resolve the algorithm Rhonda numbers step by step in the ALGOL 68 programming language
You may also check:How to resolve the algorithm Playing cards step by step in the Forth programming language
You may also check:How to resolve the algorithm Thue-Morse step by step in the C++ programming language
You may also check:How to resolve the algorithm Word wheel step by step in the Nim programming language