%=================================================================
% ITI0020 kodutöö abistav NÄIDIS: kabeprogramm
%=================================================================
% Author: J.Vain dets. 2014 License:	LGPL
%-----------------------------------------------------------------
% TEST: main(1).
% planeerimiseks abifakt: ruut(X,Y,Color,Plan_step,Prev_state, Present_state,Cost).
% X,Y - nupu koordinaadid: [1,8]
% Color - ruudul oleva nupu värv: [1,2]
% Plan_step -- planeerimispuu tase, millele fakt vastab: [0,n]
% Prev_state -- eelmise seisu ID, millest tekib jooksev seis
% Present_state --  käiguga tekkiva seisu ID
% Cost -- käigu tulemusena tekkiva seisu hind (vastase nupu võtmisel: Cost:=Cost+1, oma nupu kaotus : -1)
%-----------------------------------------------------------------
:- module(example_pgm,[main/1]).	% moodulina vormistus väldib nimede konflikte

:- dynamic avatud_seis/3.
planning_depth(4).			% Parameeter "planning_depth" näitab planeerimissammude max arvu

%----------------------- main ------------------------------------
% Sisend: mängu seis (faktid ruut/3) ja oma nuppude värv (param. Color)
% Sooritab käigu
% Väljund: mängu uus seis (faktid ruut/3)
%--------------------------------------------------------------------------
main(Color):-
	partner_color(Color,PColor),	% Salvesta fakt oma nuppude värvi meelespidamiseks
	assert(own_color(Color,PColor)),
    planning_depth(Max_Depth),          % Max_Depth = max planeerimissammude arv
    prepare_planning,                   % Planeerimiseks vajalike algseisu faktide tegemine
    generate_planning_tree(Max_Depth),  % Planeerimispuu genereerimine
    select_move,!.                      % Parima käigu valimine
main(_).
%----------------------------end main----------------------------------

%--------Planeerimiseks vajalike abifaktide ruut/7 loomine--------
prepare_planning:-
    ruut(X,Y,Color),
    assert(ruut(X,Y,Color,0,-1,0,0)),
    fail.
prepare_planning.
%-----------------Planeerimispuu genereerimine -------------------
generate_planning_tree(Max_Depth):-
    assert(planning_step(0)),
    assert(avatud_seis(0,0,0)),
    assert(votmise_tunnus(no)),
    own_color(OwnColor,ParColor),
    repeat,                              % Planeerimiskäigud oma ja vastase nuppudega. Algab oma nuppudega
        retract(planning_step(Depth)),Depth1 is Depth + 2,assert(planning_step(Depth1)), % Skaneeri nupu värve
        ((Color = OwnColor,Pl_Depth = Depth,Pl_Depth1 is Pl_Depth + 1);
        (Color = ParColor, Pl_Depth is Depth + 1,Pl_Depth1 is Pl_Depth + 1)),
        abolish(kaigu_loendur/1), assert(kaigu_loendur(0)),  % kaigu_loendur > 0 on tunnus, et sai teha käike
            leia_terminaalsed_seisud(Pl_Depth),     % Eelmise korruse terminaalsete seisude faktide tegemine
            avatud_seis(Pl_Depth,Seis,Cost),        % Eelmise korruse seisude läbivaatamine
            make_nupp_facts(Color,Seis),            % nupp-faktide genereerimine, kui seis on terminaalne
            skaneeri_nuppe(Color,Pl_Depth,Pl_Depth1,Seis,Cost),
	    assert(seis(x,y,z,k)),
            elim_valeseisud(Seis),              % kui seisust Seis saab võtta, kustuta mittevõtmisega tekkivad seisud
    (Pl_Depth1 > Max_Depth;(kaigu_loendur(0),last_branch(Seis))).   % Lõpeta planeerimine, kui plaani sügavus Max_Depth või üks pooltest ei saa käia
%-----------------------------------------------------------------
leia_terminaalsed_seisud(Depth):-
    retractall(avatud_seis(_,_,_)),
    assert(avatud_seis(x,y,0)),                 		% pseudo-fakt, et not ei annaks ERRORit
    ruut(_,_,_,Depth,_,Seis,Cost),
    not(ruut(_,_,_,_,Seis,_,_)),
    not(avatud_seis(Depth,Seis,_)),
    assert(avatud_seis(Depth,Seis,Cost)),
    fail.
leia_terminaalsed_seisud(_):-
    retract(avatud_seis(x,y,0)),                		% kustutab pseudo-fakti
    findall(Seis,avatud_seis(Depth,Seis,Cost),Bag),
    last(Bag,Viimane),abolish(last_branch/1),assert(last_branch(Viimane)),!.

%----------Nuppude käiguvõimaluste genereerimine-----------------
skaneeri_nuppe(Color,Pl_Depth,Pl_Depth1,P_State,Cost):-
    abolish(votmise_tunnus/1),assert(votmise_tunnus(no)), % Võtmise tunnus kehtib üle planeerimiskorruse
          nupp(X,Y,Color),              % seisu nupp-faktide läbikäimise
            generate_move1(X,Y,Color,Pl_Depth,Pl_Depth1,P_State,Cost),  % Genereeri nupuga võimalikud võtmised
            votmise_tunnus(T),      % Kui vähemalt ühe on nupuga võetud, siis lihtsaid käike enam ei vaadata
            generate_move2(X,Y,Color,Pl_Depth,Pl_Depth1,P_State,Cost,T),fail. % Genereeri nupu võimalikud käigud vabadele ruutudele
skaneeri_nuppe(_,_,_,_,_):- !.

%------------------------ Käik võtmisega ------------------------
generate_move1(X,Y,Color,Pl_Depth,Pl_Depth1,O_State,Cost):-
    (XSuund = 1; XSuund = -1),
    X1 is X + XSuund,
    (YSuund = 1; YSuund = -1),
    Y1 is Y + YSuund,
    X1 > 0, X1 < 9,   Y1 > 0, Y1 < 9,                               % Kas vaadeldav naaberruut on laual?
    partner_color(Color,Vastase_varv),
    ask_ruut(X1,Y1,Vastase_varv,Pl_Depth,O_State,_),                % Kas vastase nupp on naaberruudul?
    X2 is X1 + XSuund,
    Y2 is Y1 + YSuund,
    X2 > 0, X2 < 9,   Y2 > 0, Y2 < 9,                               % Kas ülejärgmine ruut on laual?
    ask_ruut(X2,Y2,0,Pl_Depth,O_State,_),                     % Kas ülejärgmine ruut on vaba?
    color(Color,Varv),  nimi(Varv,P_State),
    vota(Color,X,Y,X1,Y1,X2,Y2,Pl_Depth1,O_State,P_State,Cost),     % Genereeri võtmisega tekkiv uus seis
    fail.
generate_move1(X,Y,Color,Pl_Depth,Pl_Depth1,O_State,Cost).

vota(Color,X,Y,X1,Y1,X2,Y2,Pl_Depth1,O_State,P_State,Cost):-        % Võtmine (1 nupu)
    cost_increment(Color,Incr),Cost1 is Cost + Incr,
    assert(ruut(X,Y,0,Pl_Depth1,O_State,P_State,Cost1)),
    assert(ruut(X1,Y1,0,Pl_Depth1,O_State,P_State,Cost1)),
    assert(ruut(X2,Y2,Color,Pl_Depth1,O_State,P_State,Cost1)),
    assert(seis(Pl_Depth1,O_State,P_State,v)),
    retract(kaigu_loendur(NN)), assert(kaigu_loendur(1)),
    retract(votmise_tunnus(_)), assert(votmise_tunnus(yes)),!,      % Salvesta võtmise tunnus, siis seisust normaalkäike ei tehta
    generate_move1(X2,Y2,Color,Pl_Depth1,Pl_Depth1,P_State,Cost1).  % Proovi veel võtta

cost_increment(Color,1):-
    own_color(Color,_),!.
cost_increment(_,-1).

%----------- Käik vabale ruudule (st võtta ei saa) --------------
generate_move2(X,Y,Color,Pl_Depth,Pl_Depth1,O_State,Cost,no):-      %
    suund(Color,Suund),
    X1 is X + Suund, (Y1 is Y + 1;Y1 is Y-1),                       % Leia nupu uued koordinaadid
    X1 > 0, X1 < 9,   Y1 > 0, Y1 < 9,                               % Kas  uued koordinaadid on lubatud?
    ask_ruut(X1,Y1,0,Pl_Depth,O_State,_),                           % Kui sihtruut on vaba, siis käik on võimalik
    color(Color,Varv), nimi(Varv,P_State),                          % Leia uuele seisule nimi
    assert(ruut(X1,Y1,Color,Pl_Depth1,O_State,P_State,Cost)),       % Salvesta liikuva nupu uus positsioon
    assert(ruut(X,Y,0,Pl_Depth1,O_State,P_State,Cost)),             % Vabasta nupu lähtepositsioon
    assert(seis(Pl_Depth1,O_State,P_State,k)),
    retract(kaigu_loendur(NN)), assert(kaigu_loendur(1)),           % Näita, et toimus käik
    fail.
generate_move2(X,Y,Color,Pl_Depth,Pl_Depth1,O_State,Cost,_).

%---------- Kui antud seisust Seis saab võtta, siis mittevõtmisega käigud ei ole lubatud -----------
elim_valeseisud(Seis):-
    seis(Pl_Depth1,Seis,UusseisX,v),                                % mittevajalikud seisud võivad tekkida enne
    seis(Pl_Depth1,Seis,UusseisY,k),                                % võtmisega variandi vaatamist
    retract(ruut(X,Y,_,Pl_Depth1,Seis,UusseisY,Cost)),              % Kustutada genereeritud vale seis
    fail.
elim_valeseisud(_):-
    abolish(seis/4),!.
%===========================================
suund(1,1).
suund(2,-1).

partner_color(1,2):- !.
partner_color(2,1).

color(1,'v'):- !.
color(2,'m').
%===========================================
select_move:-                           % Jäta alles ainult parimale käigule vasav uus seis
    leia_terminaalsed_seisud(_),
    select_the_best(Seis),              % Vali genereeritud käikude hulgast parim
    copy_to_the_board(Seis),            % Kopeeri uus seis sisekujust ruut/3-faktidele
    clear_useless_facts.                % Kustuta liigsed abifaktid ruut/7

copy_to_the_board(Seis):-
	ruut(X,Y,Color,Pl_Depth1,EelSeis,Seis,Cost),
	retract(ruut(X,Y,_)),
	assert(ruut(X,Y,Color)),
	fail.
copy_to_the_board(Seis):-
	abolish(ruut/7),!.

clear_useless_facts.

select_the_best(_):-
	abolish(parim/2),
	assert(parim(0,0)),
   	 avatud_seis(Depth,Seis,Cost),
	 vordle(Seis,Cost),
    	fail.
select_the_best(BSeis):-
	retract(parim(Seis,Cost)),
	ruut(X,Y,_,Pl_Depth1,EelSeis,Seis,Cost),
	leia_I_samm(EelSeis,Seis,BSeis),!.

leia_I_samm(0,BSeis,BSeis):- !.
leia_I_samm(EelSeis,Seis,BSeis):-
	ruut(X,Y,_,_,EElSeis,EelSeis,Cost),!,
	leia_I_samm(EElSeis,EelSeis,BSeis).

vordle(Seis,Cost):-
	parim(SeisX,CostX),
	Cost > CostX,
	retract(parim(SeisX,CostX)),
	assert(parim(Seis,Cost)),!.
vordle(Seis,Cost).

%============nupp/3 faktidega on lihtsam korraldada fakt-fail tsüklit===============
make_nupp_facts(Color,Seis):-
      not(ruut(_,_,_,_,Seis,_,_)),      % kas vaadeldav seis on terminaalne st ta ei ole ühegi esivanemaks?
            abolish(nupp/3),            % kõrvaldada vanad nupp-faktid
      make_nupp_facts1(Color,Seis),!.   % leia selle seisu kõik Color värvi nupuga ruudud
make_nupp_facts(_,_):- !.

make_nupp_facts1(Color,0):-             % Sabarekursiooniga käiakse vaadeldava seisu tekkimise haru läbi alt-üles
    nupud_algseisus(Color,0),!.         % Võetakse selle haru algseisu nupude asend
make_nupp_facts1(Color,Seis):-
    ruut(_,_,_,_,E_Seis,Seis,_),
    make_nupp_facts1(Color,E_Seis),
    asenda_muutunud_ruudud(Seis,Color),!. % Asendatakse haru uue seisu faktid

nupud_algseisus(Color,Seis):-
    ruut(X,Y,Color,_,_,Seis,_),
    assert(nupp(X,Y,Color)),
    fail.
nupud_algseisus(_,_).

asenda_muutunud_ruudud(Seis,Color):-
    ruut(X,Y,ColorX,_,_,Seis,_),
    retractall(nupp(X,Y,_)),
    assert(nupp(X,Y,ColorX)),
    fail.
asenda_muutunud_ruudud(_,Color):-
    partner_color(Color,PColor),
    retractall(nupp(X,Y,PColor)),
    retractall(nupp(X,Y,0)),!.

%==============Leia planeerimispuu vaadeldavas harus X,Y-ruudu viimane seis=============
ask_ruut(X,Y,Color,0,0,Cost):-
    !,ruut(X,Y,Color,0,_,0,Cost),!.         % Kui käiguõiguse algseisus puudub ruudul otsitav nupp, annab fail
ask_ruut(X,Y,Color,Step,X_state,Cost):-
    ruut(X,Y,ColorX,Step,_,X_state,Cost),!,ColorX = Color. % värskeim XY-ruut-fakt leitud, kas ka värv klapib
ask_ruut(X,Y,Color,_,P_state,Cost):-
    ruut(_,_,_,_,O_state,P_state,_),!,      % Lähima eellase seisu küsimine
    ask_ruut(X,Y,Color,_,O_state,Cost).     % Ei ole oluline mitmenda käigu eellane
%---------------------------------------------------------------------------------------


%============================ABIPROTSEDUURID===================
%--------------------------------------------------
% Nime genereerimine
%--------------------------------------------------
nimi(Tahis,Name):-
    retract(nr(Tahis,N)),
    N1 is N + 1,
    assert(nr(Tahis,N1)),
   concat_atom([Tahis,N1],Name),!.
nimi(Tahis,Name):-
    assert(nr(Tahis,1)),
    concat_atom([Tahis,1],Name),!.

