%         Demonstrationsprogramm fr Prolog-68
%
%         Copyright ˝ 1990,91,92 Jens Kilian.
%
%
%         Start mit      | ?- worm.
%
%         Steuerung ber Tasten 2, 4, 6, 8; Abbruch mit Taste 'q'

worm :-
   recorda(score, 0, _),
   randomize,
   screen_init,
   display_frame,
   make_worm(Worm\End),
   display_worm(Worm),
   make_cookie(Cookie, Worm),
   display_cookie(Cookie),
   initial_delay(Delay),
   get_initial_direction(Dir),
   if_exception(Tag,
                worm(Worm\End, Delay, 0, Cookie, Dir),
                stop_action(Tag)
               ).

stop_action(Message) :-
   revon,
   display(Message),
   revoff,
   wait_for_space,
   screen_exit,
   recorded(score, Score, Ref),
   erase(Ref),
   display('Ihre Punktzahl war '),
   display(Score),
   nl.

worm(Worm, Delay, Grow, Cookie, Dir) :-
   delay(Delay),
   new_direction(Dir, NewDir),
   move_worm(NewDir, Grow, Worm, Grow1, NewWorm),
   check_collision(NewWorm,
                   Cookie, Delay, Grow1,
                   NewCookie, NewDelay, NewGrow
                  ), !,
   worm(NewWorm, NewDelay, NewGrow, NewCookie, NewDir).

move_worm(Dir, 0, [(X, Y) | Worm]\[(XH, YH) | NewHead], 0, Worm\NewHead) :- !,
   move(Dir, XH, YH, XH1, YH1, HC),
   NewHead = [(XH1, YH1) | NewEnd],
   put_at(X, Y, 0' ),
   put_at(XH, YH, 0'O),
   put_at(XH1, YH1, HC).
move_worm(Dir, N, Worm\[(XH, YH) | NewHead], N1, Worm\NewHead) :-
   move(Dir, XH, YH, XH1, YH1, HC),
   NewHead = [(XH1, YH1) | NewEnd],
   N1 is N - 1,
   put_at(XH, YH, 0'O),
   put_at(XH1, YH1, HC).

move(0'8, XH, YH, XH, YH1, 0'^) :- YH1 is YH - 1.
move(0'6, XH, YH, XH1, YH, 0'>) :- XH1 is XH + 1.
move(0'4, XH, YH, XH1, YH, 0'<) :- XH1 is XH - 1.
move(0'2, XH, YH, XH, YH1, 0'V) :- YH1 is YH + 1.

%  Collision detection

check_collision(Worm\[(XH, YH) | End],
                Cookie, Delay, Grow,
                NewCookie, NewDelay, NewGrow
               ) :-
   check_cookie_collision(XH, YH, Worm,
                          Cookie, Delay, Grow,
                          NewCookie, NewDelay, NewGrow
                         ),
   check_frame_collision(XH, YH),
   check_body_collision(Worm, XH, YH).

check_cookie_collision(X, Y, Worm,
                       cookie(X, Y, N), Delay, Grow,
                       NewCookie, NewDelay, NewGrow
                      ) :- !,
   update_score(N),
   NewGrow is Grow + N,
   make_cookie(NewCookie, Worm),
   display_cookie(NewCookie),
   speedup(Delay, N, NewDelay).
check_cookie_collision(_, _, _, Cookie, Delay, Grow, Cookie, Delay, Grow).

check_frame_collision(XH, YH) :- 0 < XH, XH < 79, 0 < YH, YH < 24, !.
check_frame_collision(XH, YH) :- signal_exception('Crash !').

check_body_collision([(XH, YH) | End], XH, YH) :-
   var(End), !.
check_body_collision([(XH, YH) | _], XH, YH) :-
   signal_exception('Ouch !').
check_body_collision([_ | Worm], XH, YH) :-
   check_body_collision(Worm, XH, YH).

%  Input routines

get_initial_direction(Dir) :-
   biosget0(Dir),
   legal_direction(Dir), !.
get_initial_direction(Dir) :-
   get_initial_direction(Dir).

new_direction(Dir, NewDir) :-
   biosstat,
   biosget0(NewDir),
   legal_direction(NewDir), !.
new_direction(Dir, Dir).

legal_direction(0'2).
legal_direction(0'4).
legal_direction(0'6).
legal_direction(0'8).
legal_direction(0'q) :- signal_exception('Quit.').

wait_for_space :- biosget0(0' ), !.
wait_for_space :- wait_for_space.

%  Output routines

screen_init :-
   biosput(27), biosput(0'E),
   biosput(27), biosput(0'f),
   biosput(27), biosput(0'w).

screen_exit :-
   biosput(27), biosput(0'E),
   biosput(27), biosput(0'e),
   biosput(27), biosput(0'v).

revon :-  biosput(27), biosput(0'p).
revoff :- biosput(27), biosput(0'q).

display_frame :-
   display_h(0),
   display_v(0).

display_h(80) :- !.
display_h(N) :-
   put_at(N, 0, 0'*),
   put_at(N, 24, 0'*),
   N1 is N + 1,
   display_h(N1).

display_v(25) :- !.
display_v(N) :-
   put_at(0, N, 0'*),
   put_at(79, N, 0'*),
   N1 is N + 1,
   display_v(N1).

display_worm([(X, Y) | End]) :- var(End), !, put_at(X, Y, 0'#).
display_worm([(X, Y) | W]) :- put_at(X, Y, 0'O), display_worm(W).

display_cookie(cookie(X, Y, N)) :-
   C is N + 48,
   put_at(X, Y, C).

put_at(X, Y, C) :-
   X1 is X + 32, Y1 is Y + 32,
   biosput(27), biosput(0'Y), biosput(Y1), biosput(X1), biosput(C).

update_score(N) :-
   recorded(score, Score, Ref),
   erase(Ref),
   NewScore is Score + N,
   recorda(score, NewScore, _),
   put_at(0, 0, 0'*),
   revon,
   display(NewScore),
   revoff,
   fail.                      % get rid of references ...
update_score(_).

%  Initialization

make_worm([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9) | Head]\Head) :-
   Head = [(5, 10) | WormEnd].

make_cookie(cookie(X, Y, N), Worm) :-
   random(1, 78, X),
   random(1, 23, Y),
   check_cookie_position(Worm, X, Y), !,
   random(1, 9, N).
make_cookie(Cookie, Worm) :- make_cookie(Cookie, Worm).

check_cookie_position(End, _, _) :- var(End), !.
check_cookie_position([(X, Y) | Worm], X, Y) :- !, fail.
check_cookie_position([(X, Y) | Worm], XC, YC) :-
   check_cookie_position(Worm, XC, YC).

initial_delay(160).

%  Game speed

delay(0) :- !.
delay(N) :- N1 is N - 1, delay(N1).

speedup(Delay, N, 0)    :- Delay =< 3 * N, !.
speedup(Slow,  N, Fast) :- Fast is Slow - 3 * N.

