Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: Python Script to Check Dwarf Innate Abilities and Suggest Jobs, help plz  (Read 2105 times)

Calcharger

  • Escaped Lunatic
    • View Profile

Hey all. I'm working on a python script that goes through the Steam version of Dwarf Fortress and checks each Dwarf in your fort and looks at their innate abilities and makes suggestions of jobs that they should be doing.

I started out using pytesseract and pyautogui to try to click buttons to cycle through the Dwarves' dossiers and grab screengrabs of their names, their current title, and their innate ability (on the overview page) to mixed results. I usually don't have problems putting their innate abilities into a list but the Dwarf names are sometimes jacked up from special characters.

Someone had made a suggestion to use Cheat Engine to find memory addresses for the Dwarves but when I tried to look up a Dwarf name (with no special characters) it gave me an address that appeared to be dynamically changing or something. It appeared to be the right character length but it didn't really give me a value.


Here is when I am in the Creatures/Citizens panel. I can search for 'Lolor Gusiltherleth', who is my fifth listed dwarf.


I then click on the magnifying glass associated with 'Lolor Gusiltheleth' to pull up her Dossier page (Overview). I then try using Cheat Engine to search for 'lolor gusiltherleth' and I get this result which seems to be scrolling through different details about them.

I'd like to be able to find the memory address that is storing the name of the character when I click through the dossier pages to snatch their innate abilities but i'm open to different suggestions on how to accomplish this.

I've gone though Cheat Engine and it just seems like the memory address for their name keeps changing. This is so frustrating!

Here is an example of how my original pytesseract script was getting results

Code: [Select]
CSRSCREASESSASSSee

['Disdains sacrifice', 'Not wasteful', 'Developed empathy', 'Flights of fancy', 'Not vengeful', 'Cruel']
eSteeoo

['Patient', 'Low willpover', 'Poor memory', 'Bad with words', 'Perfectionist', 'Gracious']
UVutokZonsarekBSSsyoresa


['Values knowledge', 'Disdains self', 'control', 'Good spatial sense', 'Good focus', 'Thrill', 'seeker', 'Overhearing']
MebzuthGemeshast Miner

['Good with language', 'Patient', 'Disdains harmony', 'Poor intuition', 'Low willpover', 'Not lustful']
LolorGusiltherleth
Miner
['Disdains perseverance', 'High kinesthetic sense', 'Poor social avareness', 'Low stamina', 'Recovers slouly', 'Obst inate']
AthelDuthalavuz Miner

['Disdains tranquility', 'Good memory', 'Not creative', 'Low stamina', 'High kinesthetic sense', 'Low willpower']
EdmOttansarvesh Miner

['Values independence', 'Zany', 'High social awareness', 'Emotional obsessive', 'Disdains artwork', 'Overshares']

Hit or miss as you can see.

Anyway, suggestions welcome

Edit: pytesseract code snippet:

Code: [Select]
            pyautogui.mouseDown(citizen_info)
            time.sleep(0.05)
            pyautogui.mouseUp()
            pyautogui.mouseDown(magni_glass_list[dwarf_number])
            time.sleep(0.05)
            ImageGrab.grab(dwarf_name_title).save('dwarf_name_title.png')
            image = Image.open('dwarf_name_title.png')
            dwarf_name_title_string = pytesseract.image_to_string('dwarf_name_title.png',lang='eng')

            #name function. I am converting the string to a list so that I can iterate through the characters and remove bad characters from the pytesseract interpretation which sometimes happens.
            name_spell = []
            job_spell = []
            name_status = False
            for letter in dwarf_name_title_string:
                if letter != "," and name_status == False:
                    name_spell.append(letter)
                elif letter == "," and name_status == False:
                    name_status = True
                elif letter != "," and name_status == True:
                    job_spell.append(letter)
                else:
                    print("There was an Error")
                    print(letter)

            #clean up weird characters

            for letter in name_spell:
                if letter not in alphabet:
                    name_spell.remove(letter)

            for letter in job_spell:
                if letter not in alphabet:
                    job_spell.remove(letter)

            #convert list to string
            name = ''.join(name_spell)
            job = ''.join(job_spell)

            #Grabbing the Dwarve's innate attributes

            time.sleep(0.05)
            ImageGrab.grab(attributes).save('attributes.png')
            image = Image.open('attributes.png')
            attributes_string = pytesseract.image_to_string('attributes.png',lang='eng')
            attributes_list = []
            attributes_spell = []
            for letter in attributes_string:
                if letter in alphabet or letter == " ":
                    attributes_spell.append(letter)
                else:
                    if len(attributes_spell) > 1:
                        attributes_list.append(''.join(attributes_spell))
                        attributes_spell.clear()

Edit: Just ran it and the script as it is right now takes about 24 minutes to run for 149 Dwarves (since it is using Pytesseract image interpretation and pyautogui is clicking through the menus). A lot of the time is lost from scrolling to the next Dwarf so, yeah, if we can figure out a way to get the Dwarves data from memory with consistent results, as well as where their innate abilities are located, that would speed up this process significantly but would probably take significant effort. Another way I think this can be sped up is if you exile the dwarves as you get their information and then quit without saving but then we would need to code in logic about what to do if there are nobels that can't be exiled. But even still, doing it with pytesseract is going to require a lot of different x and y coordinates due to different screen sizes, monitor amounts, etc so being able to access data directly is the best way. I dunno I'm a novice lol.
« Last Edit: December 19, 2022, 12:36:42 pm by Calcharger »
Logged

Clément

  • Bay Watcher
    • View Profile
Re: Python Script to Check Dwarf Innate Abilities and Suggest Jobs, help plz
« Reply #1 on: December 19, 2022, 04:33:57 pm »

You won't find where the names are stored like that because they are not stored as a single string. It is split between first name, nickname and references to words from the dictionary for the surname. It's described there: https://github.com/DFHack/df-structures/blob/master/df.language.xml#L222-L236.
« Last Edit: December 19, 2022, 04:37:16 pm by Clément »
Logged

Calcharger

  • Escaped Lunatic
    • View Profile
Re: Python Script to Check Dwarf Innate Abilities and Suggest Jobs, help plz
« Reply #2 on: December 19, 2022, 05:59:53 pm »

You won't find where the names are stored like that because they are not stored as a single string. It is split between first name, nickname and references to words from the dictionary for the surname. It's described there: https://github.com/DFHack/df-structures/blob/master/df.language.xml#L222-L236.

Thanks, Clement. I'll take a look when I start work on v2.

As for v1, relying mostly on pytesseract, I've gotten it to mostly work with grabbing names and abilities. Git here -

https://github.com/BenjaminHei/DVAB/blob/main/dwarf_attr_checker3.py

Next steps are to put together lists of jobs with desired innates, compare the dwarves innates to those jobs, assign the dwarf a job and spit it onto an excel spreadsheet. There are some issues with some of the names being 'slightly' misspelled and some of the traits coming back funky, so I'm putting together a screening list (bad list on line 26) with a good list (line 27) that I am trying to keep the index numbers the same so that if it finds a wrong name it corrects it.

Can't really do much with the names if they get misspelled. I guess the dwarven admin tech taking down their names has bad handwriting (lol!)
Logged

ab9rf

  • Bay Watcher
    • View Profile
    • ab9rf@github
Re: Python Script to Check Dwarf Innate Abilities and Suggest Jobs, help plz
« Reply #3 on: December 20, 2022, 07:14:21 am »

While this is all very fascinating, you'll probably find it a whole lot easier to just use DFHack's Lua scripting engine once DFHack is ready. The process that your approach takes 28 minutes can be done in a matter of milliseconds and with far less guesswork.
Logged

Calcharger

  • Escaped Lunatic
    • View Profile
Re: Python Script to Check Dwarf Innate Abilities and Suggest Jobs, help plz
« Reply #4 on: December 21, 2022, 08:31:23 pm »

While this is all very fascinating, you'll probably find it a whole lot easier to just use DFHack's Lua scripting engine once DFHack is ready. The process that your approach takes 28 minutes can be done in a matter of milliseconds and with far less guesswork.

Yes sir, I'm sure I'm going to make some big changes once DFhack is ready. I just needed a tool because I couldn't keep playing without being able to navigate my dwarve's innates a little easier.

Github is updated. Program is functional to the point of taking all of the Dwarf's innates and putting them into a spreadsheet.



If you are to use it you're gonna need to update the coordinates on lines 14-20. Probably helps if you are good with Python.

Next steps are to make a gui and make it an executable so you don't have to go through all of the steps of getting python, downloading all the modules through PIP, getting pytesseract and pointing to where it is on line 13, etc...

But it works! Hopefully the smarter fellas at DFhack and Dwarf Therapist finish their project and release it relatively soon so you don't have to mess around with my half-broken Python script! :)

EDIT: There are issues with pytesseract's text recognition misspelling a lot of stuff. It's still not at 100%, so I'm working on adding some logic to change misspelled words into the words that they match the closest. As for Dwarf's names, I can't really fix that with how this is set up. Once we have a better idea of how to navigate either the save file or memory with DFHack it'll likely be much easier. This is really just a quick tool I put together over the course of a few days to help get Dwarve's stats in a better readable format but it has blemishes.
« Last Edit: December 21, 2022, 08:33:34 pm by Calcharger »
Logged