NaNoGenMo / 2018

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

The League of Extraordinarily Dull Gentlemen #6

Open cpressey opened 6 years ago

cpressey commented 6 years ago

The League of Extraordinarily Dull Gentlemen

Goals

(There were other goals but this turned out to be the main one.)

Code

Of particular interest are the world-description in 930 lines of Samovar and the 363-line Python 3 script that renders the generated events into sentences.

The version of Samovar used was 0.2. For more information on Samovar, see its entry at Cat's Eye Technologies or its repository on GitHub.

Novel

In celebration of GitHub's recent acquisition by Microsoft, I have provided this document for you in Microsoft Word format. It is 85 pages long and consists of 53,129 words.

(If you cannot (or prefer not to) view files in Microsoft Word format, there is also a Markdown version which you can view directly on GitHub.)

Preview

Because it tells a story over the course of 50,000 words, I feel that a single excerpt would not do it justice, so here are a handful of them.

Chapter 2

[...] Moonlight flooded in through the French window and illuminated the suit of armor. The shadowy figure rubbed his chin. An owl hooted outside and the shadowy figure froze. The shadowy figure coughed and was now sure no one else was about. An owl hooted outside and the shadowy figure froze. The shadowy figure cast a furtive glance around the room and coughed and was now sure no one else was about and examined the leather couch closely and leaned back in the leather couch and looked out the French window and examined the leather couch closely and looked out the French window and leaned back in the leather couch and got up and stretched and coughed and rubbed his chin and coughed and

Chapter 4

[...] Pranehurst put down the encyclopedia. Scurthorpe looked at Pranehurst. Throgmorton nodded to Pranehurst. Furze-Platt looked at Pranehurst. Pranehurst nodded to Throgmorton and nodded to Furze-Platt. Scurthorpe picked up the quill pen and got up and stretched and walked around the library and coughed. Throgmorton looked at Scurthorpe. "I shall write to Old Grisbourne. He will know just what to do," said Throgmorton. Throgmorton looked at Pranehurst. Scurthorpe walked around the library. Furze-Platt examined the bookshelf closely. Throgmorton brushed some dust off his coat sleeve. Pranehurst nodded to Scurthorpe and

Chapter 9

[...] Nearby there was a grandfather clock. Furze-Platt walked over to the fireplace. Throgmorton sat down on the leather chair and leaned back in the leather chair. Furze-Platt rubbed his chin. Throgmorton brushed some dust off his coat sleeve. Furze-Platt rubbed his chin and picked up the whiskey. Throgmorton looked at Furze-Platt. "I think YOU stole the silver statuette of Artemis, Furze-Platt!" shouted Throgmorton. "WHAT?" bellowed Furze-Platt. Throgmorton rubbed his chin and put down the newspaper. Furze-Platt walked around the sitting room. Throgmorton rubbed his chin and coughed and nodded to Furze-Platt. "I think YOU stole the silver statuette of Artemis, Throgmorton!" shouted Furze-Platt. "Well I never!" bellowed Furze-Platt. Furze-Platt spluttered and looked out the window.

Chapter 12

[...] Furze-Platt looked out the grimy kitchen window and put down the empty teapot and put down the empty kettle and picked up the tea infuser and picked up the empty teapot and put down the empty teapot and looked out the grimy kitchen window and rubbed his chin and picked up the cannister of tea and rubbed his chin and picked up the empty teacup and picked up the empty kettle and examined the grimy kitchen window closely and put down the empty teacup and picked up the empty teapot and put down the empty kettle and picked up the empty kettle and coughed and rubbed his chin and rubbed his wrist and coughed and looked out the grimy kitchen window and walked away from the grimy kitchen window and walked over to the oven and walked away from the oven and

(Original content of this post is retained below)


For past NaNoGenMos I've alternated between "experimental works" and generating "proper novels". Last year I did some "experimental works" so this year I guess I better generate a proper novel, hey? Not that I have the time for this.

Looking at my previous generators, The Swallows was essentially simulation-based and MARYSUE was more-or-less grammar-based. For this one, I'd like to combine the two approaches using techniques that could be called railroading (TVTropes link).

Also, both of those generators modelled the world as discrete objects, in the manner of, say, a typical text adventure game. In this one, in contrast, I'd like to model the world as a set of propositions, similar to the "database" in Prolog. (I don't think I'll actually use Prolog - I mean logic is great and all but I've never been convinced it's very good for programming in. I actually sketched a DSL for this approach a while back, but I don't think I'll use that either. With the right set of abstractions, doing it in a "mainstream" language should be fine, and Python is what I'm most used to these days.)

My hope is that those two things will work well together and will allow some more sophisticated narrative development, stuff that was kind of awkward in the previous generators, to fall out fairly naturally.

There are certainly other things I'd like to tackle, but finding the time to do what I've already described is already a stretch. But at least one deserves mentioning, which is the actual construction of sentences. The output of a simulation is a sequence of events, and yes you can write one sentence per event, but it's horrendous, even if you use a lot of templates. What would be ideal is if the actual "writing" part of the generator could construct sentences more "from scratch", based on a grammar (obviously) but not just expanding that grammar randomly (obviously) but rather reflecting the content of the events. This is obviously incredibly difficult and I'm not going to get very far in this area, but I'd regard even a tiny bit of progress here a success.

hendrikboom3 commented 5 years ago

I'm going to to on working on these things. Not stopping merely because November is over. Considering my first nanogenmo was actually in June by myself a few years back ... the advantage of November is that there are others (such as you) to interact with. I propose to continue discussion as I continue. If you would continue at least commenting on what I do I'd appreciate it. I realize you have other demands on your time so you probably won't be rewriting Samovar much.

Would the generative text mailing list be appropriate?

hendrikboom3 commented 5 years ago

What you have in Samovar is a way of expressing axioms for a concatenative language, I played with this stuff a few years back but the chicanery you mention is a compositional operation rather different from the usual concatenation (and function calls) in Forth. We have a parallel execution semantics here. If the chicanery consists of "Faffchester puts down the newspaper; Faffchester picks up the newspaper", the safest thing is to put that in anywhere Faffchester happens to be holding a newspaper. It doesn't change the semantics. But putting it in as a single unit is a mere plot delay. To make it more engaging (especially if the newspaper is likely of interest to the reader) I'd want the two to be separated by other stuff (suspense?). So this chicanery becomes a plot thread that runs in parallel with the existing story, and the individual actions can get spliced into disparate locations, as long as the intervening actions in this thread and the main thread don't interact badly (judged by the propositions they depend on as their current states). This kind of parallelism isn't used in the usual concatenative languages, which follow a very strict stack discipline of do something and finish it before going back to what you were doing before.

cpressey commented 5 years ago

@hendrikboom3

If you would continue at least commenting on what I do I'd appreciate it.

Maybe opening an issue on https://github.com/hendrikboom3/nanogenmo2018 would work best? I mean I can't guarantee how much time I'll have to talk about it, but, sure, I can watch the repo for updates, and chip in my thoughts when I can.

I don't expect to work on Samovar again for a little while.

the chicanery you mention is a compositional operation rather different from the usual concatenation (and function calls) in Forth

I think of it as "replacing equals with (bigger) equals" in something that's more like an execution trace than a program.

By the way, this is basically how MARYSUE generated plots: instead of "X sits down in chair"/"X gets up from chair", it was "X is kidnapped by the bad guy"/"X is rescued by the good guys".

Around the start of the month I considered trying to generate the plot for this one, versus writing it by hand. My storytelling skills are essentially zero, so it was a tough call -- neither option looked significantly easier.

But if I had gone with generating the plot, I would have wanted to do something more sophisticated than what I did in MARYSUE, which I still only have vague ideas about, so I probably would've had to scale back and wouldn't have been happy with the result if I had gone that way.

I remember reading someone's advice for plotting once (I'd have to dig this up) which involved a matrix: 1 row per character and 1 column per chapter. If a chapter focused on one or more characters, they'd shade in those squares. They said this let them see things like when a character has been away from the story too long and should make a re-appearance, etc.

Also, people use the phrase "plot threads" and I do think a data structure for describing a plot, if it worked well, would probably consist of "threads" too.

But plotting is hard and November only hath 30 days.

mathias commented 5 years ago

Any thoughts on writing something like Samovar as data structures in a Lisp? Then one could use macros and data transformation functions on the Samovar data itself.

On Tue, Dec 4, 2018 at 5:39 AM Chris Pressey notifications@github.com wrote:

@hendrikboom3 https://github.com/hendrikboom3

If you would continue at least commenting on what I do I'd appreciate it.

Maybe opening an issue on https://github.com/hendrikboom3/nanogenmo2018 would work best? I mean I can't guarantee how much time I'll have to talk about it, but, sure, I can watch the repo for updates, and chip in my thoughts when I can.

I don't expect to work on Samovar again for a little while.

the chicanery you mention is a compositional operation rather different from the usual concatenation (and function calls) in Forth

I think of it as "replacing equals with (bigger) equals" in something that's more like an execution trace than a program.

By the way, this is basically how MARYSUE https://github.com/catseye/MARYSUE generated plots: instead of "X sits down in chair"/"X gets up from chair", it was "X is kidnapped by the bad guy"/"X is rescued by the good guys".

Around the start of the month I considered trying to generate the plot for this one, versus writing it by hand. My storytelling skills are essentially zero, so it was a tough call -- neither option looked significantly easier.

But if I had gone with generating the plot, I would have wanted to do something more sophisticated than what I did in MARYSUE, which I still only have vague ideas about, so I probably would've had to scale back and wouldn't have been happy with the result if I had gone that way.

I remember reading someone's advice for plotting once (I'd have to dig this up) which involved a matrix: 1 row per character and 1 column per chapter. If a chapter focused on one or more characters, they'd shade in those squares. They said this let them see things like when a character has been away from the story too long and should make a re-appearance, etc.

Also, people use the phrase "plot threads" and I do think a data structure for describing a plot, if it worked well, would probably consist of "threads" too.

But plotting is hard and November only hath 30 days.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/NaNoGenMo/2018/issues/6#issuecomment-444070737, or mute the thread https://github.com/notifications/unsubscribe-auth/AAANDBiKUByM7yqEyvD6SdEM-IiGHsG-ks5u1l7-gaJpZM4XZYGE .

-- Sent from my supercomputing distraction machine. I blog at http://blog.mattgauger.com

cpressey commented 5 years ago

@mathias

Any thoughts on writing something like Samovar as data structures in a Lisp? Then one could use macros and data transformation functions on the Samovar data itself.

Well, if I hadn't had an experimental DSL just hanging around that happened to be a good fit for what I wanted to do, it's likely I would've tried to write it in Scheme. In which case, yes, ideally it would've let you say something like

(rule
  ((actor A) (snack S) (holding A S))
  "?A ate the ?S."
  ((! (snack S)) (! (holding A S)))
)

There would be advantages and disadvantages of this. The main advantage is, as you say, you could manipulate that as a data structure. The main disadvantage, as I see it, is that you have to think about Scheme syntax when reading and writing that. For example, you'd need to backslash-escape any double quotes in the string. It's a small thing, but having a dedicated syntax for it, made it easier to read and write. (Plus, I don't have any clever ideas for something to do with those rules, if I were to manipulate them as data. If I did, I might have further thoughts on it.)

Of course, you could combine the two approaches, by exposing a parser for Samovar as a Scheme function -- or any other language of your choosing. You're still constrained by the host language's syntax for string literals, though. It's nicest if it has something like here-docs, where you can enter basically arbitrary text, but if it doesn't even let you have multi-line strings (e.g. older Javascript), that'd just be brutal.