%%%%%%% Maze: My Thing %%%%%%%%
% Explanation
% So this heuristic essentially improves upon the heuristic developed for the left wall portion of this assignment. 
% However, it goes beyong that as it knows not to return to a point it has already been to with equal x and y coordinates.
% This is completed by way of ensuring that it knows that it will have to avoid turns that will require returning to a previous point
% so it will follow the fastest, correct route in this case.
% I chose to improve this heuristic since I already had some familiarity with it earlier and it was simpler to make improvements 
% to an existing KB than to make an entirely new one.
% It's successful in getting us to the end in an efficient manner, however, there is likely a way to do in fewer steps.
% If I had to change something, it would be to simplify the project and make it more cohesive. 

% 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]).

% Spaces outside the bounds of the maze
badSpot([X,Y]) :- wall([X,Y]).
badSpot([X,Y]) :- X < 1; Y < 1.
badSpot([X,Y]) :- mazeSize([W, H]), (X > W; Y > H).

% Movement
move([CurrX, CurrY], [CurrX, NextY]) :- NextY is CurrY - 1.
move([CurrX, CurrY], [NextX, NextY]) :- NextY is CurrY + 1, NextX = CurrX.
move([CurrX, CurrY], [NextX, NextY]) :- NextY = CurrY, NextX is CurrX - 1.
move([CurrX, CurrY], [NextX, NextY]) :- NextY = CurrY, NextX is CurrX + 1.
% Wall movement
move(CurrentLocation, NewLocation, Dir) :- checkLeft(CurrentLocation, NewLocation, Dir).
move(CurrentLocation, NewLocation, Dir) :- checkUp(CurrentLocation, NewLocation, Dir).
move(CurrentLocation, NewLocation, Dir) :- checkRight(CurrentLocation, NewLocation, Dir).
move(CurrentLocation, NewLocation, Dir) :- moveDown(CurrentLocation, NewLocation, Dir).

% Directions
% Up
dirCheck([_, CurrY], [_, NextY], up) :- NextY is CurrY - 1. 
% Down
dirCheck([_, CurrY], [_, NextY], down) :- NextY is CurrY + 1. 
% Left
dirCheck([CurrX, _], [NextX, _], left) :- NextX is CurrX - 1. 
% Right
dirCheck([CurrX, _], [NextX, _], right) :- NextX is CurrX + 1. 

% Get position of upper wall
upperWall([X,Y], [WallX, WallY], Dir) :- Dir = left, WallX is X - 1, WallY is Y.
upperWall([X,Y], [WallX, WallY], Dir) :- Dir = right, WallX is X + 1, WallY is Y.
upperWall([X,Y], [WallX, WallY], Dir) :- Dir = up, WallX is X, WallY is Y - 1.
upperWall([X,Y], [WallX, WallY], Dir) :- Dir = down, WallX is X, WallY is Y + 1.

% Get position of left wall
leftWall([X,Y], [WallX, WallY], Dir) :- Dir = left, WallX is X, WallY is Y + 1.
leftWall([X,Y], [WallX, WallY], Dir) :- Dir = right, WallX is X, WallY is Y - 1.
leftWall([X,Y], [WallX, WallY], Dir) :- Dir = up, WallX is X - 1, WallY is Y.
leftWall([X,Y], [WallX, WallY], Dir) :- Dir = down, WallX is X + 1, WallY is Y.

% Get position of right wall
rightWall([X,Y], [WallX, WallY], Dir) :- Dir = left, WallX is X, WallY is Y - 1.
rightWall([X,Y], [WallX, WallY], Dir) :- Dir = right, WallX is X, WallY is Y + 1.
rightWall([X,Y], [WallX, WallY], Dir) :- Dir = up, WallX is X + 1, WallY is Y.
rightWall([X,Y], [WallX, WallY], Dir) :- Dir = down, WallX is X - 1, WallY is Y.

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

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

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

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

% Check up
checkUp(CurrentLocation, NewLocation, Dir) :-
    upperWall(CurrentLocation, UpperWallLocation, Dir),
    \+ wall(UpperWallLocation),
    moveUp(CurrentLocation, NewLocation, Dir).

% Check left
checkLeft(CurrentLocation, NewLocation, Dir) :- leftWall(CurrentLocation, LeftWallLocation, Dir),
    \+ wall(LeftWallLocation), moveLeft(CurrentLocation, NewLocation, Dir).
    
% Check right
checkRight(CurrentLocation, NewLocation, Dir) :-
    rightWall(CurrentLocation, RightWallLocation, Dir), \+ wall(RightWallLocation),
    moveRight(CurrentLocation, NewLocation, Dir).

% Solve
solve(CurrentLocation, _, Path, _) :- end(CurrentLocation), Path = [CurrentLocation].

solve(CurrentLocation, BeenThere, Path, Dir) :-
    move(CurrentLocation, NewLocation, Dir),
    \+member(NewLocation, BeenThere),
    dirCheck(CurrentLocation, NewLocation, NewDir),
    solve(NewLocation, [NewLocation | BeenThere], RestOfPath, NewDir),
    Path = [CurrentLocation | RestOfPath].

solve(Path) :- start(Start), move(Start, NewLocation),
    \+badSpot(NewLocation), dirCheck(Start, NewLocation, Dir),
    solve(Start, [Start], Path, Dir).

% Convenience rule
%solve(Path) :- start(Start), solve(Start, 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).