Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1] 2

Author Topic: DwarfGenManager - Batch script for generating multiple DF worlds automatically!  (Read 8791 times)

NikoKun

  • Bay Watcher
  • 420
    • View Profile

This is a Windows Batch script which automatically generates as many worlds as you want, and also organizes the maps and info files. It can be run while AFK, and is extremely useful for generating old (1000+ years) worlds, because even if DF crashes while generating a world, it will keep trying until it outputs your desired number of worlds.

To use, create a file named GenerateWorlds.bat file in your DF game folder, paste this code into it and save. Or you can name it whatever you want, as long as it's in the game folder, where "Dwarf Fortress.exe" is.
Or you can also download the file directly from:
https://github.com/Nikorasu/DwarfGenManager

Then either, choose one of the preexisting generator presets found in the world_gen.txt, or create your own custom one using the "Design New World with Advanced Parameters" in the game's main menu.

When you run GenerateWorlds.bat, it will ask for that title (spelling/capitalization must be exact), and how many worlds you want it to generate, just make sure your computer has enough space for the number you give it. It'll then load up the game window and start generating. It should do everything automatically at that point, tho the df game window will popup every so often and get in the way if you're watching something while you wait lol, so you might wanna go afk and do something else till it finishes. heh

Code: [Select]
@ECHO OFF
TITLE Generation Manager  - by Nik

IF NOT EXIST "Dwarf Fortress.exe" (
ECHO ERROR: "Dwarf Fortress.exe" not found, place script into same folder.
PAUSE && EXIT /b
)
IF EXIST data\save\region0 (
ECHO ERROR: region0 folder already present, rename/move it then try again.
PAUSE && EXIT /b
)

SETLOCAL EnableDelayedExpansion

FOR /f %%G IN ('find /c "[TITLE:" ^< data\init\world_gen.txt') DO SET gnum=%%G
ECHO There are %gnum% available generator pesets in your world_gen file:
ECHO,
FOR /f "delims=" %%T IN ('find "[TITLE:" ^< data\init\world_gen.txt') DO (
SET gparam=%%T
IF %gnum% LEQ 20 (ECHO !gparam:~8,-1!) ELSE (SET outlist=!outlist!, !gparam:~8,-1!)
)
IF %gnum% GTR 20 ECHO %outlist:~2%
ECHO,

SET /p genparam=Which preset do you want to use?:
SET /p maxcount=How many worlds to generate?:
SET /a wcount=1
SET logfile=GenLog_%date:~10,4%-%date:~4,2%-%date:~7,2%_%time:~0,2%-%time:~3,2%.txt
SET logfile=%logfile: =%

:GEN
ECHO, && ECHO,
ECHO Generating World %wcount%..

"Dwarf Fortress.exe" -gen 0 RANDOM "%genparam%"

IF NOT EXIST data\save\region0 (
ECHO Something happened, world was not generated! Trying again..
TIMEOUT 10 /nobreak
GOTO GEN
)

FOR %%F IN (region0*world_history.txt) DO SET histfile=%%~nxF
SET /p worldname=<%histfile%
ECHO,
ECHO World name is %worldname%. Organizing files now..
ECHO,
optipng -q -zc9 -zm9 -zs0 -f0 region0*.bmp
DEL region0*.bmp
MD data\save\region0\info
MOVE region0* data\save\region0\info
REN "data\save\region0\info\region0-world_gen_param.txt" "%worldname: =%-world_gen_param.txt"

SET charin=%worldname%
SET charout=
SET map=abcdefghijklmnopqrstuvwxyz

:CHARFIX
IF NOT DEFINED charin GOTO ENDFIX
FOR /f "delims=* eol=*" %%C IN ("!charin:~0,1!") DO (
IF "!map:%%C=!" NEQ "!map!" SET charout=!charout!%%C
)
SET charin=%charin:~1%
GOTO CHARFIX
:ENDFIX
SET worldname=%charout%

:DUPCHECK
IF EXIST data\save\%worldname% (
SET worldname=%worldname%_2
GOTO DUPCHECK
)

ECHO %worldname%  %histfile:~8,5%>>%logfile%
ECHO,>>%logfile%
FOR %%P IN (data\save\region0\info\region0*world_sites_and_pops.txt) DO SET popfile=%%~fsP
FOR /f %%L IN ('find " Dwarves" ^< %popfile%') DO SET lPop=         %%L
IF NOT DEFINED lPop SET lPop=         0
ECHO Dwarves: %lPop:~-9% >> %logfile%
SET lPop=
FOR /f %%L IN ('find " Goblins" ^< %popfile%') DO SET lPop=         %%L
IF NOT DEFINED lPop SET lPop=         0
ECHO Goblins: %lPop:~-9% >> %logfile%
SET lPop=
FOR /f %%L IN ('find " Elves" ^< %popfile%') DO SET lPop=         %%L
IF NOT DEFINED lPop SET lPop=         0
ECHO Elves:   %lPop:~-9% >> %logfile%
SET lPop=
FOR /f %%L IN ('find " Humans" ^< %popfile%') DO SET lPop=         %%L
IF NOT DEFINED lPop SET lPop=         0
ECHO Humans:  %lPop:~-9% >> %logfile%
SET lPop=
FOR /f %%L IN ('find " Kobolds" ^< %popfile%') DO SET lPop=         %%L
IF NOT DEFINED lPop SET lPop=         0
ECHO Kobolds: %lPop:~-9% >> %logfile%
SET lPop=
FOR /f %%L IN ('find /c ", tower" ^< %popfile%') DO SET tCount=         %%L
ECHO Towers:  %tCount:~-9% >> %logfile%
FOR /f "delims=" %%L IN ('find "Total: " ^< %popfile%') DO SET tcPop=%%L
SET tcPop=         %tcPop:~8%
ECHO TotalPop:%tcPop:~-9% >> %logfile%
ECHO,>>%logfile%
ECHO,>>%logfile%

REN data\save\region0 %worldname%

IF %wcount% EQU %maxcount% (
ECHO, && ECHO,
ECHO All %wcount% worlds complete. Summary saved to: %logfile%
ECHO,
PAUSE && EXIT /b
)

ECHO,
ECHO World %wcount% done. Waiting in case user wants to abort..
TIMEOUT 20 /nobreak
SET /a wcount+=1
GOTO GEN

REM script by Nik - https://github.com/Nikorasu/DwarfGenManager

If anyone has any suggestions to improve this, feel free to let me know!

User Ralpha created a similar Linux version, with some more advanced legends exporting features and dfhack implementation, which I'll try to add to this script eventually. ;)
https://gitlab.com/df_storyteller/df-worldgen

UPDATE 5/27/20 : Added a fix for special characters in the world-name which the game couldn't load. Also added an error check and fixed a typo.

UPDATE 6/2/20 : New feature! Now it displays a list of available world_gen presets before the question!

UPDATE 6/10/20 : Another New Feature! This version creates a timestamped GenLog file in the df folder, containing a summary of the populations and tower-count for each world generated that session. I tried to format it to make it easier to read, and all in one place, hopefully this will help users narrow-down which worlds they want to keep. Previous version still available as a Release on the GitHub page.

UPDATE 6/20/20 : Added an error check, then removed it later same day, was unnecessary. heh Also tweaked/fixed the code here and there, and added the world's year to the GenLog. Also, fixed a bug with the genlog variables not resetting to 0.

UPDATE 6/30/2020 : Put up a new Release marked r2, which includes the GenLog feature and should be bug-free, in case people were waiting on that. Also switched to r1 style versioning, and added a ReadMe to the GitHub.
« Last Edit: June 30, 2020, 10:00:00 am by NikoKun »
Logged

Starver

  • Bay Watcher
    • View Profile

A trick I use, for terminating loops at a 'good' point rather than arbitrarily or with the user forced to wait for a given juncture in the cycle and then catch it in time with a Ctrl-C, is to do an IF EXISTS ENDLOOPFILE THEN LOOPQUIT. The end-loop file can be called anything, as can the LOOPQUIT.BAT, which you call without a CALL to irreversibly pass control and can either be empty, sign off with an echo or even do tidying up such as DEL ENDLOOPFILE as it's done its job (if you run simultaneous identical batch-files in parallel, this would normally stop just the next one to get to that point in the cycle, which is sometimes handy).

Either manually or via a side-batch create the placeholder ENDLOOPFILE whenever you want, and leave it to halt itself.


Not that it matters much when the pause delay you give is jot much timebat all compared to worldgen (assuming you can concentrate on paying attention at the right time, before having to wait for another cycle) but it would otherwise speed things up to not have it sat idle for 30 seconds at a time.


Other than that, very workmanlike (.CMD is normally another valid extension for a batch-file, which is 'prefered' these days in some circles, but I haven't fully checked if that changes some of the newer command extension stuff like ERRORLEVEL setting) and though I'd have done some things differently after some incessant fiddling (perhaps further feature-creeping) by eye it looks Ok.
Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

Oh nice idea! I'll have to try adding that functionality later. Should be really simple, just a check for the end-file, maybe another file to activate/stop the script.. like you said. ;) Tho your idea is an nice improvement, the reasoning behind the 20 second delay is basically because if you're running this program afk, an additional 20 seconds here and there doesn't really hurt much.. The player can force quit at any time, it's just a brief moment to cleanly quit, if you happen to catch it. Quitting at other times just means you might have to move the info files yourself. I figured since the program doesn't run infinitely, it just asks for a specific number of maps to make, in most cases it will finish before you get back, as long as you picked a manageable number.

BTW, someone is trying to tweak my script, to add multi-instance/multi-thread support, basically so it can generate multiple maps simultaneously, if your interested:
https://pay.reddit.com/r/dwarffortress/comments/gmc56q/generate_multiple_worlds_while_afk_with_this/fr3qewd/
Edit: Seems he ran into issues, I might try to get it working eventually.

Not sure if I'll edit the OP just to post it there, but I've updated the GitHub with better descriptions and an example output image in the readme.
This is what the world folders look like after being generated by my script:
« Last Edit: July 02, 2020, 11:17:52 am by NikoKun »
Logged

Ralpha

  • Bay Watcher
    • View Profile

Nice did not know about the "command line.txt" file. That is very useful. Although I do play on Linux so don't have much use for a .bat file. But learned something new.
I think this is the bash script you mentioned? https://www.reddit.com/r/dwarffortress/comments/dw8il/dwarf_fortress_world_generation_script_gnulinux/
Your script might be useful to link to in the future! And thanks for making it!
Do you mind if I upload your script with the bash script above to Github or GitLab so more people will find it in the future?
If you want to upload it there that would be nice too.
(I'll make sure to give you the credit and link back to this thread.)

Not to drag you into the deep end here :P But maybe for the future, you should pick a software License to make sharing end editing it easier.
Something like GPL 3.0 or MIT (both very common licenses) is a good choice (there is also The Unlicense, although not common). Those basically allow people to edit and re-share the code without many restrictions.
Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

Nice did not know about the "command line.txt" file. That is very useful. Although I do play on Linux so don't have much use for a .bat file. But learned something new.
I think this is the bash script you mentioned? https://www.reddit.com/r/dwarffortress/comments/dw8il/dwarf_fortress_world_generation_script_gnulinux/
Your script might be useful to link to in the future! And thanks for making it!
Do you mind if I upload your script with the bash script above to Github or GitLab so more people will find it in the future?
If you want to upload it there that would be nice too.
(I'll make sure to give you the credit and link back to this thread.)

Not to drag you into the deep end here :P But maybe for the future, you should pick a software License to make sharing end editing it easier.
Something like GPL 3.0 or MIT (both very common licenses) is a good choice (there is also The Unlicense, although not common). Those basically allow people to edit and re-share the code without many restrictions.
Yup, I think that's the one I roughly based this on, there's a couple out there. I was originally going to link one when I mentioned it, but it slipped my mind. heh

And actually, I do have a GitHub account, so I'll see if I can post this there, hopefully the MIT license is good enough, no clue. ;) It'll be my first post there, so forgive me if I did it wrong.
EDIT: Here's a link to that: https://github.com/Nikorasu/DwarfGenManager

Also, I'm currently working on a bugfix update, which will solve a bug with special characters in the folder-name not being loadable by the game. Should have this update ready to post soon, had to add a messy bit of code to remove those characters before writing to the filename. Not that easy to do in Batch. heh

EDIT2: Updated the OP and the GitHub with the v0.2 bugfix ;) Still working on a couple other features for later, like rejecting/deleting worlds that don't meet certain criteria..
« Last Edit: June 21, 2020, 05:10:17 am by NikoKun »
Logged

Ralpha

  • Bay Watcher
    • View Profile

That git repo looks good!
And the MIT License is a very good choose.
It'll be my first post there, so forgive me if I did it wrong.
We are all here to learn ;) And you did it very well!

Already some updates :D

If you want to go to next level 8), maybe add a 'README.md' to the repo with a small description and an example of how you might run the script/example output of the script.
Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

Nice little feature update today!

Now, before it asks the user which generator preset to use, it reads and outputs a list of the ones available in your world_gen file. Makes it easier to remember all those.
It also checks in case you have more than 20 generator presets, and changes the list's display method to fit the screen nicer, if you do. heh
Logged

Ralpha

  • Bay Watcher
    • View Profile

I just found that if you are using dfhack you can also use it to do some extra things.
This way you can create a world, use the exportlegends
to create all the legend files and do even more.
This might be interesting to take a look at.

More info here: https://docs.dfhack.org/en/stable/docs/Core.html
Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

OMG! I totally didn't think that was possible.. hmm, not positive how it works yet, but I'll definitely try to implement this, if I can.
Thanks for alerting me to this possibility! Might have to release it as an alternative version for dfhack, tho I've been using that all along lol.

Seriously, I'm shocked I never noticed the dfhack-run.exe file before! Apparently it lets us run dfhack commands from an external command line, and if I can relaunch the game after it generates a map, open that map for legends, and export it's legends files and organize all those automatically too.. That would really and cut-down on the time I take reviewing the maps it generates.. This would really complete the script in the most useful way possible! ;D And it'll be a fun challenge to figure out how to do. heh
« Last Edit: June 08, 2020, 02:00:19 pm by NikoKun »
Logged

Ralpha

  • Bay Watcher
    • View Profile

I have also been working on modification to the exportlegends script in dfhack.
This modification will export all the files into a folder instead of in the main directory. So when exporting a lot of worlds this will be very welcome.
Hopefully it will be merged soon: https://github.com/DFHack/scripts/pull/152
Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

Unfortunately, I'm not having much luck getting that working.. Launching the game with "Dwarf Fortress.exe" +load-save region0 doesn't work in this instance, because it's a new/fresh save, and that only works for saves with an embark, that appear under "Continue Playing" on the main menu.

The documentation doesn't give much more info, so unless there's hidden commands I'm not sure if it's possible.. and not much comes up when I searched for this stuff, other than a couple other people asking if it's possible.. heh

They'd either have to add a flag to specify which game-mode to load into, or just make the assumption that fresh "Start Playing" worlds loaded through the command-line should just go into legends mode. And if it's got an embark, load that, if it's an adventure save, load that mode. They could just make those assumptions, because it'd be rather rare that someone would want to auto-load to find an embark. lol (Tho obviously some players might make a shortcut to autoload their favorite save, so those other 2 options are valid.)

OR they could add a special command-line option like the +load-save thing, maybe they could make direct and just have it +exportlegends-info region, which would save a little effort.. Or at least enable us to load into legends mode from command-line with something like +load-legends and then I could just use dfhack-run.exe to issue the "exportlegends info" command with my script.
« Last Edit: June 09, 2020, 12:00:06 am by NikoKun »
Logged

Ralpha

  • Bay Watcher
    • View Profile

I had been working on this yesterday. (although for linux, but same rules apply).
I created a new lua script that allows you to load the legend for a particular region and you can use that from the command line like this:
Code: [Select]
  echo "Generating World: $WORLDID"
  ./dfhack "df -gen $WORLDID RANDOM \"POCKET ISLAND\""
  echo "Exporting legends"
  ./dfhack +load-legends-mode region$WORLDID
`load-legends-mode` is the name of the script. (it is not yet in DFHack)
It will use the select menu support from DFHack to move around the interface.
Because of loadscreen timing there was not an easy way to set a trigger for when it is done loading. But figured something out, a bit of a dirty hack but it works :P
So I had to add the
Code: [Select]
dfhack.run_command("exportlegends all") to the file once that is done loading.
I might be able to set this in a second script.

The only problem I found now is that part of the `exportlegends` script uses `script.start(..)` This spawns a new process to export the detailed maps.
And the main application does not know when this is finished. Because I have to kill/exit df after it is done.

I have not found any 'join()' command that lets the main process wait for the script to finish. Maybe someone else knows this?
If I can get the main process to wait this then I got all these steps working.

I'll put the code out there once I got that part working. Then you can use the same lua scripts in the windows commands.
Logged

Ralpha

  • Bay Watcher
    • View Profile

I see there is a fresh release: `Nikorasu released this 10 minutes ago`  8)
(you will probably be typing a message here at the same time :P )

I also just got my script to work. (Will do: generate -> close -> open dfhack -> open-legends -> export legends -> close -> next world)
Although because of some DFHack and other limitations it might not be that easily ported to Windows.
But opening of legends and exporting legends should still work just have to guess a bit more when some parts are done.
So might have to be a bit more liberal with timing.
I'll publish all the code over the coming days. Once scripts are a bit stable and work for both systems I'll make a pull request to add them to dfhack.

Logged

NikoKun

  • Bay Watcher
  • 420
    • View Profile

Wow! That sounds incredible! Can't wait to check it out, it'll be nice to automate the full process. Sorry I didn't respond yesterday, it was a bit of a busy day.

Oh yup, I decided to mark the current version as a stable release. (Tho I did just now have to tweak & re-upload it to fix a typo-related bug I created last night. lol You may need to re-download it now, since what I put up last night probably won't work right. My bad.)
Anyway, figured I'd make a release now because as is, the script is pretty good at the moment. And because the current version works with the base game, without dfhack installed, which some people might need. I think doing it as a release means it'll be easier to find if someone wants this version rather than a newer one that works with dfhack. heh

Once your modifications can either be included into dfhack, or if not maybe just modded in manually, I'll have to make a dfhack-version of this script, using your commands, so it can export and move the legends files to the info folder. That'll be quite nice. Seriously, thanks for your interest, and helping to improve it like this! ;)
(And ya, figuring out when the game is done loading/exporting, so the script can move on, is gonna be a challenge. Might be able to check that certain files exist, but even that doesn't ensure it's done, if the file is still being added to.)

BTW, I'm also currently working on a new feature update for the script, which will have it output a log-file for all the worlds it generated that session, containing basic population lists and info I deem useful, formatted to be easier to read. So even tho it can't output the full legends automatically yet, hopefully this will help users more quickly narrow-down which worlds they want to check manualy, based on the info in the log.

Thanks again!
« Last Edit: June 10, 2020, 10:27:33 pm by NikoKun »
Logged

Ralpha

  • Bay Watcher
    • View Profile

I uploaded my code for generating DF worlds on Linux (and maybe Mac OSX).
I posted it on GitLab as I have some other projects in the works that this can live under.

https://gitlab.com/df_storyteller/df-worldgen

I put a lot of documentation/comments in the code so you should be able to see how it works.
Feel free to ask questions and suggest changes to get it working on your end.
The lua script should work without any problem. (maybe the detecting if it is ready is a bit harder.)

The basics come down to:
Code: [Select]
# Generate world
./dfhack "df -gen $WORLDID RANDOM \"$SIZE $WORLD_TYPE\""

# Open DFHack + load legends
./dfhack +load-legends-mode region$WORLDID

# Export legends
./dfhack-run exportlegends all

# Close DFHack
./dfhack-run die

That'll be quite nice. Seriously, thanks for your interest, and helping to improve it like this! ;)
:) Thanks, and you are welcome! Getting some feedback and interaction when you are creating something is ways nice!
And my other project I'm working on uses the legends to do some nice things ;)
It would be nice to have this script to create some worlds and test my app out on windows.

Keep up the good work! Would like to see how you incorporate the display of world info!
Logged
Pages: [1] 2