Skip to content

Instantly share code, notes, and snippets.

@Joelbyte
Created April 17, 2011 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Joelbyte/924052 to your computer and use it in GitHub Desktop.
Save Joelbyte/924052 to your computer and use it in GitHub Desktop.
Bacchus-Bosch - Part 5
:- object(builder).
:- info([
version is 1.0,
author is 'Victor Lagerkvist',
date is 2011/04/14,
comment is 'The object that builds the entities of Bacchus-Bosch.']).
:- public(build/5).
:- public(build/4).
:- public(build/3).
build(final_state, F) :-
Ps = [printable_property - State],
State = "Congratulations! A winner is you!\n (No, you can't quit. Stop trying.)\n",
entity::new(Ps, final_state, F).
build(world, Id, Rooms, Player, World) :-
Ps = [map_property-State],
build(final_state, F),
map_property::new([[F|Rooms], Player], State),
entity::new(Ps, Id, World).
build(room, Id, Description, Room) :-
Ps = [container_property - State1,
printable_property - State2],
container_property::new([], State1),
printable_property::new([Description], State2),
entity::new(Ps, Id, Room).
build(key, Id, Description, Key) :-
Ps = [key_property-State1,
printable_property-State2,
carriable_property-State3],
key_property::new([Id], State1),
printable_property::new([Description], State2),
carriable_property::new([], State3),
entity::new(Ps, Id, Key).
build(door, Id, Description, Door) :-
Ps = [openable_property-State1,
printable_property-State2,
entrance_property-State3],
openable_property::new([], State1),
printable_property::new([Description], State2),
entrance_property::new([void], State3),
entity::new(Ps, Id, Door).
build(generic_entity, Id, Description, E) :-
Ps = [printable_property-State],
printable_property::new([Description], State),
entity::new(Ps, Id, E).
build(lock, Id, Lock) :-
Ps = [lock_property-State],
lock_property::new([], State),
entity::new(Ps, Id, Lock).
build(player, Id, Player) :-
Ps = [player_property-State1,
inventory_property-State2,
health_property-State3,
movable_property-State4],
player_property::new([], State1),
inventory_property::new([], State2),
health_property::new([], State3),
movable_property::new([void], State4),
entity::new(Ps, Id, Player).
:- end_object.
:- object(entity).
:- info([
version is 1.0,
author is 'Victor Lagerkvist',
date is 2011/03/18,
comment is 'The entity operations of Bacchus-Bosch.']).
:- public(new/3).
:- public(update/2).
:- public(action/4).
:- public(get_property/2).
:- public(select_property/3).
:- public(add_property/3).
:- public(add_properties/3).
:- public(update_property/5).
%% We might as well abstract this since it will make it easier to
%% change the representation of identities later on.
new(Ps, Id, [identity_property-Id|Ps]).
update(E0, E) :-
update(E0, E0, E).
update(E, [], E).
update(E0, [P|Ps], E) :-
P = Name - _,
Name::update(E0, E1),
update(E1, Ps, E).
action(A, Args, E0, E) :-
%% Select a property from the list such that the action A can
%% be performed with the arguments Args.
list::select(P, E0, E1),
P = PropertyName - State,
PropertyName::action(A, Args, E1, State, E2, State1),
%% Add the property with the updated state to E2.
P1 = PropertyName - State1,
E = [P1|E2].
get_property(E, P) :-
list::member(P, E).
select_property(P, E, E1) :-
list::select(P, E, E1).
add_property(P, E, [P|E]).
add_properties(E0, Ps, E) :-
list::append(Ps, E0, E).
update_property(Name, E0, S0, S, [Name-S|E]) :-
list::select(Name-S0, E0, E).
:- end_object.
:- object(property).
:- info([
version is 1.0,
author is 'Victor Lagerkvist',
date is 2011/03/19,
comment is 'A property constitutes the basic behaviours of the objects in Bacchus-Bosch.']).
:- public(update/2).
:- mode(update(+entity, -entity), zero_or_more).
:- info(update/2, [
comment is 'Update the entity to which the property belong.',
argnames is ['Entity', 'Entity1']]).
:- public(action/6).
:- mode(action(+atom, +list, +entity, +state, -state, -entity), zero_or_more).
:- info(action/6, [
comment is 'Execute the action Name with respects to Args, State and Entity, and store the resulting new state and entity in State1 and Entity1.',
argnames is ['Name', 'Args', 'Entity', 'State', 'Entity1', 'State1']]).
:- public(new/2).
:- mode(new(+list, -term), zero_or_more).
:- info(new/2, [
comment is 'Unify State with the initial state of the property, with respect to Args',
argnames is ['Args', 'State']]).
%% Basic definition: do nothing!
update(E, E).
%% Basic definition, overloaded in almost every descendant
%% prototype.
new([], void).
:- end_object.
:- object(identity_property,
extends(property)).
new([Id], Id).
:- end_object.
:- object(player_property,
extends(property)).
:- end_object.
:- object(container_property,
extends(property)).
new([], []).
update(E0, E) :-
entity::select_property(container_property-Items, E0, E1),
update_children(Items, Items1),
E = [container_property-Items1|E1].
update_children([], []).
update_children([E|Es], [E1|E1s]) :-
entity::update(E, E1),
update_children(Es, E1s).
action(add_item, [E], Owner, Items, Owner, [E|Items]).
action(select_item, [P, E], Owner, Items, Owner, Items1) :-
list::select(E, Items, Items1),
entity::get_property(E, P).
action(update_item, [P, Old, New], Owner, Items, Owner, [New|Items1]) :-
list::select(Old, Items, Items1),
entity::get_property(Old, P).
action(select_items, [P, Es], Owner, Items, Owner, Items1) :-
meta::partition([E] >> (entity::get_property(E, P)), Items, Es, Items1).
action(get_item, [P, E], Owner, Items, Owner, Items) :-
list::member(E, Items),
entity::get_property(E, P).
action(print_children, Args, Owner, Items, Owner, Items) :-
meta::include([E] >>
(entity::action(print, Args, E, _)),
Items,
_).
:- end_object.
:- object(inventory_property,
extends(container_property)).
new([], []).
update(E0, E) :-
entity::select_property(inventory_property-Items, E0, E1),
update_children(Items, Items1),
E = [inventory_property-Items1|E1].
update_children([], []).
update_children([E|Es], [E1|E1s]) :-
entity::update(E, E1),
update_children(Es, E1s).
valid_item(E) :-
entity::get_property(E, carriable_property-_).
action(add_item, [E], Owner, Items, Owner, [E|Items]) :-
valid_item(E).
action(select_item, [P, Item], Owner, Items, Owner, Items1) :-
list::select(Item, Items, Items1),
entity::get_property(Item, P).
action(get_item, [P, E], Owner, Items, Owner, Items) :-
list::member(E, Items),
entity::get_property(E, P).
action(print_children, Args, Owner, Items, Owner, Items) :-
meta::include([E] >>
(entity::action(print, Args, E, _)),
Items,
_).
:- end_object.
:- object(carriable_property,
extends(property)).
%% Perhaps not the most interesting property in the game.
:- end_object.
:- object(health_property,
extends(property)).
new([], 10).
new([Health], Health).
action(decrease_health, [], Owner, H0, Owner, H) :-
H is H0 - 1.
action(increase_health, [], Owner, H0, Owner, H) :-
H is H0 + 1.
:- end_object.
:- object(key_property,
extends(property)).
%% The default key.
new([], key).
new([Id], Id).
:- end_object.
:- object(lock_property,
extends(property)).
%% The (default) key that opens the lock.
new([], Key-locked) :-
key_property::new([], Key).
new([Key, State], Key-State).
valid_key(E, Key) :-
entity::get_property(E, key_property-Key).
action(lock, [Entity], Owner, Key-_, Owner, Key-locked) :-
valid_key(Entity, Key).
action(unlock, [Entity], Owner, Key-_, Owner, Key-unlocked) :-
valid_key(Entity, Key).
action(unlocked, [], Owner, Key-unlocked, Owner, Key-unlocked).
action(set_key, [Key], Owner, _-State, Owner, Key-State).
:- end_object.
:- object(openable_property,
extends(property)).
new([], closed-Lock) :-
lock_property::new([], LP),
entity::new(LP, lock, Lock).
new([State, Lock], State-Lock).
action(open, [], Owner, _-Lock, Owner, open-Lock) :-
entity::action(unlocked, [], Lock, Lock).
action(close, [], Owner, _-Lock, Owner, closed-Lock) :-
entity::action(unlocked, [], Lock, Lock).
action(unlock, Key, Owner, State-Lock, Owner, State-Lock1) :-
entity::action(unlock, Key, Lock, Lock1).
action(lock, Key, Owner, State-Lock, Owner, State-Lock1) :-
entity::action(lock, Key, Lock, Lock1).
action(set_lock, [Lock], Owner, State-_, Owner, State-Lock) :-
entity::get_property(Lock, lock_property-_).
action(set_state, [State], Owner, _-Lock, Owner, State-Lock) :-
State = open ; State = closed.
:- end_object.
:- object(entrance_property,
extends(property)).
new([Id], Id).
action(get_location, [Location], Owner, Location, Owner, Location).
action(set_location, [Location], Owner, _, Owner, Location).
:- end_object.
:- object(on_fire_property,
extends(property)).
update(E0, E) :-
entity::action(decrease_health, [], E0, E).
:- end_object.
:- object(printable_property,
extends(property)).
new([Description], Description).
action(print, [], Owner, Description, Owner, Description) :-
format(Description).
:- end_object.
:- object(fruit_property,
extends(property)).
action(dissolve, [E0, E], Owner, State, Owner, State) :-
entity::action(increase_health, [], E0, E).
:- end_object.
:- object(magic_fruit_property,
extends(fruit_property)).
action(dissolve, [E0, E], Owner, State, Owner, State) :-
entity::action(set_location, [final_state], E0, E).
:- end_object.
:- object(map_property,
extends(property)).
new([], []-[]).
new([Rooms, Player], Rooms-Player).
update(E0, E) :-
entity::update_property(map_property, E0, Rooms0-P0, Rooms-P, E),
entity::update(P0, P),
update_rooms(Rooms0, Rooms).
update_rooms([], []).
update_rooms([R0|R0s], [R|Rs]) :-
entity::update(R0, R),
update_rooms(R0s, Rs).
action(add_rooms, [Rooms1], Owner, Rooms2-P, Owner, Rooms-P) :-
list::append(Rooms1, Rooms2, Rooms).
action(get_room, [Property, R], Owner, Rooms-P, Owner, Rooms-P) :-
list::member(R, Rooms),
entity::get_property(R, Property).
action(select_room, [Property, R], Owner, Rooms-P, Owner, Rooms1-P) :-
list::select(R, Rooms, Rooms1),
entity::get_property(R, Property).
action(update_room, [Old, New], Owner, Rooms-P, Owner, [New|Rooms1]-P) :-
list::select(Old, Rooms, Rooms1).
action(print, [], Owner, Rooms-P, Owner, Rooms-P) :-
action(current_room, [Room], Owner, Rooms-P, _, _),
entity::action(print, [], Room, _).
action(current_room, [Current], Owner, Rooms-P, Owner, Rooms-P) :-
entity::action(get_location, [Id], P, _),
list::member(Current, Rooms),
entity::get_property(Current, identity_property-Id).
action(update_current_room, [Current0, Current],
Owner, Rooms-P, Owner, [Current|Rooms1]-P) :-
entity::action(get_location, [Id], P, _),
list::select(Current0, Rooms, Rooms1),
entity::get_property(Current0, identity_property-Id).
action(update_player, [P0, P], Owner, Rooms-P0, Owner, Rooms-P).
action(get_player, [P], Owner, Rooms-P, Owner, Rooms-P).
action(set_player, [P], Owner, Rooms-_, Owner, Rooms-P) :-
entity::get_property(P, player_property-_).
:- end_object.
:- object(movable_property,
extends(property)).
new([Id], Id).
action(get_location, [Room], Owner, Room, Owner, Room).
action(set_location, [Location], Owner, _, Owner, Location).
:- end_object.
:- object(example_game).
:- public(entity/1).
:- public(add_property/1).
:- public(action/1).
:- public(connect/1).
begin entities.
build(room, room1, "A rather unremarkable room.\n").
build(room, room2, "A room almost identical to the previous one. What on earth is going on!?\n").
build(door, door, "A wooden door with a small and rusty lock.\n").
build(lock, lock).
build(key, key, "A slightly bent key.\n").
build(generic_entity, apple, "An apple! \n").
build(generic_entity, banana, "A yellow banana. Hot diggity dog!\n").
build(player, player).
end entities.
begin properties.
add_property(fruit_property, apple).
add_property(carriable_property, apple).
add_property(magic_fruit_property, banana).
add_property(carriable_property, banana).
end properties.
begin relations.
action(set_key, lock, $ key).
action(set_lock, door, lock).
action(set_state, door, $ closed).
action(add_item, room1, apple).
action(add_item, room1, key).
action(add_item, room2, banana).
action(set_location, player, $ room1).
connect(room1, room2, door).
end relations.
:- end_object.
:- object(game).
:- info([
version is 1.0,
author is 'Victor Lagerkvist',
date is 2011/03/20,
comment is 'The core functionality of Bacchus-Bosch.']).
:- public(init/1).
init(Game) :-
write('Welcome to Bacchus-Bosch!'), nl,
current_input(S),
script_interpreter::interpret_game(Game, World),
repl(S, [], World).
repl(S, History, World0) :-
entity::update(World0, World1),
entity::action(print, [], World1, _),
write('> '),
nlp::parse_line(S, Atoms),
write('The input is: '),
meta::map([X] >> (write(X), write(' ')), Atoms), nl,
nlp::tag_atoms(Atoms, AtomTags),
write('The tagged input is: '),
meta::map([X] >> (write(X), write(' ')), AtomTags), nl,
( eval(History, AtomTags, World1, World) ->
true
; write('no.'),
nl,
World = World1
),
write('-------------------'), nl,
repl(S, AtomTags, World).
eval(History, AtomTags, World, World1) :-
nlp::resolve_pronouns(History, AtomTags, AtomTags1),
nlp::parse_atoms(AtomTags1, _, Commands),
eval_commands(Commands, World, World1).
eval_commands([], World, World).
eval_commands([C|Cs], World0, World) :-
write('The command is: '), write(C), nl,
eval_command(C, World0, World1),
eval_commands(Cs, World1, World).
eval_command(take-[Id], World0, World) :-
entity::action(update_current_room, [R0, R], World0, World1),
entity::action(update_player, [P0, P], World1, World),
entity::action(select_item, [identity_property-Id, Item], R0, R),
entity::action(add_item, [Item], P0, P).
eval_command(look-[], World, World) :-
entity::action(current_room, [Room], World, _),
write('You see: '), nl,
entity::action(print_children, [], Room, _).
eval_command(look-[inventory], World, World) :-
entity::action(get_player, [P], World, _),
write('Your inventory consists of: '), nl,
entity::action(print_children, [], P, _).
eval_command(open-[Id], World0, World) :-
entity::action(update_current_room, [Room0, Room], World0, World),
entity::action(update_item, [identity_property-Id, Door0, Door],
Room0, Room),
entity::action(open, [], Door0, Door).
eval_command(close-[Id], World0, World) :-
entity::action(update_current_room, [Room0, Room], World0, World),
entity::action(update_item, [identity_property-Id, Door0, Door],
Room0, Room),
entity::action(close, [], Door0, Door).
eval_command(C-[Id1, Id2], World0, World) :-
( C = lock
; C = unlock
),
entity::action(update_current_room, [Room0, Room], World0, World),
entity::action(get_player, [P], World, _),
entity::action(update_item, [identity_property-Id1, Door0, Door],
Room0, Room),
entity::action(get_item, [identity_property-Id2, Key], P, _),
entity::action(C, [Key], Door0, Door).
eval_command(move-[Id], World0, World) :-
entity::action(current_room, [Room], World0, _),
entity::action(update_player, [P0, P], World0, World),
entity::action(get_item, [identity_property-Id, Entrance],
Room, _),
entity::action(open, [], Entrance, _),
entity::action(get_location, [Location], Entrance, _),
entity::action(set_location, [Location], P0, P).
eval_command(eat-[Id], World0, World) :-
entity::action(update_player, [P0, P], World0, World),
entity::action(select_item, [identity_property-Id, Item], P0, P1),
entity::action(dissolve, [P1, P], Item, _).
:- end_object.
:- op(900, fx, begin).
:- op(900, fx, end).
:- op(900, fx, $).
:- initialization((
logtalk_load(library(metapredicates_loader)),
logtalk_load(library(types_loader)),
logtalk_load(entity),
logtalk_load(game_logic),
logtalk_load(builder),
logtalk_load(nlp),
logtalk_load(game),
logtalk_load(script_expansion),
logtalk_load(script_interpreter),
logtalk_load(example_game, [hook(script_expansion)]),
game::init(example_game))).
:- object(script_expansion,
implements(expanding)).
term_expansion((begin _), []).
term_expansion((end _), []).
term_expansion((build(Type, Id, Description)), entity(E)) :-
builder::build(Type, Id, Description, E).
term_expansion((build(Type, Id)), entity(E)) :-
builder::build(Type, Id, E).
%% Just make the job slightly easier in the interpreter.
term_expansion((add_property(P, Id)), add_property(t(P, Id))).
term_expansion((action(M, Id1, Id2)), action(t(M, Id1, Id2))).
term_expansion((connect(Id1, Id2, Id3)), connect(t(Id1, Id2, Id3))).
:- end_object.
:- object(script_interpreter).
:- public(interpret_game/2).
interpret_game(DB, World) :-
findall(E, DB::entity(E), Es0),
findall(P, DB::add_property(P), Ps),
findall(A, DB::action(A), As),
findall(C, DB::connect(C), Cs),
interpret_properties(Ps, Es0, Es1),
interpret_actions(As, Es1, Es2),
interpret_connectors(Cs, Es2, Es),
get_rooms(Es, Rooms),
get_player(Es, Player),
builder::build(world, world1, Rooms, Player, World).
%% Find the rooms that shall be part of the game map. This is a
%% hack since it means that no other objects than rooms can
%% have the property of being a container. To solve this we
%% could e.g. create a new property with the name
%% room_property.
get_rooms(Es, Rooms) :-
meta::include([E] >>
(entity::get_property(E, container_property-_)),
Es,
Rooms).
%% Yet another hack. We should really look for an entity that
%% has the player_property, and not assume that the player is
%% always named 'player'.
get_player(Es, Player) :-
list::member(Player, Es),
entity::get_property(Player, player_property-_).
interpret_properties([], Es, Es).
interpret_properties([t(P, Id)|Ps], Es0, Es) :-
select_entity(Id, Es0, E0, Es1),
P::new([], State),
entity::add_property(P-State, E0, E),
interpret_properties(Ps, [E|Es1], Es).
interpret_actions([], Es, Es).
interpret_actions([t(M, Id1, Id2)|As], Es0, Es) :-
select_entity(Id1, Es0, E0, Es1),
lookup_argument(Id2, Es0, Arg),
entity::action(M, [Arg], E0, E),
interpret_actions(As, [E|Es1], Es).
interpret_connectors([], Es, Es).
interpret_connectors([t(Id1, Id2, Id3)|Cs], Es0, Es) :-
select_entity(Id1, Es0, Room1, Es1),
select_entity(Id2, Es1, Room2, Es2),
select_entity(Id3, Es2, Door, Es3),
connect(Room1, Room2, Door, Room3, Room4),
interpret_connectors(Cs, [Room3, Room4|Es3], Es).
lookup_argument(Id, Es, Arg) :-
( Id = $(Symbol) ->
Arg = Symbol
; lookup_entity(Id, Es, Arg)
).
select_entity(Id, Es, E, Es1) :-
%% This is quite terrible and yet another reason why it would
%% be better to store the identity as a unique property!
list::select(E, Es, Es1),
entity::get_property(E, identity_property-Id).
lookup_entity(Id, Es, E) :-
%% This is quite terrible and yet another reason why it would
%% be better to store the identity as a unique property!
list::member(E, Es),
entity::get_property(E, identity_property-Id).
%% Connect Room1 with Room2 with Door. We don't have to return a
%% new door since it is contained in the rooms.
connect(Room1, Room2, Door, Room3, Room4) :-
entity::get_property(Room1, identity_property-Id1),
entity::get_property(Room2, identity_property-Id2),
entity::action(set_location, [Id2], Door, Door1),
entity::action(set_location, [Id1], Door, Door2),
entity::action(add_item, [Door1], Room1, Room3),
entity::action(add_item, [Door2], Room2, Room4).
:- end_object.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment