The Vic 3 Mod: Heart of Appalachia Randomized Map Generator Script¶

What is this script?¶

It allows you to randomly generate resources for My Mod: Heart of Appalachia: An Atompunk Mod. Using this script, you can produce randomized resource distribution for your playthrough.

Just run this script, and copy the output to mod folder's map_data folder. Be sure to backup and clear map_data content beforehand.

What is needed to run this script?¶

You need a Python Notebook Kernel (Or, at the very basic, Python). You need package re, os, itertools, and random.

Advanced Settings.¶

If you wish to do maps for other mod, you can copy their map_data file under the same directory as this script. As long as their file name begins with a number, it should work.

You can also change the random generator as you go through this script.

Pre-Sets¶

There are some pre-sets described below. You can also find certain pre-set generations under the directory "Map Playground"

Random Seeds and Replicatability¶

This code section uses random seeds to semi-randomly generate resource distribution of maps. Therefore, unless the vanilla map have changed, a same random seed would always give the same resource distribution, hence the replicatability.

You can set if you want to use a random seed or a true random (through randomly generate a random seed). You can always give a copy of the random seed when running the script.

Advanced Settings¶

You can change the probability distribution for resource level generation or make some other changes, should you wish.

Copyright¶

If you wish to share this script, with or without modifying, you must refer to original author and source: @TelepathingTilapia on Steam, or past Mod Link: https://steamcommunity.com/sharedfiles/filedetails/?id=2921015600

Settings¶

Explanations:¶

Resource Generation Probability:¶

Probability a resource is generated in a state region.

Resource Level Distribution:¶

Probability Distribution of the levels of resource generated. For example, Uniform[40, 140] means that the generated resouce should be of level 40-140, uniformly distributed. Everything here is rounded as integer.

This value is Independent from the result for Resource Generation Probability.

Expected Level if Generated:¶

Expected level of resource if there's generation

Expected Level per State Region¶

Expected levels of resource in a state region. Formula is Expected Level if Generated * Resource Generation Probability.

Basic Setting:¶

Resource Types Uranium Mines Rad-Fungus Farm Ancient Arsenal Scavenge Rad-Fungus Colony
Resource Generation Probability 40% 12% 8% 6%
Resource Level Distribution Unif(60, 140) Unif(10, 24) + 20% chance add 10 levels Unif(4, 16) + 20% chance add 6 levels 1
Expected Level if Generated 100 19 11.2 1
Expected Level per State Region 40 2.28 0.896 0.06

Unif represents a uniform distribution.

These settings can be changed later in the code.

Random Seeds¶

You can change the random seed to anything you like in the code cells below titled with "Random Seed Generation".

You may input your own random seed, or use the default where a true random would be used.

The map used for the mod is generated with random seed 114514.

Regional Settings¶

Besides Uranium is randomly generated, each region have their own set of multipliers for more resources.

All multiplier values will be rounded by the end.

Explanations:¶

Basic Resource Multiplier:¶

All mines, except mod added ones and oil, are multiplied by this amount.

Oil Resource Multiplier:¶

Oil rig levels multiplied by this amount.

Uranium Mine Chance Multiplier:¶

Multiply the chance of spawning Uranium Mine by this.

Uranium Mine Value Multiplier:¶

Multiply the level of Uranium Mines if generated. Currently NOT IMPLEMENTED.

Region Basic Resource Multiplier Oil Resource Multiplier Uranium Mine Chance Multiplier Uranium Mine Value Multiplier
North America, South America, Central America, Australasia 1.6 1.8 1.4 1
India, East Asia, Sub-Saharan Africa 1.6 1.5 1.25 1
Rest of the World 1.25 1.5 1 1

Advanced Settings/Certain Scenarios¶

Advanced Setting ONLY affects Uranium Mine, Rad-Fungus Farm, and Ancient Arsenal Scavenge.

For *n in the entries refers to: multiply the basic setting by this much.

All these settings have a generated map in the folder. Just copy the files into map_data under Mod's root directory

The Default Setting¶

The default setting is already presented in the game. There's also a backup in the folder, in case you realized that you need a backup after overwriting the files with one of the scenarios.

This is the standard setting with no modifications, and should work for a balanced game.

Rich Boi Settings¶

Welcome to the good times! There's Uranium mines and fungus farms everywhere, you even have more ancient left-over arsenals to scavenge from! You virtually have more fuel and materials than you will ever need, and this will give an easy game for any major powers.

If you are not concerned by particularly lucky or skillful players of smaller nations to acquire Nuclear Weapon which they otherwise cannot, that is.

Modification:

Rich Boi: Resource Multiplier Uranium Mines Rad-Fungus Farm Ancient Arsenal Scavenge
Resource Generation Probability 60% 18% 12%
Resource Level Distribution *1 *1 *1

Poor Boi Settings¶

This is some hard times. Veins of Uranium Mines are extremely hard to find, and even if you come across them they are hidden in remote mountains or polar area or deep in the jungles. Rad-Fungus--the gift of the nature that enriches radioactive minerals--are found in small numbers. Even ancient arsenals have a smaller cache of leftover nukes!

There are still hope--Use a flotilla of ocean mining platforms. The question is, can you make it before resource run very dry?

Poor Boi: Resource Multiplier Uranium Mines Rad-Fungus Farm Ancient Arsenal Scavenge
Resource Generation Probability 25% 9% 6%
Resource Level Distribution *0.5 *0.5 *0.5

Wasteland Settings¶

It is unplausible why the Creator of this World, who has always been regarded as kind-hearted, benevolent, venerable, wise, [Insert more praise here], and, above all, caring for the well-being of the life of pops in the game, would create such a horrible world, where resource are scattered and hard to come-by, and humanity have to use whatever is left in this wasteland.

The mines have terrible conditions and are always remote, some nations don't even have Uranium Mines of their own. However, it is said that humanity always find a way.

But still, how could this happen? Is it the bad humor of the creator of the world? Is it just bad luck?

Or, perhaps, is it 1,000 years after another game, where Earth is devastated, perhaps for the first or fifth or eleventh or twenty-seventh or more times, where humanity just live on the tiny bit of leftovers that are, surprisingly, unexploited by the previous civilization and their devastating war efforts?

Well, if it is indeed leftovers, at least we have many obsolete nukes in them.

Wasteland: Resource Multiplier Uranium Mines Rad-Fungus Farm Ancient Arsenal Scavenge
Resource Generation Probability 10% 5% 10%
Resource Level Distribution *0.2 *0.2 *1

Economy of Scale¶

A scenario very similar to "wasteland", except that resource are actually abundant. Veins are found in small areas, and people awe in the size of fungus network that never seem to move over time. The few places that are blessed (or cursed) with rich Uranium Mines will have amazing productivity while other less fortunate land will be barren.

To excel in this scenario, one need to be very lucky or very malicious. You are either very lucky to have those rich mines within your domain, or very malicious and take those land. Or both, if you can.

Economy of Sacle: Resource Multiplier Uranium Mines Rad-Fungus Farm Ancient Arsenal Scavenge
Resource Generation Probability 10% 3% 2%
Resource Level Distribution *3 *3 *3

Scenarios¶

These are some scenarios that can be implemented. I have not included generated maps for them, but a few tweaks in code should seal the deal.

Very Abnormal Distribution:¶

Uranium Mine resources level are generated with Normal(100, 1600) (1600 as variance so 40 Standard Deviation)

The Heavenly Kingdoms¶

Uranium are found exclusively in East Asia.

Drunk Mapper¶

Drunk, the mapper decide to do a random walk. The first Uranium Mine hit is at 100 levels, but everytime the Uranium Mine resource is generated, it is previous generation + Normal (0, 100) where 100 is the variance.

Future Ideas¶

Buff/Debuff the size of mines based on arable land¶

Change the resources to discoverable¶

If this is implemented, maybe you will want to run the script and start a new game, to maximize the surprise. This works best with "Economy of Scale" or "Wasteland" Settings.

The Actual Codes¶

In [ ]:
import re
import os
import itertools ## Or whatever that flattens a 2D list to one.
import random

## THese four lines are NOT supposed to be changed!
Basic_Resource_Mult = 1.5 ## Constants: Multiply vanilla resource by this amount
Oil_Resource_Mult = 1.5 ## Multiply oil resouce by this amount
Uranium_Mine_Chance_Mult = 1 ## Multiply the chance of spawning Uranium Mine by this amount
Uranium_Mine_Value_Mult = 1 ## Multiplly the generated level of Uranium mines by this amount

Random Seeds¶

Change random seeds here, by re-defining "Random_Seed_Map_Generation". After changing the seed, set "True_Random" to False for it to successfully run.

A copy of the seed used would be printed here and at the end of documents.

Note that unless your map have over 2^19900 state regions the random seed would be enough for replication, given that no change has been made to the map file you are using AKA it breaks after Paradox or your mod changes the map file by adding or deleting states.

In [ ]:
Random_Seed_Map_Generation = 114514
True_Random = False

if True_Random:
    Random_Seed_Map_Generation = random.randint(0, 2**32 - 1)

random.seed(Random_Seed_Map_Generation)
print("Random Seed used for this generation: ", Random_Seed_Map_Generation)
Random Seed used for this generation:  114514
In [ ]:
## Resource Generation Probabilities
Global_Generation_Prob_Uranium = 0.4 ## Global Base Probability of generating uranium mine deposit in a state region
Global_Generation_Prob_Farm = 0.12 ## Global Base Probability of generating rad-fungus farm deposit in a state region
Global_Generation_Prob_Arsenal = 0.08 ## Global Base Probability of generating ancient arsenal scavenge deposit in a state region


## Resource Level Multiplier
Global_level_Mult_Uranium = 1
Global_Level_Mult_Farm = 1
Global_Level_Mult_Arsenal = 1



## List of mines to be used
Mine_Names_List = ["bg_rad_fungus_colony", "bg_rad_fungus_farm", "bg_uranium_mining", "bg_ancient_arsenal_scavenge"]
In [ ]:
'''
## Additional Scenarios
###############-----RICH BOI-----######################
## Resource Generation Probabilities
Global_Generation_Prob_Uranium = 0.6
Global_Generation_Prob_Farm = 0.18
Global_Generation_Prob_Arsenal = 0.12


## Resource Level Multiplier
Global_level_Mult_Uranium = 1
Global_Level_Mult_Farm = 1
Global_Level_Mult_Arsenal = 1

###############-----POOR BOI-----######################
## Resource Generation Probabilities
Global_Generation_Prob_Uranium = 0.25
Global_Generation_Prob_Farm = 0.09
Global_Generation_Prob_Arsenal = 0.06


## Resource Level Multiplier
Global_level_Mult_Uranium = 0.5
Global_Level_Mult_Farm = .5
Global_Level_Mult_Arsenal = .5

###############-----WASTELAND-----######################
## Resource Generation Probabilities
Global_Generation_Prob_Uranium = 0.10
Global_Generation_Prob_Farm = 0.05
Global_Generation_Prob_Arsenal = 0.10


## Resource Level Multiplier
Global_level_Mult_Uranium = 0.25
Global_Level_Mult_Farm = .25
Global_Level_Mult_Arsenal = 1

###############-----ECONOMY OF SCALE-----######################
## Resource Generation Probabilities
Global_Generation_Prob_Uranium = 0.10
Global_Generation_Prob_Farm = 0.03
Global_Generation_Prob_Arsenal = 0.02


## Resource Level Multiplier
Global_level_Mult_Uranium = 3
Global_Level_Mult_Farm = 3
Global_Level_Mult_Arsenal = 3

'''
Out[ ]:
'\n## Additional Scenarios\n###############-----RICH BOI-----######################\n## Resource Generation Probabilities\nGlobal_Generation_Prob_Uranium = 0.6\nGlobal_Generation_Prob_Farm = 0.18\nGlobal_Generation_Prob_Arsenal = 0.12\n\n\n## Resource Level Multiplier\nGlobal_level_Mult_Uranium = 1\nGlobal_Level_Mult_Farm = 1\nGlobal_Level_Mult_Arsenal = 1\n\n###############-----POOR BOI-----######################\n## Resource Generation Probabilities\nGlobal_Generation_Prob_Uranium = 0.25\nGlobal_Generation_Prob_Farm = 0.09\nGlobal_Generation_Prob_Arsenal = 0.06\n\n\n## Resource Level Multiplier\nGlobal_level_Mult_Uranium = 0.5\nGlobal_Level_Mult_Farm = .5\nGlobal_Level_Mult_Arsenal = .5\n\n###############-----WASTELAND-----######################\n## Resource Generation Probabilities\nGlobal_Generation_Prob_Uranium = 0.10\nGlobal_Generation_Prob_Farm = 0.05\nGlobal_Generation_Prob_Arsenal = 0.10\n\n\n## Resource Level Multiplier\nGlobal_level_Mult_Uranium = 0.25\nGlobal_Level_Mult_Farm = .25\nGlobal_Level_Mult_Arsenal = 1\n\n###############-----ECONOMY OF SCALE-----######################\n## Resource Generation Probabilities\nGlobal_Generation_Prob_Uranium = 0.10\nGlobal_Generation_Prob_Farm = 0.03\nGlobal_Generation_Prob_Arsenal = 0.02\n\n\n## Resource Level Multiplier\nGlobal_level_Mult_Uranium = 3\nGlobal_Level_Mult_Farm = 3\nGlobal_Level_Mult_Arsenal = 3\n\n'
In [ ]:
def Random_Resource_Generator():
  resource_string = []
  uranium_mine_RNG = random.random()
  if uranium_mine_RNG <= Global_Generation_Prob_Uranium * Uranium_Mine_Chance_Mult:
    ## Generate Mine Levels
    raw_result = 40 + round(random.random() * 100)
    resource_string.append(f'        bg_uranium_mining = {round(raw_result * Global_level_Mult_Uranium)}\n')
  rad_fungus_farm_RNG = random.random()
  if rad_fungus_farm_RNG <= Global_Generation_Prob_Farm:
    raw_result = random.randint(10, 24) + 10 if random.random() < 0.2 else random.randint(10, 24)
    resource_string.append(f'        bg_rad_fungus_farm = {round(raw_result * Global_Level_Mult_Farm)}\n')
  ancient_arsenal_RNG = random.random()
  if ancient_arsenal_RNG <= Global_Generation_Prob_Arsenal:
    raw_result = random.randint(4, 16) + 6 if random.random() < 0.2 else random.randint(4, 16)
    resource_string.append(f'        bg_ancient_arsenal_scavenge = {round(raw_result * Global_Generation_Prob_Arsenal)}\n')
  fungus_colony_RNG = random.random()
  if fungus_colony_RNG <= 0.06:
    resource_string.append(f'        bg_rad_fungus_colony = 1\n')
  return resource_string
In [ ]:
def match_updator(match, mult):
  bg_string = match.group(1)
  remaining_text = match.group(3)
  newline = match.group(4)
  modified_number = str(int(float(match.group(2)) * mult))
  modified_line = bg_string + modified_number + remaining_text + newline
  return modified_line
In [ ]:
def chunk_creator(continent_name,
                     Vanilla_Basic_Resource_Mult = 1.5,
                     Vanilla_Oil_Resource_Mult = 1.5):
  files = os.listdir()
  for file in files:
    vanilla_identify_pattern = r"^\d+_{term_A}\.txt$".format(term_A=re.escape(continent_name))
    if re.match(vanilla_identify_pattern, file, re.IGNORECASE):
       Vanilla_File = open(file, "r", encoding='utf-8-sig').readlines()
    else:
      continue
  Vanilla_File_Text_chunk = Vanilla_File
  Resource_Pattern = r'^(\s*bg_\w+\s*=\s*)(\d+(?:\.\d+)?)(.*?)(\n?)$'
  Oil_Pattern = r"^(.*?)type\s*=\s*\"bg_oil_extraction\"\s*(.*)$"
  Amount_Pattern = r'^(\s*.*?_amount\s*=\s*)(\d+)(.*?)(\n?)$'
  Rubber_Pattern = r"^(.*?)type\s*=\s*\"bg_rubber\"\s*(.*)$"
  i = 0
  while i < len(Vanilla_File_Text_chunk):
    line = Vanilla_File_Text_chunk[i]
    if re.match(r".*capped_resources = {.*", line):
      Random_Result = Random_Resource_Generator()
      if len(Random_Result) > 0:
        Vanilla_File_Text_chunk[i+1:i+1] = Random_Result
      i = i + 1
      continue
    ## Skip if contain mod resources
    contains_any = any(sub in str(line) for sub in Mine_Names_List)
    if contains_any:
      i = i + 1
      continue
    match = re.match(Resource_Pattern, line)
    if match:
      Vanilla_File_Text_chunk[i] = match_updator(match, Vanilla_Basic_Resource_Mult)
      i = i + 1
      continue
    match = re.match(Oil_Pattern, line)
    if match:
      match_oil_resource_line = re.match(Amount_Pattern, Vanilla_File_Text_chunk[i + 1])
      if match_oil_resource_line:
        Vanilla_File_Text_chunk[i + 1] = match_updator(match_oil_resource_line, Vanilla_Oil_Resource_Mult)
        i = i + 1
        continue
    match = re.match(Rubber_Pattern, line)
    if match:
      match_rubber_resource_line = re.match(Amount_Pattern, Vanilla_File_Text_chunk[i + 1])
      if match_rubber_resource_line:
        Vanilla_File_Text_chunk[i + 1] = match_updator(match_rubber_resource_line, Vanilla_Basic_Resource_Mult)
        i = i + 1
        continue
    i = i + 1
  return Vanilla_File_Text_chunk
In [ ]:
def compiled_result_writer(Result_text_chunks, continent_name,
                            prefix_modded_file,
                           suffix = ""):
  For_Write = list(itertools.chain(*Result_text_chunks))
  print("Completed FileName:", prefix_modded_file + continent_name + suffix + ".txt")
  with open(prefix_modded_file + continent_name + suffix + ".txt" ,
            'w', encoding='utf-8-sig') as f:
    for line in For_Write:
        f.write(line)
In [ ]:
continent_list = []

files = os.listdir()
for file in files:
     matched = re.search(r"\d+_(\w+)\.txt", file)
     if matched:
        needed_part = matched.group(1)
        continent_list.append(needed_part)

continent_list

for continent in continent_list:
  if continent == 'south_america' or continent == 'north_america' or continent == 'central_america' or continent == 'australasia':
    print(continent + " Class A")
  elif continent == 'india' or continent == 'east_asia' or continent == 'subsaharan_africa':
    print(continent + " Class B")
  else:
    print(continent + " Class C")
west_europe Class C
south_europe Class C
east_europe Class C
north_africa Class C
subsaharan_africa Class B
north_america Class A
central_america Class A
south_america Class A
middle_east Class C
central_asia Class C
india Class B
east_asia Class B
indonesia Class C
australasia Class A
siberia Class C
In [ ]:
for continent in continent_list:
  if continent == 'south_america' or continent == 'north_america' or continent == 'central_america' or continent == 'australasia':
    Resource_Basics_Mult = 1.6
    Resource_Oil_Mult = 1.8
    Uranium_Mine_Chance_Mult = 1.4
  elif continent == 'india' or continent == 'east_asia' or continent == 'subsaharan_africa':
    Resource_Basics_Mult = 1.6
    Resource_Oil_Mult = 1.5
    Uranium_Mine_Chance_Mult = 1.25
  else:
    Resource_Basics_Mult = 1.25
    Resource_Oil_Mult = 1.5
  continent_result = chunk_creator(continent_name = continent,
                                       Vanilla_Basic_Resource_Mult = Resource_Basics_Mult,
                                       Vanilla_Oil_Resource_Mult = Resource_Oil_Mult)
  compiled_result_writer(Result_text_chunks = continent_result,
                       continent_name = continent,
                       prefix_modded_file = "Mod_Nuclear_")
print("-------All Generation Finished-------------")
print("Random Seed used for this generation: ", Random_Seed_Map_Generation)
Completed FileName: Mod_Nuclear_west_europe.txt
Completed FileName: Mod_Nuclear_south_europe.txt
Completed FileName: Mod_Nuclear_east_europe.txt
Completed FileName: Mod_Nuclear_north_africa.txt
Completed FileName: Mod_Nuclear_subsaharan_africa.txt
Completed FileName: Mod_Nuclear_north_america.txt
Completed FileName: Mod_Nuclear_central_america.txt
Completed FileName: Mod_Nuclear_south_america.txt
Completed FileName: Mod_Nuclear_middle_east.txt
Completed FileName: Mod_Nuclear_central_asia.txt
Completed FileName: Mod_Nuclear_india.txt
Completed FileName: Mod_Nuclear_east_asia.txt
Completed FileName: Mod_Nuclear_indonesia.txt
Completed FileName: Mod_Nuclear_australasia.txt
Completed FileName: Mod_Nuclear_siberia.txt
-------All Generation Finished-------------
Random Seed used for this generation:  114514