summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/agent.prolog278
-rw-r--r--src/command.prolog47
-rw-r--r--src/main.prolog34
-rw-r--r--src/map.prolog135
-rw-r--r--src/misc.prolog20
-rw-r--r--src/parser.prolog77
6 files changed, 591 insertions, 0 deletions
diff --git a/src/agent.prolog b/src/agent.prolog
new file mode 100644
index 0000000..4085bf9
--- /dev/null
+++ b/src/agent.prolog
@@ -0,0 +1,278 @@
+
+:- module(agent, [init, look/0, move/1, take/1, shoot/1, hazardAt/1, somethingAt/1]).
+
+
+:- consult('misc.prolog').
+:- consult('parser.prolog').
+:- consult('map.prolog').
+
+
+
+
+% initialisation
+
+init :-
+ initPos,
+ initPits,
+ initWumpus,
+ initGold,
+ initBats,
+ roomList(Rooms),
+ filter(Rooms, hazardAt, Walkable),
+ isConnected(Walkable) *-> !; init.
+
+
+initPos :-
+ retractall(moves(_)),
+ asserta(moves(0)),
+ retractall(here(_)),
+ roomList(Rooms),
+ filter(Rooms, somethingAt, Emptyrooms),
+ random_member(X,Emptyrooms),
+ asserta(here(X)).
+
+
+initPits :-
+ retractall(pitAt(_)),
+ insert(5,pitAt).
+
+
+initWumpus :-
+ retractall(wumpusAt(_)),
+ roomList(Rooms),
+ filter(Rooms, somethingAt, Emptyrooms),
+ random_member(X,Emptyrooms),
+ asserta(wumpusAt(X)).
+
+
+initGold :-
+ retractall(goldAt(_)),
+ retractall(haveGold(_)),
+ insert(3,goldAt),
+ asserta(haveGold(0)).
+
+
+initBats :-
+ retractall(batsAt(_)),
+ insert(2,batsAt).
+
+
+insert(N,Thing) :-
+ N > 0,
+ Nx is N - 1,
+ insert(Nx,Thing),
+ roomList(Rooms),
+ filter(Rooms, somethingAt, Emptyrooms),
+ random_member(X,Emptyrooms),
+ P =.. [Thing,X],
+ asserta(P).
+insert(0,_) :- true.
+
+
+
+
+% command functions
+
+look :-
+ here(Location),
+ writeSenses(Location),
+ writeExits(Location).
+
+
+move(Direction) :-
+ here(Location),
+ connects(Location, New, Direction),
+ retract(here(Location)),
+ asserta(here(New)),
+ join(['You move to the ',Direction,'.\n\n'],W),
+ write(W),
+ checkHazards,
+ incrementMoves,
+ look, !.
+
+
+move(_) :-
+ write('Unknown direction.\n\n').
+
+
+take(gold) :-
+ here(Location),
+ goldAt(Location),
+ retract(goldAt(Location)),
+ haveGold(G),
+ Gx is G + 1,
+ retract(haveGold(G)),
+ asserta(haveGold(Gx)),
+ write('You find some gold. Lucky you.\n\n'),
+ incrementMoves, !.
+
+
+take(_) :-
+ write('You cannot take that.\n\n').
+
+
+shoot(Direction) :-
+ here(Location),
+ connects(Location, Target, Direction),
+ wumpusAt(Target),
+ write('You hear an unearthly scream.\n\n'),
+ retract(wumpusAt(Target)),
+ incrementMoves,
+ win.
+
+
+shoot(Direction) :-
+ here(Location),
+ connects(Location, Target, Direction),
+ not(wumpusAt(Target)),
+ write('Thunk. Missed.\n\n'),
+ incrementMoves,
+ moveWumpus.
+
+
+shoot(_) :-
+ write('Unknown target.\n\n').
+
+
+
+
+% letting the player know what's going on
+
+writeSenses(Location) :-
+ glitter(Location),
+ breeze(Location),
+ bats(Location),
+ stench(Location).
+
+
+glitter(Location) :-
+ current_predicate(goldAt/1),
+ goldAt(Location),
+ write('You see a glitter along the sandy floor of the cave.\n').
+glitter(_) :- true.
+
+
+breeze(Location) :-
+ current_predicate(pitAt/1),
+ connects(Location, ConnectedRoom, _),
+ pitAt(ConnectedRoom),
+ write('A cold breeze blows through the room, making you shiver slightly.\n').
+breeze(_) :- true.
+
+
+bats(Location) :-
+ current_predicate(batsAt/1),
+ connects(Location, ConnectedRoom, _),
+ batsAt(ConnectedRoom),
+ write('All available surfaces are covered in guano. How unsanitary.\n').
+bats(_) :- true.
+
+
+stench(Location) :-
+ current_predicate(wumpusAt/1),
+ connects(Location, ConnectedRoom, _),
+ wumpusAt(ConnectedRoom),
+ write('An overpowering stench fills your nose.\n').
+stench(_) :- true.
+
+
+writeExits(Location) :-
+ findall(X, connects(Location,_,X), Exits),
+ intercalate(Exits, ', ', O),
+ join(['There are exits to the ',O,'.\n'],W),
+ write(W).
+
+
+
+
+% modifying things and checking the ramifications
+
+moveWumpus :-
+ wumpusAt(Old),
+ findall(X, connects(Old,X,_), PossibleNews),
+ random_member(New, PossibleNews),
+ retract(wumpusAt(Old)),
+ asserta(wumpusAt(New)),
+ checkWumpus.
+
+
+checkHazards :-
+ checkWumpus,
+ checkBats,
+ checkPit.
+
+
+checkWumpus :-
+ current_predicate(wumpusAt/1),
+ here(Location),
+ wumpusAt(Location),
+ lose(eaten).
+checkWumpus :- true.
+
+
+checkBats :-
+ current_predicate(batsAt/1),
+ here(Location),
+ batsAt(Location),
+ write('A giant bat swoops down, picks you up, and deposits you elsewhere in the cave.\n\n'),
+ roomList(Rooms),
+ random_member(NewLocation, Rooms),
+ retract(here(Location)),
+ asserta(here(NewLocation)),
+ checkHazards.
+checkBats :- true.
+
+
+checkPit :-
+ current_predicate(pitAt/1),
+ here(Location),
+ pitAt(Location),
+ lose(pit).
+checkPit :- true.
+
+
+
+
+% winning, losing, and otherwise
+
+incrementMoves :-
+ moves(N),
+ retractall(moves(_)),
+ Nx is N + 1,
+ asserta(moves(Nx)).
+
+
+win :-
+ write('*** YOU WIN ***\n'),
+ moves(M),
+ haveGold(G),
+ join(['\nWin accomplished in ',M,' moves with ',G,' gold found\n'],W),
+ write(W),
+ halt(0).
+
+
+lose(eaten) :-
+ write('You have been eaten by the wumpus.\n'),
+ write('*** GAME OVER ***\n'),
+ halt(0).
+
+
+lose(pit) :-
+ write('You have fallen into a bottomless pit.\n'),
+ write('*** GAME OVER ***\n'),
+ halt(0).
+
+
+
+
+% miscellaneous clauses
+
+somethingAt(X) :- current_predicate(here/1), here(X).
+somethingAt(X) :- current_predicate(goldAt/1), goldAt(X).
+somethingAt(X) :- hazardAt(X).
+
+
+hazardAt(X) :- current_predicate(wumpusAt/1), wumpusAt(X).
+hazardAt(X) :- current_predicate(pitAt/1), pitAt(X).
+hazardAt(X) :- current_predicate(batsAt/1), batsAt(X).
+
diff --git a/src/command.prolog b/src/command.prolog
new file mode 100644
index 0000000..f897735
--- /dev/null
+++ b/src/command.prolog
@@ -0,0 +1,47 @@
+
+
+:- module(command, [getCommand/2]).
+
+
+:- consult('parser.prolog').
+
+
+
+
+% functions for parsing a list of words into a recognised command
+
+getCommand(['quit'],C) :- C =.. [quit].
+getCommand(['exit'],C) :- C =.. [quit].
+
+
+getCommand(['go',X],C) :- C =.. [move,X].
+getCommand(['move',X],C) :- C =.. [move,X].
+
+
+getCommand(['n'],C) :- C =.. [move,north].
+getCommand(['north'],C) :- C =.. [move,north].
+getCommand(['s'],C) :- C =.. [move,south].
+getCommand(['south'],C) :- C =.. [move,south].
+getCommand(['e'],C) :- C =.. [move,east].
+getCommand(['east'],C) :- C =.. [move,east].
+getCommand(['w'],C) :- C =.. [move,west].
+getCommand(['west'],C) :- C =.. [move,west].
+
+
+getCommand(['nw'],C) :- C =.. [move,northwest].
+getCommand(['northwest'],C) :- C =.. [move,northwest].
+getCommand(['ne'],C) :- C =.. [move,northeast].
+getCommand(['northeast'],C) :- C =.. [move,northeast].
+getCommand(['sw'],C) :- C =.. [move,southwest].
+getCommand(['southwest'],C) :- C =.. [move,southwest].
+getCommand(['se'],C) :- C =.. [move,southeast].
+getCommand(['southeast'],C) :- C =.. [move,southeast].
+
+
+getCommand(['take',X],C) :- C =.. [take,X].
+getCommand(['shoot',X],C) :- C =.. [shoot,X].
+
+
+getCommand(['l'],C) :- C =.. [look].
+getCommand(['look'],C) :- C =.. [look].
+
diff --git a/src/main.prolog b/src/main.prolog
new file mode 100644
index 0000000..ef23f4f
--- /dev/null
+++ b/src/main.prolog
@@ -0,0 +1,34 @@
+
+
+:- consult('parser.prolog').
+:- consult('agent.prolog').
+:- consult('command.prolog').
+
+
+
+
+play :-
+ init,
+ look,
+ playLoop.
+
+
+playLoop :-
+ prompt(Line),
+ evalLine(Line).
+
+
+evalLine(Line) :-
+ getCommand(Line,Command),
+ Command,
+ playLoop.
+
+
+evalLine(_) :-
+ write('What?\n\n'),
+ playLoop.
+
+
+quit :-
+ halt(0).
+
diff --git a/src/map.prolog b/src/map.prolog
new file mode 100644
index 0000000..f5bb863
--- /dev/null
+++ b/src/map.prolog
@@ -0,0 +1,135 @@
+
+:- module(map, [roomList/1, isConnected/1, connects/3]).
+
+
+:- consult('misc.prolog').
+
+
+
+
+% map has 20 rooms, labelled a through t
+% connections between rooms inscribe a dodecahedron, with the rooms
+% corresponding to the vertices and the connections between them to
+% the edges
+
+
+roomList(X) :- X = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t].
+
+
+isConnected([]) :- true.
+isConnected(X) :-
+ head(Y,X),
+ tail(Z,X),
+ connectTail([Y],Z).
+
+connectTail(_,[]) :- true.
+connectTail([],_) :- false.
+connectTail(Open,G) :-
+ head(X,Open),
+ tail(Y,Open),
+ findall(N, connects(X,N,_), Ns),
+ subtract(G,Ns,Gx),
+ subtract(G,Gx,Nx),
+ append(Y,Nx,Yx),
+ connectTail(Yx,Gx).
+
+
+% loop around the middle
+
+connects(a,b,northeast).
+connects(b,c,southeast).
+connects(c,d,northeast).
+connects(d,e,southeast).
+connects(e,f,northeast).
+connects(f,g,southeast).
+connects(g,h,northeast).
+connects(h,i,southeast).
+connects(i,j,northeast).
+connects(j,a,southeast).
+
+
+% loop in the other direction around the middle
+
+connects(a,j,northwest).
+connects(j,i,southwest).
+connects(i,h,northwest).
+connects(h,g,southwest).
+connects(g,f,northwest).
+connects(f,e,southwest).
+connects(e,d,northwest).
+connects(d,c,southwest).
+connects(c,b,northwest).
+connects(b,a,southwest).
+
+
+% connections from the middle loop up to the top pentagon
+
+connects(b,k,north).
+connects(d,l,north).
+connects(f,m,north).
+connects(h,n,north).
+connects(j,o,north).
+
+
+% connections from the top pentagon down to the middle loop
+
+connects(k,b,south).
+connects(l,d,south).
+connects(m,f,south).
+connects(n,h,south).
+connects(o,j,south).
+
+
+% connections around the top pentagon
+
+connects(k,l,east).
+connects(l,m,east).
+connects(m,n,east).
+connects(n,o,east).
+connects(o,k,east).
+
+
+% connections around the top pentagon in the other direction
+
+connects(l,k,west).
+connects(m,l,west).
+connects(n,m,west).
+connects(o,n,west).
+connects(k,o,west).
+
+
+% connections from the middle loop to the bottom pentagon
+
+connects(a,p,south).
+connects(c,q,south).
+connects(e,r,south).
+connects(g,s,south).
+connects(i,t,south).
+
+
+% connections from the bottom pentagon up to the middle loop
+
+connects(p,a,north).
+connects(q,c,north).
+connects(r,e,north).
+connects(s,g,north).
+connects(t,i,north).
+
+
+% connections around the bottom pentagon
+
+connects(p,q,east).
+connects(q,r,east).
+connects(r,s,east).
+connects(s,t,east).
+connects(t,p,east).
+
+
+% connections around the bottom pentagon in the other direction
+
+connects(q,p,west).
+connects(r,q,west).
+connects(s,r,west).
+connects(t,s,west).
+connects(p,t,west).
+
diff --git a/src/misc.prolog b/src/misc.prolog
new file mode 100644
index 0000000..6d8ad68
--- /dev/null
+++ b/src/misc.prolog
@@ -0,0 +1,20 @@
+
+:- module(misc, [filter/3, head/2, tail/2]).
+
+
+
+
+filter(List, Predicate, Result) :-
+ Test =.. [Predicate,X],
+ findall(X, Test, No),
+ subtract(List, No, Result).
+
+
+head(X,[Y]) :- X = Y.
+head(X,[Y|_]) :- X = Y.
+
+
+tail(X,[]) :- X = [].
+tail(X,[_]) :- X = [].
+tail(X,[_|Y]) :- X = Y.
+
diff --git a/src/parser.prolog b/src/parser.prolog
new file mode 100644
index 0000000..c9f9771
--- /dev/null
+++ b/src/parser.prolog
@@ -0,0 +1,77 @@
+
+:- module(parser, [prompt/1, readList/1, readLine/1, split/2, join/2, intercalate/3]).
+
+
+% functions for dealing with lines of text
+% eg obtaining one, parsing it into a list of words, and rejoining it
+
+% prompt provides the user with a '>' prompt
+% readList merely obtains and parses the input
+% readLine obtains a raw line of input
+% split parses a line of text into words
+% join takes a list of words and joins them into one string
+
+
+prompt(L) :-
+ write('> '),
+ readList(L).
+
+
+readList(L) :-
+ readLine(X),
+ split(X,L).
+
+
+readLine(L) :-
+ get_char(C),
+ readLine_tail(C,L).
+
+
+readLine_tail('\n',[]) :- !.
+readLine_tail(C,[C|X]) :-
+ get_char(C2),
+ readLine_tail(C2,X).
+
+
+split(X,R) :-
+ wordList(R,X,[]), !.
+
+
+join([X|Y],R) :-
+ join(Y,Rx),
+ atom_concat(X,Rx,R).
+join([X],R) :-
+ R = X.
+
+
+intercalate([X|Y], Spacer, Result) :-
+ intercalate(Y, Spacer, Tail),
+ atom_concat(X, Spacer, Head),
+ atom_concat(Head, Tail, Result).
+intercalate([X], _, Result) :- Result = X.
+
+
+wordList(X) --> whitespace, wordList(X).
+wordList([X]) --> word(X).
+wordList([X]) --> word(X), whitespace.
+wordList([X|Y]) --> word(X), whitespace, wordList(Y).
+
+
+word(W) --> charList(X), {atom_chars(W,X)}.
+
+
+charList([X|Xs]) --> char(X), charList(Xs).
+charList([X]) --> char(X).
+
+
+whitespace --> whsp, whitespace.
+whitespace --> whsp.
+
+
+whsp --> oneOf(_,[' ', '\r', '\n', '\t']).
+char(X) --> noneOf(X,[' ', '\r', '\n', '\t']).
+
+
+oneOf(X,L) --> [X], {member(X,L)}.
+noneOf(X,L) --> [X], {not(member(X,L))}.
+