DEV Community

Cover image for Let’s Build a Random Plot Generator
Kailana Kahawaii
Kailana Kahawaii

Posted on • Edited on

Let’s Build a Random Plot Generator

National Novel Writing Month starts in a few days, and if you still don’t have an idea, this article may be for you. You can build your own random plot generator to aid when the muses have left you.

Random plot generator

You’ll need to have a basic understanding of React and Javascript to get started.

This project borrows many ideas from the Random Character Generator I built. See the corresponding article to get started.

Planning a plot

We all know that stories follow five basic plot points, but sometimes getting started with writing one is the hardest part. To get started, try coming up with a generic summary sentence that encapsulates a narrative.

Here’s the sentence I came up with:

A (genre) story that involves (genre related character) in a (genre related setting) and (genre related problem).

Using the variables such as genre related character and genre related setting, I brainstormed out elements that fit in those areas. I found using a spreadsheet was a good way to keep track of everything.

Spreadsheet showing characters, setting, a problem words and actions

I’m going to stick to the fantasy genre for now.

Coming up with a list of fantasy characters is easy enough.
List of fantasy characters on a spreadsheet

However, fantasy settings usually have a lot of detail to them. How can I generate contextually correct descriptors?

Thinking in data structures

It would make sense to store our setting verbs as a part of an array. That way, we can randomly choose an index. But how can we make these actions correspond to the correct noun? After all, a mountain can’t be in the path of collapse.

Since each action is assigned to an index, we can make the nouns keys to an object. Then, we can create number values that correspond to contextually correct verbs.

For example, beasts can work with the actions at indices 0, 1, 2, 4, and 5.

beasts: 0, 1, 2, 4, 5
Enter fullscreen mode Exit fullscreen mode

This way, we’ll be able to correctly match the action to the noun.

A list of setting words and corresponding actions

Finally, we move on to the conflict, or what makes a story really tick. There are six types of conflicts in stories, but I’m going to consolidate those into two types: a character arc and a quest arc.

On the character arc side, an action will happen to a character. On the quest side, some sort of action will happen over an item, person, or place.

List of quest and character arc types

With this done, we’re ready to construct our object.

The fantasy object

In a new file that I’ve labeled randomPlotData.js, I build out the object from the spreadsheet.

const randomPlotData = {
    genres: {
        fantasy: {
            characters: [
                an elf,
                []
                ],
            setting: {
                places: [
                    In a village,
                    []
                    ],
                actions: [
                    beset by,
                    []
                ], 
                plights: [
                    {' beasts':[0, 1, 2, 4, 5, 6]},
                [], 
                ],
            }, 
            conflict: [
                {
                    type1: {
                        problem: [
                            the death,
                            [],
                            ], 
                        instigator: [
                         of a king,
                        [], 
                    },
                }, 
                {
                    type2: {
                        problem: [
                            the hunt for, 
                            [], 
                        ],
                        instigator: [
                             a panacea,
                        ], 
                    []
}

Enter fullscreen mode Exit fullscreen mode

I’ve made a few changes here. First, I’ve changed the conflict types to type1 and type2. This way, we can add more conflicts later without having to change the functions. I’ve also added prepositions, articles, and spacing to make the sentences grammatically correct.

To see the full object, check out the repo.

Generate random plot points using Math.random()

Finally, it’s time to work on our component.

Create a new component called RandomPlotGenerator.js and import randomPlotData.

We’ll use Math.random() to choose a random index in the character array.

 genreRelatedCharacter = (data)=> {
        let charList = data.genres.fantasy.characters
        let number = Math.floor(Math.random() * charList.length)
        return charList[number]
    }
Enter fullscreen mode Exit fullscreen mode

Generating a random setting uses the same logic.

In order to generate a setting description, however, we’ll need to expand our logic a bit.

Let’s look over the data we’re using to generate these descriptors again.

actions: [
                    'beset by',
                    'in the path of',
                    'consumed by',
                    'on the verge of',
                    'conquered by',
                    'covered in',
                    'threatened by'

                ], plights: [
                    {' beasts':[0, 1, 2, 4, 5, 6]},
                    {' a raging storm':[0, 1, 2, 6]},
                    {' a plague':[0, 1, 2, 3, 6]},
                    {' collapse':[0, 3, 6]},
                    {' a celebration':[2, 3, 6]},
                    {' a malicious ruler':[0, 1, 4, 6]},
                    {' ice':[0, 1, 2, 5, 6]},
                    {' lava':[0, 1, 2, 5, 6]},
                    {' moss':[0, 1, 2, 5, 6]},
                    {' an invading army':[0, 1, 4, 6]},
                ]
Enter fullscreen mode Exit fullscreen mode

We have an actions array and an array of objects called plights.

Let’s create a variable that’ll hold our final string called settingDesc.

let settingDesc = ""
Enter fullscreen mode Exit fullscreen mode

Then, we’ll extract the list from our object and hold on to the random number that’s generated. We’ll also select our plight (remember it’s an object, so we’ll need to use Object.keys to find it).

let plightList = data.genres.fantasy.setting.plights
        let plightNum =  Math.floor(Math.random() * plightList.length)
 let plight = Object.keys(plightList[plightNum])

Enter fullscreen mode Exit fullscreen mode

This takes care of finding a plight. Now we’ll need to use this plight to find a contextually correct action.

We’ll use Object.values to access the number list assigned to each plight key. Currently, our number list looks something like this [ [ 0, 1, 2, 4, 5, 6 ] ], so we’ll need to flatten the array as well.

 let plightArr = Object.values(plightList[plightNum]).flat()
Enter fullscreen mode Exit fullscreen mode

That will give us an array that looks like this: [ 0, 1, 2, 4, 5, 6 ].

Again, we want to randomly select a number from this array, which will be the index of the word for our action.

 let actionNum = plightArr[Math.floor(Math.random() * plightArr.length)]
  let action = data.genres.fantasy.setting.actions[actionNum]
Enter fullscreen mode Exit fullscreen mode

This chooses a contextually correct action word.

There’s just one problem. Our plights are keys in an object, not strings, so we’ll need to clean up that data with JSON.stringify and some regex.

  let stringedPlight = JSON.stringify(plight).replace(/[\[\]']+/g,'').replace(/\"/g, "")
Enter fullscreen mode Exit fullscreen mode

Finally, we concat the action and stringedPlight at the end.

return settingDesc.concat(action, stringedPlight)
Enter fullscreen mode Exit fullscreen mode

The complete function will look something like this:

    genreRelatedSettingDescription = (data) => {
        let settingDesc = ""
        let plightList = data.genres.fantasy.setting.plights
        let plightNum =  Math.floor(Math.random() * plightList.length)
        let plight = Object.keys(plightList[plightNum])

        let plightArr = Object.values(plightList[plightNum]).flat()
        let actionNum = plightArr[Math.floor(Math.random() * plightArr.length)]
        let action = data.genres.fantasy.setting.actions[actionNum]
        let stringedPlight = JSON.stringify(plight).replace(/[\[\]']+/g,'').replace(/\"/g, "")

        return settingDesc.concat(action, stringedPlight)
    }
Enter fullscreen mode Exit fullscreen mode

The last random set we need to generate is the conflict. Remember, we can have two possible conflict types, so we’ll need to generate three random numbers here: one for the conflict type, one for the problem, and one for the instigator.

Again, we’ll also need to concatenate the two descriptions generated.

 let conflict = ""
        let conflictList = data.genres.fantasy.conflict
        let num = Math.floor(Math.random() * conflictList.length)
        let conflictType = conflictList[num]
        let conflictWrapper = conflictType[`type${num+1}`]
Enter fullscreen mode Exit fullscreen mode

Because our conflict is either type1 or type2, we can simply interpolate the string.

Finally, we can generate random descriptions using the conflict type.

let problem = conflictWrapper.problem[Math.floor(Math.random() * conflictWrapper.problem.length)]
        let instigator = conflictWrapper.instigator[Math.floor(Math.random() * conflictWrapper.instigator.length)]

        return conflict.concat(problem, instigator)
Enter fullscreen mode Exit fullscreen mode

Putting it all together

Our functions are done. In the render method, we’ll invoke each of them to construct our random plot generator!

render(){
    return(
        <div>
            <h1>Random Plot Generator</h1> 
        <p>{`A story that involves ${this.genreRelatedCharacter(RandomPlotData)} in a ${this.genreRelatedSetting(RandomPlotData)} ${this.genreRelatedSettingDescription(RandomPlotData)} and ${this.genreRelatedConflict(RandomPlotData)}.`} </p>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

By using objects, we can construct data for random plot generators. We can also use data structures and Math.random() to make our plot generators provide even more ideas and detail. Go ahead and add even more detail words to the object, or create a new genre object. By following the pattern, and making reusable functions, the sky’s the limit!

Top comments (0)