NaNoGenMo / 2018

National Novel Generation Month, 2018 edition.
https://nanogenmo.github.io/
112 stars 6 forks source link

Novel via prolog chaining #21

Open peterohanley opened 5 years ago

peterohanley commented 5 years ago

The idea is to create a bunch of actions that have preconditions, expressed as prolog, and consequences. For example:

start_pursuit(A, B) :- character(A),
  character(B),
  knows_whereabouts(A, B),
  desires_interaction(A, B, OutcomeA, _),
  positive(OutcomeA).

which has as consequence pursuing(A, B). Another program will handling introducing and removing facts from the prolog environment. (frankensteined linear prolog basically) Possibly I will take character knowledge up one meta level, but not two. The novel is then a bunch of actions. Already this gives interesting narrative structures, like "tell the story in topological sort order but intentionally off by two" so you're always finding out why actions were just taken. The issues that I can foresee are:

I'm sure I will find many other problems but I think the approach is promising, I know similar things have been done before.

enkiv2 commented 5 years ago

DCGs can generate text based on template rules (see: https://github.com/enkiv2/misc/blob/master/tracery2dcg.py). Since you can inject extra state with semicontext notation & inject calls to traditional goals with {} (see https://www.metalevel.at/prolog/dcg) it should be possible to interleave normal goals & DCGs, and so to constrain pieces of what might otherwise look like a normal tracery-style grammar based on attributes that have been filled in. I'm considering doing that for #5.

One problem with using DCGs as a grammar generator is that the result order isn't random. Getting all results & picking a random one isn't feasible within prolog (I run out of resources on more than 100k results), & jumping out of prolog to use shell tools loses state. I figure there must be a way to replace standard ordering with a user-defined rule, & maybe that rule could be random selection (bringing DCG behavior in line with tracery), but I'm not familiar enough with prolog to know how to do this :(

On Sun, Oct 28, 2018 at 9:16 PM peterohanley notifications@github.com wrote:

The idea is to create a bunch of actions that have preconditions, expressed as prolog, and consequences. For example:

start_pursuit(A, B) :- character(A), character(B), knows_whereabouts(A, B), desiresinteraction(A, B, OutcomeA, ), positive(OutcomeA).

which has as consequence pursuing(A, B). Another program will handling introducing and removing facts from the prolog environment. (frankensteined linear prolog basically) Possibly I will take character knowledge up one meta level, but not two. The novel is then a bunch of actions. Already this gives interesting narrative structures, like "tell the story in topological sort order but intentionally off by two" so you're always finding out why actions were just taken. The issues that I can foresee are:

  • how to write dialogue?
  • how to phrase pursuing and knows_whereabouts so I can write enough different actions?
  • desires_interaction is doing a lot of work here.
  • how to stop telling the story?

I'm sure I will find many other problems but I think the approach is promising, I know similar things have been done before.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/NaNoGenMo/2018/issues/21, or mute the thread https://github.com/notifications/unsubscribe-auth/AAd6GbNcM4Kh5HXQMbQN6yZTiwutVYtZks5uplb1gaJpZM4X-ThY .

shakna-israel commented 5 years ago

desires_interaction would probably just need a series of weights, and then Prolog can handle it easily.

What motivates the character? Money? Hoarding everything?


I see a few things that would be easier, like dialogue, by using an embedded Prolog. That way, if there's no easy way to use the Prolog solver, you can just step back into the host language.

Say PicoLisp. It could handle generating the output file, and running a word count on it, to check for when the story should cease being generated.

enkiv2 commented 5 years ago

Embedded prolog helps when you're using the solver for filling in details & doing the broad strokes in an imperative language. If I understand correctly, this is the opposite situation, right? Do prolog & kanren embeddings typically support reentering into the host language in a way that would be convenient for solving this problem?

I'm starting to think that rather than transforming tracery into DCGs, it might make more sense to use DCGs (which can be arbitrarily constrained) to generate a tracery grammar -- to expand something that's easiest to think about as a grammar where certain branches are constrained into a grammar where all the constraints are turned into more specific rules.

For instance, if we've got Annals of the Perrigues style overlapping categorizations or weights for sets of terms -- some terms are more in line with the 'egg' theme & some are more aligned with the 'salt' theme -- we can express the term weights for these themes in a natural way as prolog terms but then take the template for 'building' as a DCG & generate tracery grammars for egg_building and salt_building that merely repeat the sub-trees a particular number of times to correspond to their weight.

That kind of style might fit well with the idea of generating all possible results fulfilling the DCG, parsing them as json objects, & merging them.

On Mon, Oct 29, 2018 at 7:25 PM James Milne notifications@github.com wrote:

desires_interaction would probably just need a series of weights, and then Prolog can handle it easily.

What motivates the character? Money? Hoarding everything?

I see a few things that would be easier, like dialogue, by using an embedded Prolog. That way, if there's no easy way to use the Prolog solver, you can just step back into the host language.

Say PicoLisp https://picolisp.com/wiki/?home. It could handle generating the output file, and running a word count on it, to check for when the story should cease being generated.

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/NaNoGenMo/2018/issues/21#issuecomment-434117328, or mute the thread https://github.com/notifications/unsubscribe-auth/AAd6GeD319JM4ZsJfdpNH3aN7jKkUNGzks5up45QgaJpZM4X-ThY .

peterohanley commented 5 years ago

Yeah, prolog rules determine the plot and actions and the host language will fill in descriptions and dialogue. Characters are motivated by finding the grail, which they do by finding enough kinds of information about , which they can get in a number of different ways or by taking from other characters. This plot is concrete enough that it should be practical. I considered an evil overlord plot but foiling plans seems more abstract. Of course, now that I seriously consider it I can think of ways to state it, but I'm sticking with Grail Hunters in the Jungle.

shakna-israel commented 5 years ago

Grail Hunters sounds awesome, and there's a lot to play with narrative-ly there. Easy to get several radically different outcomes.

enkiv2 commented 5 years ago

Following grailers also gives a great excuse for surreal events & leaps of logic. It's hard to imagine a generated story that's as strange as the actual story of Parzival.

On Thu, Nov 1, 2018 at 8:17 AM James Milne notifications@github.com wrote:

Grail Hunters sounds awesome, and there's a lot to play with narrative-ly there. Easy to get several radically different outcomes.

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/NaNoGenMo/2018/issues/21#issuecomment-435023135, or mute the thread https://github.com/notifications/unsubscribe-auth/AAd6Ga3o6H37s1luopRkhGmGfkdsHc8oks5uquZZgaJpZM4X-ThY .

peterohanley commented 5 years ago

I've made some progress.

start_moving(C, S, T) :-
    character(C),
    location(S),
    location(T),
    character_location(C, S),
    movement_possible(S, T),
    \+ moving(C, _, _).
start_moving(C, S, T) :! +moving(C, S, T),
    -character_location(C, S).
start_moving(C, S, T) :~ "$C left $S for $T.".

finish_moving(C, S, T) :-
    moving(C, S, T).
finish_moving(C, S, T) :!
    -moving(C, S, T),
    +character_location(C, S, T).
finish_moving(C, S, T) :~ "$C arrived at $T.".

ambush(C, T) :-
    moving(C, _, T).
ambush(C, T) :! true.
ambush(C, T) :~ "$C was ambushed by bandits on the way to $T.".

got_lost_while_moving(C, L) :-
    moving(C, _, _),
    location(L). % C goes to a random place
got_lost_while_moving(C, L) :! -moving(C, _, _), % safe because you can only be moving once.
    +character_location(C, L).
got_lost_while_moving(C, L) :~ "$C got lost and washed up at $L.".

I have a copy of https://github.com/Erdwolf/prolog that I've updated to compile, and which I'll modify to accept the above syntax. :- is like normal prolog, :! says how to modify the environment if this action is chosen, and :~ is my current plan for output. I will allow multiple :- and :~ clauses and if I think of a coherent use case for multiple effects allowing that will be easy too. This will allow a moving(C, Itinerary) multistep moving action that goes through locations rather than beelining, if necessary.

Actions are divided into starts and ends to allow interaction in the middle, like the ambush and getting lost possibilities here.

I have some ideas about choosing which action to take that I'll write up once they're more fleshed out.