Taroko Gorge Explained

next -->
        
<!-- Nick Montfort
Original Python program:
8 January 2009, Taroko Gorge National Park, Taiwan and Eva Air Flight 28
This JavaScript version, with links:
22 November 2017

Copyright (c) 2009-2017 Nick Montfort

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-->
next -->

The stylesheet

body {
 background: #062;
 color: #dad;
 margin: 0 24pt 0 24pt;
 font-family: Optima, sans-serif;
 font-size: 13pt;
}
div {
 height: 16pt;
}
a {
 color: #117;
 text-decoration: none;
}
next -->

The code

// t tracks how many lines have been generated.
var t = 0;
// The interaction between 'n' and 'paths' determines how the next line of the poem is
// generated. Both start at zero.
var n = 0;
var paths = 0;

// Nouns, I guess?
var above = 'brow,mist,shape,layer,the crag,stone,forest,height'.split(',');
var below = 'flow,basin,shape,vein,rippling,stone,cove,rock'.split(',');
// Verbs
var trans = 'command,pace,roam,trail,frame,sweep,exercise,range'.split(',');
var imper = 'track,shade,translate,stamp,progress through,direct,run,enter';
imper = imper.split(',');
var intrans = 'linger,dwell,rest,relax,hold,dream,hum'.split(',');
// uhhhhh, this is ['s', ''] (so either s or a lack of s)
var s = 's,'.split(',');
// Textures
var texture = 'rough,fine'.split(',');

//Gives you a random whole number less than whatever 'max' you give it.
function rand_range(max) {
    return Math.floor(Math.random() * (max + 1));
}

// Chooses a random member of a list.
function choose(array) {
    return array[rand_range(array.length - 1)];
}

// uh ok now we get into the weird shit
function path() {
    // This picks a random number that is either zero or one.
    // Basically, flip a coin.
    var p = rand_range(1);
    // Start building our line by picking a random word from the above list
    var words = choose(above);
    // If that word is "forest", one out of 4 times, replace the word with monkeys and
    // a verb
    if ((words == 'forest') && (rand_range(3) == 1)) {
        words = 'monkeys ' + choose(trans);
    } else {
        // In JS, the % operator means 'remainder after division'.
        // So for any number n, n % 2...
        //  * is zero if n is even
        //  * is one if n is odd.
        // Since p is either zero or one, (p + 1) is 1 or 2...
        // so if p is 0, (p+1)%2 = 1 % 2 = 1
        // and if p is 1, 2 % 2 = 0.
        // SO! if p is 0, the below line puts an 's' after the first word
        // And if p is 1, this puts an 's' after the second.
        words += s[p] + ' ' + choose(trans) + s[(p + 1) % 2];
    }
    // Also add a potentially essed below word.
    words += ' the ' + choose(below) + choose(s) + '.';
    return words;
}

function site() {
    // Start this line blank.
    var words = '';
    // One out of three times, add a word from above
    if (rand_range(2) == 1) {
        words += choose(above);
    } else {
        //Every other times, add a word from below
        words += choose(below);
    }
    words += 's ' + choose(intrans) + '.';
    return words;
}

function cave() {
    // Adds some adjectives
    var adjs = ('encompassing,' + choose(texture) + ',sinuous,straight,objective,arched,cool,clear,dim,driven').split(',');
    // Generate a random number between 1 and 4 to be a target
    var target = 1 + rand_range(3);
    // As long as adjs.length is greater than that target...
    while (adjs.length > target) {
        // pick a random adjective to delete.
        adjs.splice(rand_range(adjs.length), 1);
    }
    // By this point, our list of adjectives has been pared down to have 'target' number
    // of adjectives.
    // The \u00a0 and \u2014 are Majic JavaScrippte Incantations for a space and an
    // em-dash, respectively.
    // So this is two spaces, a random imperative, and all the adjectives, connected
    // together with spaces.
    var words = '\u00a0\u00a0' + choose(imper) + ' the ' + adjs.join(' ') + ' \u2014';
    return words;
}


function do_line() {
    // This gets the part of the HTML document with the ID 'main'
    var main = document.getElementById('main');
    // If we've got 25 or fewer lines, just go ahead and add them
    if (t <= 25) {
        t += 1;
    } else {
        // If we've got more than 25 lines, remove the topmost line before we add one to the
        // bottom
        main.removeChild(document.getElementById('main').firstChild);
    }

    // If n is zero, we insert a blank line.
    if (n === 0) {
        text = ' ';
    }
    // Else, if n == 1, set paths to a value between 2 and 4, and use 
    // path() to generate a line of text.
    else if (n == 1) {
        paths = 2 + rand_range(2);
        text = path();
    }
    // Else, if n is less than paths, use site()
    else if (n < paths) {
        text = site();
    }
    // If n is the same as paths, use path()
    else if (n == paths) {
        text = path();
    }
    // If n is one greater than paths, use a blank line
    else if (n == paths + 1) {
        text = ' ';
    }
    // If n is two greater than paths, go into the cave.
    else if (n == paths + 2) {
        text = cave();
    }
    // If n is more than two greater than paths, reset n to zero and use a blank line
    else {
        text = ' ';
        n = 0;
    }
    // Increment n by one
    n += 1;
    // Capitalise the first letter
    text = text.substring(0, 1).toUpperCase() + text.substring(1, text.length);
    // Add it to the bottom of main.
    last = document.createElement('div');
    last.appendChild(document.createTextNode(text));
    main.appendChild(last);
}
// This is the main function. It sets a timer that runs the above
// do_line function every 1.2 seconds
function poem() {
    setInterval(do_line, 1200);
}