The Million Dungeon Project

In January I adapted the Advanced Dungeons and Dragons 1st Edition Random Dungeon Generator to code, which is available here:-

https://github.com/bluetyson/ADnD1e-Random-Dungeon-Generator

This launches a container that you can run the code in via the simple instructions and make whatever sorts of dungeons you like.

https://mybinder.org/v2/gh/bluetyson/ADnD1e-Random-Dungeon-Generator/HEAD

Using this random dungeon generator I have made a million dungeons – all with 10 as the Periodic Check number.

So they will be quite small in general.

Here you can randomly choose one of those:-

https://cosmicheroes.space/cgi-bin/dungeoncount5.pl

and here you can pick your own from 0 to 999999

https://cosmicheroes.space/cgi-bin/dungeoncount4.pl

Advanced Dungeons and Dragons Monster Clustering

A few years ago I looked at this, have found it again, so here’s a start.

Not, many mess categories as you know, but here’s a plot from 2 that will fit on a screen.

Vermin and plants, some animals and common humanoids and your garden variety monstrous humanoids when you lump it at this very joined together level.

Kmodes method, just have to find where I put that part.

https://github.com/bluetyson/ADnD-Monster-Cluster-Analysis/blob/main/ADnD-Monster-Clustering-Basic.ipynb

Map-Generation part 2

I have started looking at adapting this for HexDescribe type reasons.

For example: – https://github.com/bluetyson/Map-Generation/blob/main/src/map.py

https://chgowiz-games.blogspot.com/ is working on a epic random generator for a campaign you can read about at his blog – using HexDescribe https://campaignwiki.org/hex-describe

You can see some examples here https://docs.google.com/spreadsheets/d/1G8AZf2tBOtGYr5AEKis7_AZYpJ1_T18LFYp_mi_rikY/edit#gid=1019219400 and https://pastebin.com/raw/W9zgH3pN

Borrowing his business classifications for an example for the Map-Generation software:

The output for this program is GeoJSON, so thanks to QGIS – which is great open source software.

closeup example

Randomly perturbed Voronoi generation seems to work well for the random wanderiness of towns or cities that spring up.

The original code has churches, monasteries and Cathedrals. Pretty sure our D&D type games don’t need that sort of building type overload.

As well as GeoJSON – it has a viewer script that is basically QTing a PNG, so your usual plt.savefig() before that will get you that version of the output, saved, too. Need to make one that is labelled – maybe a geopandas .

Converting this to perl directly would take a bit of work – no higher level geospatial apis like shapely around there, so would have to redo in gdal directly :- https://metacpan.org/pod/Geo::GDAL. Probably easier to wrap, for fun.

Map Generation – Cities and Towns

https://github.com/LAVS-TM/Map-Generation

A program that can generate cities and towns, medieval Voronoi style. Along with a viewer that can produce graphcis the base output is json, which gives you vector data to use with other things. Very nice!

Sample city

Here is an example of a possible configuration for the city to be generated:

city = City(10000, 10000, has_walls=True, has_castle=True)
tools.json(city, '/generated_city/city.json')

ChatGPT uses – random tables

Things along these lines:

please continue as above, starting at 22
22: {
'6th Level': 'Spiritwrack',
'7th Level': None,
'8th Level': None,
'9th Level': None
},
23: {
'6th Level': 'Stone To Flesh',
'7th Level': None,
'8th Level': None,
'9th Level': None
},
24: {
'6th Level': 'Tenser's Transformation',
'7th Level': None,
'8th Level': None,
'9th Level': None
}
}




please continue as above, starting at 17
Sure, here is the python dictionary for the table starting at level 17:

python
Copy code
spells = {
    6: {
        '17': 'Otiluke\'s Freezing Sphere',
        '18': 'Part Water',
        '19': 'Project Image',
        '20': 'Reincarnation',
        '21': 'Repulsion',
        '22': 'Spiritwrack',
        '23': 'Stone To Flesh',
        '24': 'Tenser\'s Transformation'
    },
    7: {
    },
    8: {
    },
    9: {
    },
}
Please note that the numbers in the outermost dictionary are the spell level and the numbers in the innermost dictionaries are the spell numbers.





can you give me the full 7th 8th and 9th levels as above?
Sure, here is the full python dictionary for the 7th, 8th, and 9th levels:

python
Copy code
{
    7th Level: {
    '1': 'Anti-Magic Shell',
    '2': 'Bigby's Grasping Hand',
    '3': 'Bigby's Forceful Hand',
    '4': 'Control Weather',
    '5': 'Death Spell',
    '6': 'Disintegrate',
    '7': 'Enchant An Item',
    '8': 'Extension III',
    '9': 'Geas',
    '10': 'Glassee',
    '11': 'Globe Of Invulnerability',
    '12': 'Guards And Wards',
    '13': 'Invisible Stalker',
    '14': 'Legend Lore',
    '15': 'Lower Water',
    '16': 'Monster Summoning IV',
    '17': 'Move Earth',
    '18': 'Otiluke's Freezing Sphere',
    '19': 'Part Water',
    '20': 'Project Image',
    '21': 'Reincarnation',
    '22': 'Repulsion',
    '23': 'Spiritwrack',
    '24': 'Stone To Flesh',
    '25': 'Tenser's Transformation'
    },
    8th Level: {
    '1': 'Antipathy/Sympathy',
    '2': 'Cacodemon',
    '3': 'Bigby's Clenched Fist',
    '4': 'Bigby's Crushing Hand',
    '5': 'Charm Plants',
    '6': 'Clone',
    '7': '



please finish the table from 3 responses ago that ended at 21: {
'Move Earth': '',
'Vanish': '',
'Trap The Soul': '',
},
22: {
'Otiluke's Freezing Sphere': '',
'Part Water': '',
'Project Image': '',
'Reincarnation': '',
'Repulsion': '',
'Spiritwrack': '',
'Stone To Flesh': '',
'Tenser's Transformation': '',
},
23: {
'Anti-Magic Shell': 'Bigby's Grasping Hand',
'Bigby's Forceful Hand': 'Cacodemon',
'Control Weather': 'Charm Plants',
'Death Spell': 'Delayed Blast Fireball',
'Disintegrate': "Drawmij's Instant Summons",
'Enchant An Item': 'Duo-Dimension',
'Extension III': 'Limited Wish',
'Geas': 'Mass Invisibility',
'Glassee': 'Monster Summoning V',
'Globe Of Invulnerability': "Mordenkainen's Sword",
},




The below, for if I want to rewrite to lazily add to my generic shared host here.

convert this python function to perl     def random_check():
        pc_dict = {}
        pc = roll_dice(1,20)
        
        if pc <= 2:
            pc_dict['direction'] = 'ahead'
            pc_dict['check'] = 6
        elif pc >= 3 and pc <= 5:
            pc_dict['direction'] = 'exit'
            pc_dict['check'] = 6
        elif pc >= 6 and pc <= 10:
            pc_dict['direction'] = 'side'
            pc_dict['check'] = 3
        elif pc >= 11 and pc <= 13:
            pc_dict['direction'] = 'turn'
            pc_dict['check'] = 3
        elif pc >= 14 and pc <= 16:
            pc_dict['direction'] = 'room'
            pc_dict['check'] = 3
        elif pc == 17:
            pc_dict['direction'] = 'level'
            pc_dict['check'] = 'up_down'
            if LEVEL == "N": #make this what you like or could be config
                pc_dict['direction'] = 'room'
                pc_dict['check'] = 3
        elif pc == 18:
            pc_dict['direction'] = 'stop'
            pc_dict['check'] = 'spy_things'
        elif pc == 19:
            pc_dict['direction'] = 'bad_things'
            pc_dict['check'] = 3
        elif pc == 20:
            pc_dict['direction'] = 'random_encounter'
            pc_dict['check'] = 'roll_again'
            

        if VERBOSITY:
            print("RANDOM_CHECK",pc, pc_dict['direction'])

        #test purposes
        #pc_dict['direction'] = 'ahead'

        return pc_dict
sub random_check {
my $pc_hash = {};
my $pc = roll_dice(1,20);

if ($pc <= 2) {
$pc_hash->{'direction'} = 'ahead';
$pc_hash->{'check'} = 6;
} elsif ($pc >= 3 && $pc <= 5) {
$pc_hash->{'direction'} = 'exit';
$pc_hash->{'check'} = 6;
} elsif ($pc >= 6 && $pc <= 10) {
$pc_hash->{'direction'} = 'side';
$pc_hash->{'check'} = 3;
} elsif ($pc >= 11 && $pc <= 13) {
$pc_hash->{'direction'} = 'turn';
$pc_hash->{'check'} = 3;
} elsif ($pc >= 14 && $pc <= 16) {
$pc_hash->{'direction'} = 'room';
$pc_hash->{'check'} = 3;
} elsif ($pc == 17) {
$pc_hash->{'direction'} = 'level';
$pc_hash->{'check'} = 'up_down';
if ($LEVEL eq "N") {
$pc_hash->{'direction'} = 'room';
$pc_hash->{'check'} = 3;
}
} elsif ($pc == 18) {
$pc_hash->{'direction'} = 'stop';
$pc_hash->{'check'} = 'spy_things';
} elsif ($pc == 19) {
$pc_hash->{'direction'} = 'bad_things';
$pc_hash->{'check'} = 3;
} elsif ($pc == 20) {
$pc_hash->{'direction'} = 'random_encounter';
$pc_hash->{'check'} = 'roll_again';
}

if ($VERBOSITY) {
print "RANDOM_CHECK $pc $pc_hash->{'direction'}\n";
}

return $pc_hash;
}




I had already done monsters before I thought of this - and they were standard mostly, so a parser did all 10 levels reasonbly well.
Did magic items and spells with ChatGPT assistance.  Possibly not lots and lots quicker - but easier on the fingers and wrists.

AD&D 1e Random Dungeon Generator Binder

So you can run this in a browser:

https://mybinder.org/v2/gh/bluetyson/ADnD1e-Random-Dungeon-Generator/HEAD

  • click the above to fire up a web container environment that lets you run this in your browser
  • you get jupyterlab, just click on the terminal link at the lower left and then type ‘python dungeon.py 1’ [or however many rolls as you like] and the files created will also be in the main directory, one for each dungeon level. 

AD&D 1e Random Dungeon Generator Version 0.3

I just update to this and i just generated a short dungeon. For reference, this is ’10 rolls’

See the GitHub README for a bit more details.

https://github.com/bluetyson/ADnD1e-Random-Dungeon-Generator

DUNGEON
BBBBBCCHlCOCCCBB
BBCCCCCHlCBBBBBB
BBCCCCwmCBBBBBBB
BBBBBCBCBBBBBB
BBBBBCCCCBBBBB
BBBBBCCCwmBBBBB
BBBBCBBBBBBBBB
BBBCBBBBBBBBBB
BBCBBBBBBBBBBB
BBCBBBBBBBBBBB
BBCBBBBBBBBBBB
BBCBBBBBBBBBBB
BBCBBBBBBBBBBB
BBCBBBBBBBBBBB
BDsdCDBBBBBBBBBB
R2R2R1R1R1R1R1R1R1R1R1BBB
R2R2R1R1R1R1R1R1R1R1R1BBB
R2R2R1R1R1R1R1R1R1R1R1BBB
R2R2R1R1R1R1R1R1R1R1R1CCHlC
BBR1mR1R1R1R1R1R1R1R1CCHlC
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1gR1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBR1R1R1R1R1R1R1R1R1BBB
BBBBBBBBBBCBBB
BBBBBBBBBBCBBB
BBBBBBBBBBCBBB

Data: 1

{‘shape’: ‘R’, ‘size’: [9, 13], ‘water’: ‘N’, ‘fancy_shape’: ‘H’, ‘fromdoor’: ‘Y’, ‘contents’: {‘monster’: {‘level’: 1, ‘type’: ‘Orc’, ‘No’: 8, ‘XP’: 14, ‘lair’: ‘35%’, ‘treasure_individual’: [‘L’], ‘treasure_lair’: [‘C’, ‘O’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘S’]}, ‘treasure’: {‘type’: {‘copper’: 0, ‘silver’: 0, ‘electrum’: 0, ‘gold’: 275, ‘platinum’: 0, ‘gems’: 0, ‘jewellery’: 0, ‘magic’: 0}, ‘gems_list’: [], ‘jewellery_list’: [], ‘magic_list’: [], ‘magic_xp’: [], ‘magic_values’: [], ‘store’: ‘Loose’, ‘protection’: ‘hide’, ‘hide’: ‘Secret: Loose stone – in wall’}}, ‘exits’: 2, ‘exitlocations’: {1: ‘O’, 2: ‘R’}, ‘exitdirections’: {1: ‘A’, 2: ‘A’}}

Key: 1

monster:{‘level’: 1, ‘type’: ‘Orc’, ‘No’: 8, ‘XP’: 14, ‘lair’: ‘35%’, ‘treasure_individual’: [‘L’], ‘treasure_lair’: [‘C’, ‘O’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘Q’, ‘S’]}
LairTry:32 from 35 is in lair: True
Monster Lair Treasure:
{‘copper’: 10000, ‘silver’: 0, ‘electrum’: 0, ‘gold’: 0, ‘platinum’: 0, ‘gems’: 6, ‘jewellery’: 0, ‘magic’: 0}
{‘gems’: [100, 1000, 60.0, 100, 200, 20], ‘jewellery’: [], ‘magic’: [], ‘magic_list’: [], ‘magic_xp’: [], ‘magic_values’: []}

treasure:{‘type’: {‘copper’: 0, ‘silver’: 0, ‘electrum’: 0, ‘gold’: 275, ‘platinum’: 0, ‘gems’: 0, ‘jewellery’: 0, ‘magic’: 0}, ‘gems_list’: [], ‘jewellery_list’: [], ‘magic_list’: [], ‘magic_xp’: [], ‘magic_values’: [], ‘store’: ‘Loose’, ‘protection’: ‘hide’, ‘hide’: ‘Secret: Loose stone – in wall’}

Data: 2

{‘shape’: ‘R’, ‘size’: [2, 4], ‘fromdoor’: ‘Y’, ‘contents’: {’empty’: ‘Y’}, ‘exits’: 2, ‘exitlocations’: {1: ‘R’, 2: ‘R’}, ‘exitdirections’: {1: ’45AB’, 2: ‘A’}}

Key: 2

Empty

Wandering Monster: 0

{(-3, 2, -1): {‘level’: 1, ‘type’: [‘Character’, {1: {‘class’: ‘FIGHTER’, ‘level’: 1, ‘race’: ‘Human’, ‘multi’: ‘N’, ‘magic_items’: []}, 2: {‘class’: ‘FIGHTER’, ‘level’: 1, ‘race’: ‘Human’, ‘multi’: ‘N’, ‘magic_items’: []}, 3: {‘class’: ‘FIGHTER’, ‘level’: 1, ‘race’: ‘Gnome’, ‘multi’: ‘Y’, ‘multi_no’: 2, ‘magic_items’: []}, 4: {‘class’: ‘FIGHTER’, ‘level’: 1, ‘race’: ‘Dwarf’, ‘multi’: ‘N’, ‘magic_items’: []}, 5: {‘class’: ‘MAGIC-USER’, ‘level’: 1, ‘race’: ‘Half-elf’, ‘multi’: ‘Y’, ‘multi_no’: 2, ‘magic_items’: []}, 6: {‘class’: ‘man-at-arms’}, 7: {‘class’: ‘woman-at-arms’}, 8: {‘class’: ‘woman-at-arms’}, 9: {‘class’: ‘woman-at-arms’}}], ‘No’: 9, ‘XP’: 0, ‘lair’: ‘0%’, ‘treasure_individual’: [], ‘treasure_lair’: []}}
XP:0

Wandering Monster: 1

{(0, 5, -1): {‘level’: 1, ‘type’: ‘Zombie’, ‘No’: 2, ‘XP’: 38, ‘lair’: ‘0%’, ‘treasure_individual’: [], ‘treasure_lair’: []}}
XP:76

Monster Total XP:112

Monster Total Treasure:{‘copper’: 10000, ‘silver’: 10000, ‘electrum’: 0, ‘gold’: 0, ‘platinum’: 0, ‘gems’: 6, ‘jewellery’: 0, ‘magic’: 0}

Monster Total Valuations:{‘gems’: [100, 1000, 60.0, 100, 200, 20, 100, 1000, 60.0, 100, 200, 20], ‘jewellery’: [], ‘magic’: [], ‘magic_xp’: [], ‘magic_values’: [], ‘magic_list’: []}

Wandering Monster Total XP:76

Wandering Monster Total Treasure:{‘copper’: 0, ‘silver’: 0, ‘electrum’: 0, ‘gold’: 0, ‘platinum’: 0, ‘gems’: 0, ‘jewellery’: 0, ‘magic’: 0}

Wandering Monster Subtable:[‘human-character’, ‘monster’]

Room Total Treasure:{‘copper’: 0, ‘silver’: 0, ‘electrum’: 0, ‘gold’: 275, ‘platinum’: 0, ‘gems’: 0, ‘jewellery’: 0, ‘magic’: 0}

Room Total Gems:0

Room Total Jewellery:0

Room Total Magic:0

Total Treasure: {‘copper’: 10000, ‘silver’: 10000, ‘electrum’: 0, ‘gold’: 275, ‘platinum’: 0, ‘gems’: 6, ‘jewellery’: 0, ‘magic’: 0}

Coins: 1375.0
Gems: 2960.0
Jewellery: 0
Magic: 0
Total Gold Equivalent: 4335.0
%d bloggers like this: