% Row 1
wall([X,1]) :- X =\= 2.
% Row 2
wall([X,2]) :- X = 1; X = 4; X = 6.
% Row 3
wall([X,3]) :- X = 1; X = 2; X = 6.
% Row 4
wall([X,4]) :- X =\= 3,X =\= 5.
% Row 5
wall([X,5]) :- X = 1; X = 4; X = 6.
% Row 6
wall([X,6]) :- X =\= 2.
% Start / end
start([2,1]).
end([2,6]).
% Maze Size
mazeSize([6,6]).


badSpot([X,Y]) :- wall([X,Y]).
badSpot([X,Y]) :- X < 1; Y < 1.
badSpot([X,Y]) :- mazeSize([W, H]), (X > W; Y > H).


%up
up([CurrX, CurrY], [CurrX, NextY]) :- NextY is CurrY - 1.
%down
down([CurrX, CurrY], [NextX, NextY]) :- NextY is CurrY + 1, NextX = CurrX.
%left
left([CurrX, CurrY], [NextX, NextY]) :- NextY = CurrY, NextX is CurrX - 1.
%right
right([CurrX, CurrY], [NextX, NextY]) :- NextY = CurrY, NextX is CurrX + 1.


% A solution of the maze from our current location is [] if our current location is the end.
solve(CurrentLocation, _, Path) :- end(CurrentLocation), Path = [CurrentLocation].


%Check left wall depending on Orientation
checkleft([X, Y], Orientation) :-
    (Orientation = orientation(down), NextX is X + 1, badSpot([NextX, Y]));
    (Orientation = orientation(right), NextY is Y - 1, badSpot([X, NextY]));
    (Orientation = orientation(up), NextX is X - 1, badSpot([NextX, Y]));
    (Orientation = orientation(left), NextY is Y + 1, badSpot([X, NextY])).


%Check front wall depending on Orientation
checkforward([X, Y], Orientation) :-
    (Orientation = orientation(down), NextY is Y +1, badSpot([X, NextY]));
    (Orientation = orientation(right), NextX is X + 1, badSpot([NextX, Y]));
    (Orientation = orientation(up), NextY is Y - 1, badSpot([X, NextY]));
    (Orientation = orientation(left), NextX is X - 1, badSpot([NextX, Y])).

        
%Rotating based on Orientation
rotate(Orientation, NewOrientation) :-
    (Orientation = orientation(down), NewOrientation = orientation(left));
    (Orientation = orientation(right), NewOrientation = orientation(up));
    (Orientation = orientation(up), NewOrientation = orientation(right));
    (Orientation = orientation(left), NewOrientation = orientation(down)).


solve(CurrentLocation, Orientation, Path) :-

%check left if there is no bad spot go that way
        ((\+checkleft(CurrentLocation, Orientation),
          Orientation = down, right(CurrentLocation, NewLocation), NewOrientation = orientation(right));
        (\+checkleft(CurrentLocation, Orientation),
          Orientation = right, up(CurrentLocation, NewLocation), NewOrientation = orientation(up));
        (\+checkleft(CurrentLocation, Orientation),
          Orientation = up, left(CurrentLocation, NewLocation), NewOrientation = orientation(left));
        (\+checkleft(CurrentLocation, Orientation),
          Orientation = left, down(CurrentLocation, NewLocation), NewOrientation = orientation(down)),
           solve(NewLocation, NewOrientation, RestOfPath),
           Path = [CurrentLocation | RestOfPath], !);

          
%if left is bad spot then check forward, if forward is good go that way
        ((checkleft(CurrentLocation, Orientation),
          \+checkforward(CurrentLocation, Orientation),
          Orientation = down, down(CurrentLocation, NewLocation), NewOrientation = Orientation);
         (checkleft(CurrentLocation, Orientation),
          \+checkforward(CurrentLocation, Orientation),
          Orientation = right, right(CurrentLocation, NewLocation), NewOrientation = Orientation);
         (checkleft(CurrentLocation, Orientation),
          \+checkforward(CurrentLocation, Orientation),
          Orientation = up, up(CurrentLocation, NewLocation), NewOrientation = Orientation);
         (checkleft(CurrentLocation, Orientation),
          \+checkforward(CurrentLocation, Orientation),
          Orientation = left, left(CurrentLocation, NewLocation), NewOrientation = Orientation),
         solve(NewLocation, NewOrientation, RestOfPath),
         Path = [CurrentLocation | RestOfPath],
         !);

%rotate if nothing else works
        (rotate(Orientation, NewOrientation),
        solve(NewLocation, NewOrientation, RestOfPath), Path = [CurrentLocation | RestOfPath]).
        

% Convenience rule
solve(Path) :- start(Start), solve(Start, orientation(down), Path).


drawCell(Column, Row, _) :- wall([Column, Row]), write("X"), !.
drawCell(Column, Row, _) :- start([Column, Row]), write("S"), !.
drawCell(Column, Row, _) :- end([Column, Row]), write("E"), !.
drawCell(Column, Row, Path) :- member([Column, Row], Path), write("P"), !.
drawCell(_, _, _) :- write("O").

drawRow(Row, Path) :- drawCell(1, Row, Path), tab(1), drawCell(2, Row, Path), tab(1),
        drawCell(3, Row, Path), tab(1), drawCell(4, Row, Path), tab(1),
        drawCell(5, Row, Path), tab(1), drawCell(6, Row, Path), nl.
                
draw :- drawRow(1, []), drawRow(2, []), drawRow(3, []), drawRow(4, []), drawRow(5, []),
        drawRow(6, []).
draw(Path) :- drawRow(1, Path), drawRow(2, Path), drawRow(3, Path), drawRow(4, Path),
        drawRow(5, Path), drawRow(6, Path).
