%------------------------------------------------------------------------------
:-module(controller).
:-export read_maze/2.
:-export draw_maze/0.
:-export my_state/2.
:-export sense/4.
:-export move/1.
:-export beam/1.
%------------------------------------------------------------------------------
:-dynamic(square/3).
:-dynamic(state/2).
%------------------------------------------------------------------------------
my_state(CurrentSquare,Money):-
    state(CurrentSquare,Money).
%------------------------------------------------------------------------------
sense(North,South,West,East):-
    steal_from_maze,
    retract(state(square(X,Y,Value),OldMoney)),
    compute_new_money(OldMoney,1,0,NewMoney),
    assert(state(square(X,Y,Value),NewMoney)),
%DEBUG write('Ran sensors'),write(' Spent 1'),nl,
    list_north_south(X,Y,-1,North),
    list_north_south(X,Y,1,South),
    list_west_east(X,Y,-1,West),
    list_west_east(X,Y,1,East).
%------------------------------------------------------------------------------
list_north_south(X,Y,Direction,[square(X,NextY,ShowValue)|RestOfSquares]):-
    NextY is Y + Direction,
    square(X,NextY,Value),
    !,
    make_show_value(Value,ShowValue),
    list_north_south(X,NextY,Direction,RestOfSquares).

list_north_south(_,_,_,[]).
%------------------------------------------------------------------------------
list_west_east(X,Y,Direction,[square(NextX,Y,ShowValue)|RestOfSquares]):-
    NextX is X + Direction,
    square(NextX,Y,Value),
    !,
    make_show_value(Value,ShowValue),
    list_west_east(NextX,Y,Direction,RestOfSquares).

list_west_east(_,_,_,[]).
%------------------------------------------------------------------------------
%----50% of the time show positive as 0
make_show_value(Integer,0):-
    integer(Integer),
    Integer > 0,
    random(TellLie),
    mod(TellLie,2,0).
%    ShowValue is max(Integer,0).

make_show_value(Value,Value).
%------------------------------------------------------------------------------
move(square(XTo,YTo,_)):-
    steal_from_maze,
    integer(XTo),
    integer(YTo),
    square(XTo,YTo,Value),
    state(CurrentSquare,OldMoney),
    are_adjacent(CurrentSquare,square(XTo,YTo,Value)),
    !,
    retract(square(XTo,YTo,Value)),
    assert(square(XTo,YTo,been_there)),
    retract(state(CurrentSquare,OldMoney)),
    compute_new_money(OldMoney,2,Value,NewMoney),
%DEBUG write('Moved to '),write(XTo),write(','),write(YTo),
%DEBUG write(' Gained '),write(Value),write(' and spent 2'),nl,
    assert(state(square(XTo,YTo,been_there),NewMoney)).

%----Still charge if a wrong move
move(square(_,_,_)):-
    retract(state(CurrentSquare,OldMoney)),
    compute_new_money(OldMoney,2,0,NewMoney),
%DEBUG write('Moved nowhere'), write(' Spent 2'),nl,
    assert(state(CurrentSquare,NewMoney)),
    fail.
%------------------------------------------------------------------------------
are_adjacent(square(X,YCurrent,_),square(X,YNext,_)):-
    1 is abs(YCurrent - YNext).

are_adjacent(square(XCurrent,Y,_),square(XNext,Y,_)):-
    1 is abs(XCurrent - XNext).
%------------------------------------------------------------------------------
beam(square(XTo,YTo,_)):-
    steal_from_maze,
    integer(XTo),
    integer(YTo),
    square(XTo,YTo,Value),
    !,
    retract(square(XTo,YTo,Value)),
    assert(square(XTo,YTo,been_there)),
    retract(state(_,OldMoney)),
    compute_new_money(OldMoney,4,Value,NewMoney),
%DEBUG write('Beamed to '),write(XTo),write(','),write(YTo),
%DEBUG write(' Spent 4'),nl,
    assert(state(square(XTo,YTo,been_there),NewMoney)).

%----Still charge if a wrong move
beam(square(_,_,_)):-
    retract(state(CurrentSquare,OldMoney)),
    compute_new_money(OldMoney,4,0,NewMoney),
%DEBUG write('Beamed nowhere'),write(' Spent 4'),nl,
    assert(state(CurrentSquare,NewMoney)),
    fail.
%------------------------------------------------------------------------------
compute_new_money(OldMoney,CostOfAction,SquareValue,NewMoney):-
    integer(SquareValue),
    !,
    NewMoney is OldMoney - CostOfAction + abs(SquareValue).

compute_new_money(OldMoney,CostOfAction,_,NewMoney):-
    NewMoney is OldMoney - CostOfAction.
%------------------------------------------------------------------------------
%----Turn of the cosmic rays
steal_from_maze:-
    !.

steal_from_maze:-
    square(X,Y,Value),
    integer(Value),
    Value > 0,
    random(Number),
    mod(Number,100,0),
    retract(square(X,Y,Value)),
    assert(square(X,Y,0)),
    fail.

steal_from_maze.
%------------------------------------------------------------------------------
%----Read in the maze from the file
read_maze(FileName):-
    random(Seed),
    read_maze(FileName,Seed).
    
read_maze(FileName,Seed):-
    seed(Seed),
    clear_maze,
%----Open the file
    open(FileName,read,Stream),
%----Read the first element
    read(Stream,FirstSquare),
%----Read the rest of the file
    read_rest_of_file(Stream,FirstSquare),
%----Close the file
    close(Stream),
    !.
%------------------------------------------------------------------------------
%----Read the rest of the file
%----The case that reading the file is finished
read_rest_of_file(_,end_of_file):-
    square(X,Y,entrance),
    !,
    assert(state(square(X,Y,entrance),0)).

read_rest_of_file(_,end_of_file):-
    !,
    write('Missing entrance or exit!'),
    nl,
    fail.

%----The case that reading the file hasn't finished
read_rest_of_file(Stream,square(X,Y,OldValue)):-
    !,
    make_value(OldValue,NewValue),
    assert(square(X,Y,NewValue)),
    read(Stream,AnotherOne),
    read_rest_of_file(Stream,AnotherOne).

%----Ignore anything else
read_rest_of_file(Stream,_):-
    read(Stream,AnotherOne),
    read_rest_of_file(Stream,AnotherOne).
%------------------------------------------------------------------------------
make_value(entrance,entrance):-
    !.

%----Exit is a normal square in this model
make_value(exit,Value):-
    !,
    make_value(unknown,Value).

make_value(Integer,Integer):-
    integer(Integer),
    !.

%----33% of them have no money
make_value(unknown,0):-
    random(ForZero),
    mod(ForZero,3,0),
    !.

make_value(unknown,Value):-
    random(Number),
    mod(Number,5,Modulus),
    Value is 5 + Modulus.
%    make_sensor_error(Modulus1,Value).
%------------------------------------------------------------------------------
%----25% of the time show static 0 when there's a value
%----Not in use now
make_sensor_error(Value,ShowValue):-
    random(SensorNumber),
    mod(SensorNumber,4,0),
    !,
    ShowValue is - Value.

make_sensor_error(Value,Value).
%------------------------------------------------------------------------------
clear_maze:-
    retract_all(square(_,_,_)),
    retract_all(state(_,_)).
%------------------------------------------------------------------------------
%------------------------------------------------------------------------------
draw_maze:-
    square(LargestX,_,_),
    \+ (
        square(LargerX,_,_),
        LargerX > LargestX
    ),
    square(_,LargestY,_),
    \+ (
        square(_,LargerY,_),
        LargerY > LargestY
    ),
    !,
    draw_X_header(LargestX),
    draw_Y_rows(0,LargestX,LargestY),
    draw_X_tail(LargestX).
%------------------------------------------------------------------------------
draw_X_header(LargestX):-
    write(' X'),
    draw_indices(0,LargestX),
    nl,
    write('Y+'),
    draw_dashes(0,LargestX),
    write('+'),
    nl.
%------------------------------------------------------------------------------
draw_indices(Current,Largest):-
    Current =< Largest,
    !,
    mod(Current,10,Modulo),
    write(' '),
    write(Modulo),
    Next is Current + 1,
    draw_indices(Next,Largest).

draw_indices(_,_).
%------------------------------------------------------------------------------
draw_dashes(Current,Largest):-
    Current =< Largest,
    !,
    write('--'),
    Next is Current + 1,
    draw_dashes(Next,Largest).

draw_dashes(_,_).
%------------------------------------------------------------------------------
draw_Y_rows(CurrentY,LargestX,LargestY):-
    CurrentY =< LargestY,
    !,
    mod(CurrentY,10,Modulo),
    write(Modulo),
    write('|'),
    draw_squares(CurrentY,0,LargestX),
    write('|'),
    nl,
    NextY is CurrentY + 1,
    draw_Y_rows(NextY,LargestX,LargestY).

draw_Y_rows(_,_,_).
%------------------------------------------------------------------------------
draw_squares(Y,CurrentX,LargestX):-
    CurrentX =< LargestX,
    !,
    draw_the_square(CurrentX,Y),
    NextX is CurrentX + 1,
    draw_squares(Y,NextX,LargestX).

draw_squares(_,_,_).
%------------------------------------------------------------------------------
draw_the_square(X,Y):-
    state(square(X,Y,_),_),
    !,
    do_draw_the_square(X,Y,'*').

draw_the_square(X,Y):-
    do_draw_the_square(X,Y,' ').
%------------------------------------------------------------------------------
do_draw_the_square(X,Y,Prefix):-
    square(X,Y,entrance),
    !,
    write(Prefix),
    write('E').

do_draw_the_square(X,Y,Prefix):-
    square(X,Y,been_there),
    !,
    write(Prefix),
    write('-').

%----For the old static sensor error
do_draw_the_square(X,Y,_):-
    square(X,Y,Value),
    Value < 0,
    !,
    write('-'),
    ShowValue is abs(Value),
    write(ShowValue).

do_draw_the_square(X,Y,Prefix):-
    square(X,Y,Value),
    !,
    write(Prefix),
    write(Value).

do_draw_the_square(_,_,_):-
    write('WW').
%------------------------------------------------------------------------------
draw_X_tail(LargestX):-
    write(' +'),
    draw_dashes(0,LargestX),
    write('+'),
    nl.
%------------------------------------------------------------------------------
