From bf71791fc540a584dd6d88548c91192294d106bc Mon Sep 17 00:00:00 2001 From: dozens Date: Tue, 25 Jun 2024 21:22:35 -0600 Subject: feat: add story engine --- .gitignore | 1 + dist/.gitkeep | 0 doc/README.md | 12 +++++- doc/tilde30.t | 23 ++++++++++ justfile | 7 ++- src/story/README.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++ src/story/cards.dat | 46 ++++++++++++++++++++ src/story/story.dat | 19 ++++++++ src/story/story.fnl | 58 +++++++++++++++++++++++++ src/story/story.test.dat | 80 ++++++++++++++++++++++++++++++++++ src/story/story.test.fnl | 16 +++++++ test/README | 18 -------- test/README.md | 19 ++++++++ 13 files changed, 389 insertions(+), 20 deletions(-) create mode 100644 dist/.gitkeep create mode 100644 src/story/README.md create mode 100644 src/story/cards.dat create mode 100644 src/story/story.dat create mode 100644 src/story/story.fnl create mode 100644 src/story/story.test.dat create mode 100644 src/story/story.test.fnl delete mode 100644 test/README create mode 100644 test/README.md diff --git a/.gitignore b/.gitignore index ae062d0..0f00ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ test/*.expect +dist/nmm diff --git a/dist/.gitkeep b/dist/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/README.md b/doc/README.md index 0452038..bc998a5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -38,7 +38,8 @@ Here's what it looks like: ## BACKGROUND -9mm is legit a great game. +Nine Mens Morris is legit a great game, +and I like it a lot. One time i wrote an essay about the social contract implicit to nine mens morris: https://write.tildeverse.org/dozens/nine-mens-morris-cultural-meanings-and-social-contracts @@ -49,3 +50,12 @@ https://en.wikipedia.org/wiki/Morabaraba also look at these round cows https://en.wikipedia.org/wiki/Spherical_cow +## USAGE + +1. If you have fennel installed, + run `fennel main.fnl` + +2. If you have fennel installed, + you can compile a binary + using `fennel --compile-binary`. + (See `justfile` for an example of how I do it.) diff --git a/doc/tilde30.t b/doc/tilde30.t index 8a6911a..c17d27d 100644 --- a/doc/tilde30.t +++ b/doc/tilde30.t @@ -310,6 +310,29 @@ for the client to interpret. I'm still interested in the Result / Either monad option but I think I'm doing to first try the conventional lua way and throw an error, and then 'pcall' the function and handle the error politely. +. +. +.IP "WEEK FOUR REVIEW" +Today is the end of tilde30! +I didn't get too much done this week +on account of the surgery. +At least, +I wasn't able to keep up with the daily updates. +I did end up writing a tracery-lite +type templating thing +that you can find +in `src/story`. +I did not succeed in +actually incorporating it into the game. +But it's there. +tilde30 on the whole was a fun exercise +and a success. +I had a lot of fun working on my project, +and I especially had a lot of fun hearing about other people's projects. +Even if they didn't complete them. +I am planning to do it again in September, +and am planning to continue working on 9mm +in the meantime! .pl \n[nl]u diff --git a/justfile b/justfile index 825aa93..be64e85 100644 --- a/justfile +++ b/justfile @@ -2,10 +2,15 @@ default: just --list --unsorted +build: + fennel --compile-binary main.fnl dist/nmm \ + /usr/local/lib/liblua.a \ + /usr/local/include/lua5.4 + # run tests test: #!/bin/zsh - for f in **/*.test.fnl; do fennel $f | faucet; done + for f in lib/**/*.test.fnl; do fennel $f | faucet; done # build expect scripts expects: diff --git a/src/story/README.md b/src/story/README.md new file mode 100644 index 0000000..0cb81f4 --- /dev/null +++ b/src/story/README.md @@ -0,0 +1,110 @@ +## Format + +Here is a list of lists +representing a slightly augmented +deck of cards +(basically the heckadeck): + + +``` +:: suit +spades +hearts +clubs +diamonds +acorns +clouds +swords +planets + +:: face +beast +thief +jack +queen +king + +:: number +zero +one +two +three +four +five +six +seven +eight +nine +ten +eleven +twelve + +:: special +crone +joker +watcher +traveler + +:: card +[[number]] +[[face]] + +:: draw +[[card]] of [[suit]] +[[special]] +``` + +A list title appears on a line by itself, +preceded by a double colon (::) +and at least one space. +A list title can contain alphabet characters and a dash. + +Following the list title are list items, +each on its own line. +A list item may be (or contain) a reference +to a list title in double brackets. +e.g. [[list-title]] + +Blank lines are ignored. + +## Usage + +Pass the filename of a file of lists formatted in this way +to `create_corpus` +and get a "corpus" in return. +A corpus is just a deserialized fennel table +of list titles and list items. + +Then pass the corpus to `flatten` +along with a string or a list to serve as the "origin". +In this case if you pass `corpus.draw`, +then `flatten` will return a random selection from `:draw`, +expanding references along the way: + +- beast of spades +- five of hearts +- jack of acorns +- watcher +- three of planets +- beast of clubs + +Read and run `story.test.fnl` for an example. + +## Inspiration + +This is inspired by [tracery][1] +and [perchance][4] +and the [List to HTML Generator][5], +and is similar to [twee][2] format. +In fact, +with just a little modification, +you can use this story file +to generate tracery output +in a twine story +using [trice][3]. + +[1]: https://github.com/galaxykate/tracery +[2]: https://twinery.org/cookbook/terms/terms_twee.html +[3]: https://github.com/incobalt/Trice +[4]: https://perchance.org/ +[5]: https://slightadjustments.blogspot.com/p/generator.html diff --git a/src/story/cards.dat b/src/story/cards.dat new file mode 100644 index 0000000..12a1a8f --- /dev/null +++ b/src/story/cards.dat @@ -0,0 +1,46 @@ +:: suit +spades +hearts +clubs +diamonds +acorns +clouds +swords +planets + +:: face +beast +thief +jack +queen +king + +:: number +zero +one +two +three +four +five +six +seven +eight +nine +ten +eleven +twelve + +:: special +crone +joker +watcher +traveler + +:: card +[[number]] +[[face]] + +:: draw +[[card]] of [[suit]] +[[special]] + diff --git a/src/story/story.dat b/src/story/story.dat new file mode 100644 index 0000000..7c48d1a --- /dev/null +++ b/src/story/story.dat @@ -0,0 +1,19 @@ +:: origin +[[you]] [[need]] [[go]] [[search]] [[find]] [[take]] [[return]] [[change]] + +:: you +[[beginning]] + +:: beginning +Once upon a time +I've told you before but I'll tell you again +Once there was +One day, a long time ago +There was and there was not +East of the sun and west of the moon +In the beginning +Back in the day +I remember when +On an old day, in the old times +Back when tigers used to smoke tobacco +That time then and once again diff --git a/src/story/story.fnl b/src/story/story.fnl new file mode 100644 index 0000000..f1ca812 --- /dev/null +++ b/src/story/story.fnl @@ -0,0 +1,58 @@ +(fn lines [filename callback] + (case (pcall #(with-open [file (io.open filename)] (each [line (file:lines)] (callback line)))) + (false err) (print (string.format "Error: Could not open file %s\n%s" filename err)))) + +(fn _create-corpus [lines data] + (var current-key nil) + (var corpus {}) + (lines data + #(let [key (string.match $1 "^::%s+([%a-]+)") + blank (or (= nil $1) (= "" $1))] + (when (not blank) + (if (not key) + (let [list (. corpus current-key)] + (table.insert list $1) + (tset corpus current-key list)) + (do + (set current-key key) + (tset corpus current-key [])))))) + corpus) +(local create-corpus (partial _create-corpus lines)) + +(fn one-of [t] + "returns a random element of a sequential or non-sequential table" + (let [len (accumulate [l 0 _ _ (pairs t)] (+ l 1)) ;; do it the hard way + ;; because nonseq tables + ;; have no length? + handle (io.popen "echo $RANDOM") + output (handle:read "*a") + random (output:gsub "[\n\r]" "") + seed (math.randomseed random) ;; SIDE EFFECT + whatever (handle:close) ;; SIDE EFFECT + idx (math.random len) + keys (accumulate [acc [] k v (pairs t)] (do (table.insert acc k) acc)) + rndkey (. keys idx) + it (. t rndkey)] + it)) + +(fn flatten [corpus origin] + (let [str (if (= "string" (type origin)) + origin + (if (= "table" (type origin)) + (one-of origin) + (error "Origin must be a table or a string"))) + template-pattern "%[%[[%a-]+%]%]" ; [[word]] + word-pattern "%[%[([%a-]+)%]%]" ; word + (i j) (string.find str template-pattern) ; indices + word (string.match str word-pattern)] ; the actual keyword + (if (not i) + str + (let [next-str (string.format "%s%s%s" + (string.sub str 1 (- i 1)) + (one-of (. corpus word)) + (string.sub str (+ j 1)))] + (flatten corpus next-str j))))) ;; this is a tail call! + +{: create-corpus + : flatten + } diff --git a/src/story/story.test.dat b/src/story/story.test.dat new file mode 100644 index 0000000..e28ad44 --- /dev/null +++ b/src/story/story.test.dat @@ -0,0 +1,80 @@ +:: start +To [[do]] in the [[place]] [[preposition]] the [[color]] [[celestial]] + +:: do +[[walk]] +[[feel]] + +:: place +[[biome]] +[[weather]] [[biome]] +[[weather]] [[biome]] + +:: feel +brood +go to pieces +wallow +percolate +ferment +pine +waste away +ponder +wonder + +:: preposition +beneath +amongst +betwixt +below +between +through +around +despite + +:: walk +walk +stroll +jaunt +wander +meander +amble +stalk +ambulate + +:: weather +blistering +undulating +weeping +mourning +hidden +secret +wistful +taciturn +sticky + +:: biome +woods +dunes +forest +plains +hills +mountains +ocean +bog +lake + +:: color +chartreuse +opalescent +verdant +vermilion +aquamarine +copper + +:: celestial +skies +moon +stars +planets +clouds +sun diff --git a/src/story/story.test.fnl b/src/story/story.test.fnl new file mode 100644 index 0000000..e4fce4d --- /dev/null +++ b/src/story/story.test.fnl @@ -0,0 +1,16 @@ +(let [{ + : flatten + : create-corpus + } (require :src.story.story)] + + (let [corpus (create-corpus "src/story/story.test.dat") + get-story (partial flatten corpus corpus.start)] + (print "\n== POEMS ==") + (for [_ 1 10] (print (get-story)))) + + (let [corpus (create-corpus "src/story/cards.dat") + get-story (partial flatten corpus corpus.draw)] + (print "\n== CARDS ==") + (for [_ 1 10] (print (get-story))))) + + diff --git a/test/README b/test/README deleted file mode 100644 index 11b09a4..0000000 --- a/test/README +++ /dev/null @@ -1,18 +0,0 @@ -== ABOUT == - -these files are to help me test the ui - -moves are recorded in `.dat`. -then you can `awk -f test.awk file.dat > file.expect`. -(or `just expects` to build them all.) -then you can `expect file.expect` -to have expect play the game for you up to a certain point. - -== REQUIREMENTS == - -- awk -- expect - -== FUTURE PLANS == - -have actual integration tests? diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..a9602fa --- /dev/null +++ b/test/README.md @@ -0,0 +1,19 @@ +## ABOUT + +these files are to help me test the ui. +actually, just the game state really. + +moves are recorded in `.dat`. +then you can `awk -f test.awk file.dat > file.expect`. +(or `just expects` to build them all.) +then you can `expect file.expect` +to have expect play the game for you up to a certain point. + +## REQUIREMENTS + +- awk +- expect + +## FUTURE PLANS + +have actual integration tests? -- cgit 1.4.1-2-gfad0