summary refs log tree commit diff
path: root/src/story
diff options
context:
space:
mode:
authordozens2024-06-25 21:22:35 -0600
committerdozens2024-06-30 23:24:42 -0600
commitbf71791fc540a584dd6d88548c91192294d106bc (patch)
treeba73c1f1616c19a34e33634a102c9d84184cada7 /src/story
parent9bf31e86cef62bed76a35353e791768960b14d70 (diff)
feat: add story engine HEAD main
Diffstat (limited to 'src/story')
-rw-r--r--src/story/README.md110
-rw-r--r--src/story/cards.dat46
-rw-r--r--src/story/story.dat19
-rw-r--r--src/story/story.fnl58
-rw-r--r--src/story/story.test.dat80
-rw-r--r--src/story/story.test.fnl16
6 files changed, 329 insertions, 0 deletions
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)))))
+
+