
%  TICTACTOE - Demo Program for TOY Prolog
%
%  (c) 1986 Jens J. Kilian

tictactoe :- grf_mode, tag(tictactoe(human)), txt_mode.

tictactoe(human) :- screen, play(human, [u, u, u, u, u, u, u, u, u]),
      tictactoe(computer).
tictactoe(computer) :- screen, play(computer, [u, u, u, u, u, u, u, u, u]),
      tictactoe(human).

play(_, Board) :- wins(o, Board), delay(500, bell).
play(_, Board) :- wins(x, Board), delay(500, bell).
play(_, Board) :- not member(u, Board), delay(500, true).

play(human, Board) :- repeat, get_move(Pos), legal(Pos, Board), !,
      move(o, 0, Pos, Board, NewBoard), play(computer, NewBoard).

play(computer, Board) :- think(Board, Pos),
      move(x, 0, Pos, Board, NewBoard), play(human, NewBoard).

move(Sym, N, N, [u | R], [Sym | R]) :- show(N, Sym), !.
move(Sym, N, L, [H | T], [H | NT]) :- N1 is N + 1, move(Sym, N1, L, T, NT).

legal(0, [u | _]) :- !.
legal(N, [_ | T]) :- N1 is N - 1, legal(N1, T).

get_move(Pos) :- repeat, request(X, Y), stop_button(X, Y),
      less(170, X), less(X, 470), less(50, Y), less(Y, 350),
      Pos is 3 * ((Y - 50) / 100) + (X - 170) / 100, !.

request(X, Y) :- repeat, vq_mouse(vdi_handle, 1, X, Y), !,
                 repeat, vq_mouse(vdi_handle, 0, _, _), !.

stop_button(X, Y) :- less(30, X), less(X, 70), less(30, Y), less(Y, 70),
      tagexit(tictactoe(_)).
stop_button(_, _).

% the computer's strategy :

% try to use a winning situation

think(Board, Pos) :- insert(x, Board, Pos, NewBoard), wins(x, NewBoard).

% try to destroy the human's winning situation

think(Board, Pos) :- insert(o, Board, Pos, NewBoard), wins(o, NewBoard).

% select an empty field, but prefer center to corners to edges

think([_, _, _, _, u, _, _, _, _], 4).
think([u, _, _, _, _, _, _, _, _], 0).
think([_, _, u, _, _, _, _, _, _], 2).
think([_, _, _, _, _, _, u, _, _], 6).
think([_, _, _, _, _, _, _, _, u], 8).
think([_, u, _, _, _, _, _, _, _], 1).
think([_, _, _, u, _, _, _, _, _], 3).
think([_, _, _, _, _, u, _, _, _], 5).
think([_, _, _, _, _, _, _, u, _], 7).

insert(Sym, [u | R], 0, [Sym | R]).
insert(Sym, [H | T], N, [H | NT]) :- insert(Sym, T, N1, NT), N is N1 + 1.

% determining the end of a game :

wins(X, [X, X, X, _, _, _, _, _, _]).
wins(X, [_, _, _, X, X, X, _, _, _]).
wins(X, [_, _, _, _, _, _, X, X, X]).
wins(X, [X, _, _, X, _, _, X, _, _]).
wins(X, [_, X, _, _, X, _, _, X, _]).
wins(X, [_, _, X, _, _, X, _, _, X]).
wins(X, [X, _, _, _, X, _, _, _, X]).
wins(X, [_, _, X, _, X, _, X, _, _]).

delay(0, _).
delay(N, Call) :- N1 is N - 1, Call, delay(N1, Call).

% graphics :

screen :- v_hide_c(vdi_handle), vsf_interior(vdi_handle, 2),
      vsf_style(vdi_handle, 4), vr_recfl(vdi_handle, 0, 0, 639, 399),
      vsf_interior(vdi_handle, 0), v_rfbox(vdi_handle, 150, 30, 489, 369),
      clr(50), clr(150), clr(250),
      vsf_interior(vdi_handle, 2), vsf_style(vdi_handle, 1),
      v_rfbox(vdi_handle, 30, 30, 70, 70), vst_effects(vdi_handle, 0),
      v_gtext(vdi_handle, 34, 42, 'STOP'), v_show_c(vdi_handle, 0).

clr(Y) :- square(170, Y), square(270, Y), square(370, Y).

show(Number, Symbol) :- X is 100*(Number mod 3) + 170,
      Y is 100*(Number / 3) + 50, v_hide_c(vdi_handle), show(X, Y, Symbol),
      v_show_c(vdi_handle, 0).

show(X, Y, o) :- !, circle(X, Y).
show(X, Y, x) :- cross(X, Y).

square(X, Y) :- X1 is X + 99, Y1 is Y + 99,
      vsf_interior(vdi_handle, 0), v_bar(vdi_handle, X, Y, X1, Y1).

circle(X, Y) :- X1 is X + 50, Y1 is Y + 50, vsl_width(vdi_handle, 15),
      vsl_ends(vdi_handle, 2, 2), v_arc(vdi_handle, X1, Y1, 30, 0, 3600).

cross(X, Y) :- X1 is X + 20, Y1 is Y + 20, X2 is X + 80, Y2 is Y + 80,
      vsl_width(vdi_handle, 15), vsl_ends(vdi_handle, 2, 2),
      v_pline(vdi_handle, [X1, Y1, X2, Y2]),
      v_pline(vdi_handle, [X1, Y2, X2, Y1]).

