Actually (and I'll have to find a more useful/accessible place to put this), as a general policy note: I have absolutely zero objections whatsoever to anyone using any part of my code whatsoever, with or without credit.[4] I like being credited (especially if you tell me about it—I love to hear that people are making use of the things I share!) but it's a nice bonus, not a requirement.
Long answer: It takes a list of sections of texts and gives me a pseudo-random selection of a line from that list, but doing so requires that it weight those sections so that, say, the 40-line Aetia and the 476-line Ars Poetica don't show up equally often (which would give something like 10 times the weight to a line in the Aetia compared to a line in the Ars).
Unfortunately, most of the texts aren't like that. I'm going to have to deal with epics, like Vergil's Aeneid, which is twelve books of a few hundred lines each. Getting a result of, say, "line 5000" is of extremely limited use to me, because line numbers start over with each new book and I do not want to do that math each time. So now instead of having arrays of ['title', line no.]
, I'll do ['title', [[bk. no, line no.], [bk. no, line no.]...]]
. For works not divided into books, I'll just set the book number as 0, which gives me this:
Doubly unfortunately, some of the texts are not epics with ten or twelve books but instead are, say, the 116 poems of Catullus. I'm also not going to be sitting here inputting 116 line counts, especially not when I have to do a fucked-up little work-around to get the numbers in the first place. What I'll do is put the number of poems in the line number of space so I can at least get a random poem.
I'll also add an extra number at the end of the total of lines in the work, either estimated or provided by Wikipedia if possible, which I can use to get an average poem length (for Catullus, about 20 lines). I can use this later to get a random number between 1 and that average length; if the line doesn't exist, I can use a random number generator to get a one-off number, but this should cover most circumstances. Now I've got this for my array (with the spacing changed for readability):
Triply unfortunately, some of the texts on my list are prose, which means that they aren't broken up into lines. For this, I put number of chapters in the line number space and used the line count property, for which I basically just took a guess as to the average number of lines per chapter[6] and then multiplied it by the number of chapters. (Why yes, this did take for-fucking-ever.) I also threw in a true
at the next index so that I can identify the approximate counts later. Now I've got:
Quadrupally unfortunately, sometimes texts have the same names, so we really need to include author names:
The first two are easy. For the first, I need an array of total line numbers, which I make by either taking a[n][3]
if it exists or getting the sum of the line numbers of all books in a text if it doesn't. For the second, I take the corpus array and the weight array and use the both of them to produce a selection from the corpus based on this tutorial.
For the third, I input the corpus and use functions one and two to select a random text, then use function two again to select a book from within the text. Then I use a simple random method to get a line/poem/chapter number within that book. If there is a total line count at a[n][3]
, I also use that to get an average line count per poem/chapter and then get a random line number within that (these are marked with a star so that I'm not surprised if the line fails to exist). All of this is then formatted so it looks pretty in the real version, but I've stripped that back to pretty basic HTML for my example code here.
For the fourth, I just want to run through the array for the title and author, then through the books array at a[n][2]
to get the included books and the line/poem/chapter number of each book. I also have a note on the number of lines total, which is marked if it's an approximation. Each list is enclosed in a <ul>
in a <details>
tag, with each text in its own <li>
tag.
const testCorp = [
['catullus', 'carmina', [[0, 116]], 2328],
['tacitus', 'annals', [[13, 58], [14, 65], [15, 74], [16, 35]], 4640]
];
const rlCorp = [
['caesar', 'de bello civile', [[1, 87]], 1740, true],
['caesar', 'de bello gallico', [[1, 54]], 1080, true],
['cicero', 'de officiis', [[1, 161]], 2185, true],
['cicero', 'de oratore', [[1, 265]], 3930, true],
['cicero', 'in catalinam', [[1, 33], [2, 29]], 682, true],
['cicero', 'in antoniam', [[2, 119]], 1488, true],
['cicero', 'pro archia', [[0, 32]], 448, true],
['cicero', 'pro caelio', [[0, 80]], 1000, true],
['catullus', 'carmina', [[0, 116]], 2328],
['horace', 'ars poetica', [[0, 476]]],
['horace', 'carmina', [[1, 38], [2, 20], [3, 30]], 2452],
['horace', 'epistulae', [[2.1, 270], [2.2, 216]]],
['horace', 'sermones', [[1, 10]], 1029],
['juvenal', 'satires', [[1, 171], [2, 170], [3, 322], [4, 154], [5, 173], [6, 695], [7, 243], [8, 275], [9, 150], [10, 366], [11, 208], [12, 130], [13, 249], [14, 331], [15, 174], [16, 60]]],
['livy', 'ab urbe condita', [[1, 60], [6, 42], [21, 63]], 2805, true],
['lucan', 'pharsalia', [[1, 695], [2, 736], [3, 762], [4, 824], [5, 815], [6, 830], [7, 872], [8, 872], [9, 1108], [10, 546]]],
['lucretius', 'de rerum naturae', [[1, 1117], [2, 1174], [3, 1094], [4, 1287], [5, 1457], [6, 830]]],
['ovid', 'amores', [[1.1, 34], [1.2, 52], [1.3, 26], [1.4, 70], [1.5, 26], [1.6, 74], [1.7, 68], [1.8, 114], [1.9, 46], [1.10, 64], [1.11, 28], [1.12, 30], [1.13, 48], [1.14, 56], [1.15, 42]]],
['ovid', 'fasti', [[4, 954]]],
['ovid', 'metamorphoses', [[1, 779], [8, 884]]],
['plautus', 'amphitruo', [[0, 1146]]],
['plautus', 'casina', [[0, 1018]]],
['plautus', 'menaechmi', [[0, 1162]]],
['propertius', 'elegies', [[1.1, 38], [4.4, 94], [4.6, 86], [4.8, 88]]],
['quintilian', 'institutio oratoriae', [[10.1, 131]], 655, true],
['sallust', 'bellum catilinae', [[0, 61]], 1342, true],
['seneca minor', 'epistulae', [[51, 13], [77, 20], [78, 29], [90, 46], [114, 27], [122, 19]], 1155],
['seneca minor', 'phaedra', [[0, 1280]]],
['statius', 'silvae', [[4.0, 37], [4.1, 47], [4.2, 67], [4.3, 163], [4.4, 105], [4.5, 60], [4.6, 109], [4.7, 56], [4.8, 62], [4.9, 55]]],
['suetonius', 'vita augustae', [[0, 101]], 750, true],
['sulpicia', 'elegies', [[0, 6]], 40],
['tacitus', 'agricola', [[0, 46]], 650],
['tacitus', 'annales', [[1, 81], [2, 88], [3, 76], [4, 75]], 6400, true],
['tacitus', 'dialogus', [[0, 42]], 840, true],
['tacitus', 'historiae', [[1, 90]], 1800, true],
['terence', 'adelphoe', [[0, 997]]],
['terence', 'phormio', [[0, 1055]]],
['tibullus', 'elegies', [[1.1, 78], [1.3, 94], [1.4, 84], [2.3, 80], [2.5, 122]]],
['vergil', 'aeneid', [[1, 756], [2, 804], [3, 718], [4, 705], [5, 871], [6, 901], [7, 865], [8, 817], [9, 818], [10, 908], [11, 915], [12, 952]]],
['vergil', 'eclogues', [[1, 83], [2, 73], [3, 111], [4, 63]]],
['vergil', 'georgics', [[1, 514], [4, 566]]],
['aeschylus', 'agamemnon', [[0, 1673]]],
['aeschylus', 'choephoroi', [[0, 1075]]],
['aeschylus', 'eumenides', [[0, 1470]]],
['apollonius rhodius', 'argonautica', [[3, 1407]]],
['aristophanes', 'nubes', [[0, 1510]]],
['aristophanes', 'ranae', [[0, 1533]]],
['callimachus', 'aetia', [[0, 40]]],
['callimachus', 'hymn to zeus', [[0, 96]]],
['callimachus', 'hymn to apollo', [[0, 113]]],
['callimachus', 'hymn to athena', [[0, 142]]],
['callimachus', 'hymn to demeter', [[0, 138]]],
['demosthenes', 'olynthiacs', [[1, 28], [2, 31], [3, 36]], 714, true],
['demosthenes', 'de corona', [[0, 324]], 2430, true],
['demosthenes', 'against neiara', [[0, 123]], 923, true],
['dio chrysostom', 'orationes', [[7, 152]], 1140, true],
['euripides', 'bacchae', [[0, 1392]]],
['euripides', 'hippolytus', [[0, 1466]]],
['euripides', 'medea', [[0, 1419]]],
['hesiod', 'theogony', [[0, 1022]]],
['herodotus', 'histories', [[1, 216], [6, 140], [7, 239], [8, 144]], 14780, true],
['homer', 'iliad', [[1, 611], [2, 877], [3, 461], [4, 544], [5, 909], [6, 529], [9, 713], [16, 867], [18, 617], [19, 424], [22, 515], [23, 897], [24, 804]]],
['homer', 'odyssey', [[1, 444], [5, 493], [6, 331], [7, 347], [8, 586], [9, 566], [10, 574], [11, 640], [12, 543], [19, 604], [21, 434], [22, 501], [23, 548]]],
['longus', 'daphnis and chloe', [[1, 32], [2, 39], [3, 34], [4, 40]], 2900, true],
['lucian', 'true histories', [[0, 47]], 705, true],
['lysias', 'on the murder of eratosthenes', [[0, 50]], 300, true],
['lysias', 'against eratosthenes', [[0, 100]], 750, true],
['menander', 'dyscolus', [[0, 964]]],
['pindar', 'isthmian ode', [[7, 51]]],
['pindar', 'olympian ode', [[1, 116], [2, 100], [7, 95]]],
['pindar', 'pythian ode', [[1, 100], [8, 100], [10, 72]]],
['plutarch', 'vita ciceronis', [[0, 49]], 1764],
['plutarch', 'demosthenes and cicero', [[0, 5]], 96, true],
['sophocles', 'antigone', [[0, 1353]]],
['sophocles', 'oedipus coloneus', [[0, 1779]]],
['sophocles', 'oedipus tyrannos', [[0, 1530]]],
['theocritus', 'idylls', [[1, 152], [2, 166], [7, 157], [11, 81]]],
['thucydides', 'peloponnesian war', [[1, 146], [6, 105], [7, 87]], 10140, true],
['xenophon', 'anabasis', [[3.1, 47], [3.2, 39], [3.3, 20], [3.4, 49], [3.5, 18]], 865, 1]
];
const myCorp = [
['catullus', 'carmina', [[0, 116]], 2328],
['lucan', 'pharsalia', [[1, 695], [2, 736], [3, 762], [4, 824], [5, 815], [6, 830], [7, 872], [8, 872], [9, 1108], [10, 546]]],
['ovid', 'amores', [[1.1, 34], [1.2, 52], [1.3, 26], [1.4, 70], [1.5, 26], [1.6, 74], [1.7, 68], [1.8, 114], [1.9, 46], [1.10, 64], [1.11, 28], [1.12, 30], [1.13, 48], [1.14, 56], [1.15, 42]]],
['ovid', 'heroides', [[1, 116], [2, 148], [3, 154], [4, 176], [5, 158], [6, 164], [7, 196], [8, 122], [9, 168], [10, 150], [11, 128], [12, 212], [13, 166], [14, 132], [15, 220]]],
['ovid', 'metamorphoses', [[1, 779], [2, 875], [3, 733], [4, 803], [5, 678], [6, 721], [7, 865], [8, 884], [9, 797], [10, 739], [11, 795], [12, 628], [13, 968], [14, 851], [15, 879]]],
['seneca minor', 'phaedra', [[0, 1280]]],
['seneca minor', 'medea', [[0, 1027]]],
['seneca minor', 'thyestes', [[0, 1112]]],
['tacitus', 'annals', [[13, 58], [14, 65], [15, 74], [16, 35]], 4640],
['vergil', 'aeneid', [[1, 756], [2, 804], [3, 718], [4, 705], [5, 871], [6, 901], [7, 865], [8, 817], [9, 818], [10, 908], [11, 915], [12, 952]]],
['vergil', 'georgics', [[1, 514], [4, 566]]],
['aeschylus', 'agamemnon', [[0, 1673]]],
['aristophanes', 'ranae', [[0, 1533]]],
['callimachus', 'aetia', [[0, 40]]],
['euripides', 'medea', [[0, 1419]]],
['homer', 'iliad', [[1, 611], [6, 529], [22, 515], [23, 897], [24, 804]]],
['homer', 'hymn', [[2, 495], [3, 546], [4, 580], [5, 293]]],
['sophocles', 'antigone', [[0, 1353]]],
['sophocles', 'elektra', [[0, 1353]]],
['sophocles', 'oedipus tyrannos', [[0, 1530]]],
['unknown', 'batrachomyomachia', [[0, 304]]]
];
function textLengths(corpus) {
var len = [];
for (let i of corpus) {
let x = 0;
if (i.length == 4) {
x = i[3];
} else {
list = Array.from(i[2]);
for (let j of i[2]) {x += j[1]};
}
len.push(x);
}
return(len);
}
function weightedRandom(items, weights) {
const cumulativeWeights = [];
for (let i = 0; i < weights.length; i += 1) {
cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
}
const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
const randomNumber = maxCumulativeWeight * Math.random();
for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
if (cumulativeWeights[itemIndex] >= randomNumber) {
return items[itemIndex];
}
}
}
function randomLine(corpus) {
length = textLengths(corpus);
let choice = weightedRandom(corpus, length);
const auth = choice[0];
const text = choice[1];
let lns = [];
for (let i of choice[2]) {
let x = i[1];
lns.push(x);
}
let loc = weightedRandom(choice[2], lns)
const bk = loc[0];
const ln = Math.floor(Math.random() * loc[1]) + 1;
let star = '';
if (choice.length == 4) {
sections = 0;
for (let i of choice[2]) {
sections += i[1];
}
avgLen = choice[3] / sections;
provRand = Math.floor(Math.random() * avgLen) + 1;
star = '.' + provRand.toString() + '*';
}
let result = '';
if (bk == 0){
result = auth + ", <em>" + text + "</em> " + ln.toString() + star;
} else {
result = auth + ", <em>" + text + "</em> " + bk.toString() + "." + ln.toString() + star;
}
resultHTML = '<div><h3>';
if(corpus == rlCorp) {
resultHTML += 'reading list';
} else {
resultHTML += 'my list';
}
resultHTML += '</h3><p>' + result + '</p></div>';
return(resultHTML);
}
function corpList(corpus) {
let resultHTML = '<details><summary>';
if(corpus == rlCorp) {
resultHTML += 'reading list</summary>';
} else {
resultHTML += 'my list</summary>';
}
resultHTML += '<ul>'
for (let i of corpus) {
auth = i[0];
text = i[1];
range = '';
for (let j of i[2]) {
if (j[0] == 0) {
range += '1–' + j[1].toString();
break;
} else if (i[2].indexOf(j) < i[2].length - 1) {
range += j[0].toString() + '.1–' + j[1].toString() + ', ';
} else {
range += j[0].toString() + '.1–' + j[1].toString();
}
}
if (i[3]) {
ct = i[3].toString().replace(/'B(?<!'.'d*)(?=('d{3})+(?!'d))/g, ",");
if (i[4]) {
range += ' (approx. ' + ct + ' lns.)'
} else {
range += ' (' + ct + ' lns.)'
}
}
resultHTML += '<li>' + auth + ', <em>' + text + '</em>: ' + range + '</li>';
}
resultHTML += '</ul></details>';
return(resultHTML);
}
let pageHTML = '';
pageHTML += '<div><h2>random passages</h2>';
pageHTML += '<div>' + randomLine(rlCorp) + '</div>';
pageHTML += '<div>' + randomLine(myCorp) + '</div>';
pageHTML += '</div>';
pageHTML += '<div><h2>corpus information</h2>'
pageHTML += corpList(rlCorp, 'reading list');
pageHTML += corpList(myCorp, 'personal list');
pageHTML += '</div>'
const element = document.getElementById('result');
if (element) element.innerHTML = pageHTML;
Again, note that this is out-of-date relative to the JS, but I'm including it just in case.
import random
from re import sub
latCorp = [\
['catullus', 'carmen', [[0, 116]], 2328], \
['horace', 'ars poetica', [[0, 476]]], \
['horace', 'carmina', [[1, 38], [2, 20], [3, 30]], 2452], \
['horace', 'epistulae', [[2, 2]], 486], \
['horace', 'sermones', [[1, 10]], 1029], \
['juvenal', 'satires', [[0, 16]], 3871], \
['lucan', 'pharsalia', [[1, 695], [2, 736], [3, 762], [4, 824], [5, 815], [6, 830], [7, 872], [8, 872], [9, 1108], [10, 546]]], \
['lucretius', 'de rerum naturae', [[1, 1117], [2, 1174], [3, 1094], [4, 1287], [5, 1457], [6, 830]]], \
['ovid', 'amores', [[1.1, 34], [1.2, 52], [1.3, 26], [1.4, 70], [1.5, 26], [1.6, 74], [1.7, 68], [1.8, 114], [1.9, 46], [1.10, 64], [1.11, 28], [1.12, 30], [1.13, 48], [1.14, 56], [1.15, 42]]], \
['ovid', 'fasti', [[4, 954]]], \
['ovid', 'metamorphoses', [[1, 779], [8, 884]]], \
['plautus', 'amphitruo', [[0, 1146]]], \
['plautus', 'casina', [[0, 1018]]], \
['plautus', 'menaechmi', [[0, 1162]]], \
['propertius', 'elegies', [[1.1, 38], [4.4, 94], [4.6, 86], [4.8, 88]]], \
['seneca minor', 'phaedra', [[0, 1280]]], \
['statius', 'silvae', [[4.0, 37], [4.1, 47], [4.2, 67], [4.3, 163], [4.4, 105], [4.5, 60], [4.6, 109], [4.7, 56], [4.8, 62], [4.9, 55]]], \
['sulpicia', 'elegy', [[0, 6]], 40], \
['terence', 'adelphoe', [[0, 997]]], \
['terence', 'phormio', [[0, 1055]]], \
['tibullus', 'elegy', [[1.1, 78], [1.3, 94], [1.4, 84], [2.3, 80], [2.5, 122]]], \
['vergil', 'aeneid', [[1, 756], [2, 804], [3, 718], [4, 705], [5, 871], [6, 901], [7, 865], [8, 817], [9, 818], [10, 908], [11, 915], [12, 952]]], \
['vergil', 'eclogues', [[1, 83], [2, 73], [3, 111], [4, 63]]], \
['vergil', 'georgics', [[1, 514], [4, 566]]]\
]
latLen = []
for i in latCorp:
x = 0
if len(i) == 4:
x = i[3]
else:
for j in i[2]:
x += j[1]
latLen.append(x)
grkCorp = [\
['aeschylus', 'agamemnon', [[0, 1673]]], \
['aeschylus', 'choephoroi', [[0, 1075]]], \
['aeschylus', 'eumenides', [[0, 1470]]], \
['apollonius rhodius', 'argonautica', [[3, 1407]]], \
['aristophanes', 'nubes', [[0, 1510]]], \
['aristophanes', 'ranae', [[0, 1533]]], \
['callimachus', 'aetia', [[0, 40]]], \
['callimachus', 'hymn to zeus', [[0, 96]]], \
['callimachus', 'hymn to apollo', [[0, 113]]], \
['callimachus', 'hymn to athena', [[0, 142]]], \
['callimachus', 'hymn to demeter', [[0, 138]]], \
['euripides', 'bacchae', [[0, 1392]]], \
['euripides', 'hippolytus', [[0, 1466]]], \
['euripides', 'medea', [[0, 1419]]], \
['hesiod', 'theogony', [[0, 1022]]], \
['homer', 'iliad', [[1, 611], [2, 877], [3, 461], [4, 544], [5, 909], [6, 529], [9, 713], [16, 867], [18, 617], [19, 424], [22, 515], [23, 897], [24, 804]]], \
['homer', 'odyssey', [[1, 444], [5, 493], [6, 331], [7, 347], [8, 586], [9, 566], [10, 574], [11, 640], [12, 543], [19, 604], [21, 434], [22, 501], [23, 548]]], \
['menander', 'dyscolus', [[0, 964]]], \
['pindar', 'isthmian ode', [[7, 51]]], \
['pindar', 'olympian ode', [[1, 116], [2, 100], [7, 95]]], \
['pindar', 'pythian ode', [[1, 100], [8, 100], [10, 72]]], \
['sophocles', 'antigone', [[0, 1353]]], \
['sophocles', 'oedipus coloneus', [[0, 1779]]], \
['sophocles', 'oedipus tyrannos', [[0, 1530]]], \
['theocritus', 'idyll', [[1, 152], [2, 166], [7, 157], [11, 81]]]\
]
grkLen = []
for i in grkCorp:
x = 0
if len(i) == 4:
x = i[3]
else:
for j in i[2]:
x += j[1]
grkLen.append(x)
lang = ""
if lang == 'latin' or lang == 'lat':
corpus = latCorp
length = latLen
elif lang == 'greek' or lang == 'grk':
corpus = grkCorp
length = grkLen
else:
corpus = latCorp + grkCorp
length = latLen + grkLen
choice = random.choices(corpus, length)
choice = choice[0]
auth = choice[0]
text = choice[1]
loc = random.choice(choice[2])
bk = loc[0]
ln = random.randrange(1, loc[1])
if len(choice) == 4:
star = '*'
else:
star = ''
if bk == 0:
result = auth + ", " + text + " " + str(ln) + star + '\n'
else:
result = auth + ", " + text + " " + str(bk) + "." + str(ln) + star + '\n'
print(result)