Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - Nagidal

Pages: 1 2 [3] 4 5 ... 29
31
@Warmist: When I was searching the lua scripts in DF and DFHack directory, I found that utils.lua has a binary search function generalised to work with any list.

@Patrick:
I was puzzled why I can't find this script in DFHack or in the game already and I did not figure out how and when to run it. Thanks for telling me. It's only now that I realise that you did all this work for your private needs of getting an almost perfect duplicate of the text displayed under Thoughts and preferences. Hats off. You practically did 90% of the work I was afraid I'd had to do myself before I wrap it up with a routine gathering this periodically.

I am sorry if my snarky remark about the function naming. I thought I am looking at a publicly available DFHack script which is supposed to be properly commented and clear for all the DFHack devs. I didn't know that you did all this just to do your own DFHacking stuff and graciously share this.

Yet I have to say I truly struggled to understand all that code. A couple of comments of what this all or each of its functions are supposed to do would have saved me several hours of clueless staring at it and sort of reverse engineering it. I only learn programming as a pastime hobby and I never knew such algorithm as binary search. And I didn't know how hard—if not impossible—it is to implement it 100% bug-free.

Anyway, a thousand thanks for this script, it already gathers most of the information I am looking for. Back to the topic now: I have tried it on some of my dwarfves. Besides some of the information from thoughts missing (which I'm going to try to add to the script) I found that for some dwarves it writes the wrong feeling for inebriation. DF says "she didn't feel anything due to inebriation" thoughts.lua says "... felt euphoric due to inebriation" Although I'm currently not interested in keeping track of my dwarfs recent experiences, I wonder what can be causing this difference.

32
I am now more acquainted with Lua syntax. I looked at the the Locator in broader context

there is a global table of historical figures which is indexed and assumed to be ordered by unitID in ascending order, e.g.:

Code: [Select]
index  | Historical Figure (as object)
0      | unit42
1      | unit56
2      | unit215
3      | unit418
...    | ...
354624 | unit678954

Each unit object in this table has an attribute .id, that's one of the game-internal attributes of the unit class, I presume.

Locator is a class (or table, in Lua terms) which serves two purposes:

1. Given an index in the table of historical figures, it can find access the corresponding historical figure and lookup its id. The method (or function) doing this is called id_of. This method is undefined by default, so every time the Locator class is instantiated, this method has to be defined like this:
Spoiler: id_of() (click to show/hide)

2. Given a game-internal unit id, it can find the unit with this unit id in the table of historical figures and return the index under which it can be found there. The method doing this is called index_of. Nah, that would make the code more readable, let's call it locate instead.

The locate method uses some sort of searching algorithm which I assume is pretty quick in searching in a list where the the historical figures are sorted by their ids in an ascending order. When I was trying to perform this method myself to see how its searching algorithm actually works, I think I have found a bug. It seems that it cannot handle the case where it is supposed to find and return the index of the very first historical figure in the list (index 0).

Let's shorten our list of global historical figures to only two members (count = 2):
Code: [Select]
index  | Historical Figure (as object)
0      | unit205
1      | unit408

I'll now run through the locate function:

Spoiler: locate(id, count) (click to show/hide)

When we call hf_locator:locate(205, 2), the initial values are:
id = 205
count = 2
bottom = 0
top = 1

the the loop starts:
step = 1 // (top - bottom)
step % 2 == 1 is true, that's why:
step = 2 (step + 1)
step = 1 (math.floor (step / 2))
index = 1 (bottom + step)
self.id_of(index) < id is false, skipping this if, executing elseif
elseif self.id_of(index) > id is true, executing the if body:
    if top == index is true (1 == 1), executing the if body:
        return -1, returning -1 index not found. (which is wrong, it should have found it and return 0)


Once the loop progresses to the point where top search limit is reduced to 1, the function only checks the unitID, and if it is not the unit we are looking for, it returns -1 forgetting to look at the one last remaining place in the list, index 0.


33
Your preferences spreadsheet is bound to be rather long, given that dorfs can have preferences for most everything in the game.

I know, that's why I would only list those preferred things which matter to the fortress members. If there is nobody in the fortress who likes donkeys, and the script only accumulates favourites of fortress members, donkeys would not appear in the list at all.

Now, reading the Lua script you linked: It looks like it is a script which would print out the things I see when I read the thoughts of a dwarf, right? I see how the values are found and the whole thoughts text is composed. At least the parts we know how they map to the values.

Could you explain the Locator function in more detail? I don't understand how does it limit its scope only to fortress members. How does it ignore invaders, visitors, merchants, animals, and all other historical figures of the world which are not even physically present on the embark map.

This is how I read it (I'm not at all familiar with Lua syntax):

Code: [Select]
function Locator:locate (id, count) // is "Locator" a class and "locate" a method of it?
// I figure its used like this:
// locatorInstance = Locator()
// locatorInstance.locate(23547, 13) would access unitID 23547 and ... what is count used for?
  // declaration of local variables:
  local bottom = 0
  local top = count - 1 // what is the value of count?, whatever, we'd start with 12 (top = 13 - 1)
  local step
  local index // this is the thing the locate() method returns after some math. But what does index represent?
 
  while true do
    step = top - bottom  // step = 12 - 0
    if step == 0 then
      return -1  --  Not found // allright if I call .locate(someID, 0) I'll get -1
    end

    // This rest of all this code is only executed if .locate() was called with a nonzero count, e.g. locatorInstance.locate(23547, 1)
    if step % 2 == 1 then  // this makes step even. Our step of 12 would be unaffected
      step = step + 1
    end
 
    step = math.floor (step / 2)   // ok, since step is always even, we can divide it by 2 (but WHY?), step is now 6
 
    index = bottom + step    // ok, 6. index = 0 + 6, whatever

    if self.id_of (index) < id then  // what does the method id_of do?
      // how can we call locatorInstance.id_of() is this method wasn't declared in Locator class?
      // whatever, self.id_of(6) < 23547 is either true or false
      bottom = index  // either bottom = 6 ...
     
    elseif self.id_of (index) > id then  // ... or  self.id_of(6) > 23547 , BUT ...
      if top == index then  // ... if top = 6, in this special case return -1 again
        return -1  --  Not found
      end
      // but actually
      top = index   // top = 6 , wait, wasn't this the case which is supposed to return -1?
    else
      // if neither (self.id_of (index) < id) nor (self.id_of (index) > id)
      // so actually only if self.id_of (index) == id ...
      return index    // ... return the index of the unitID in locatorInstance.locate(unitID, count) what the hell is count?
    end 
  end
end

// so, this method .locate() returns an index. Is this index the position of a particular unitID in a list of units?
e.g. listOfUnits = [unit35723, unit95473, unit23547, ...]
instanceOfLocator.locate(23547, someCountOfSomething) would return 1 because listOfUnits[2] = unit23547

Seriously, a handful of little comment lines of what is what in this function would save me hours of lifetime.  :'( :'(

Edit:
I now read the top lines which seem to be comments. They revealed to me that I only had a look at a part of the Locator class definition. Also, count is the number of units in a sorted (and indexed) list of units. Seriously? Why do we even use count? Why don't we use len(listOfUnits)? Instead we allow the programmer to call .locate with an invalid count argument and must handle errors resulting from it? Anyway... could count be the number of unit one sees in the Fortress (u)nits list?

34
Utilities and 3rd Party Applications / What do I need to write this script:
« on: November 04, 2017, 09:52:11 am »
I am really interested in keeping detailed track of each dwarves' thoughts and preferences. (skills, body and soul attributespersonality traits, ...) I'd like to see which of them change over time, how often they change etc.

There is two things I want to achieve with this:

Goal 1. Sort of a personal file of each dwarf which would track the dwarves' development (take a daily record of his skills, attributes, traits). This would allow me to see how quickly do miners or pump operators grow muscles, how quickly each dwarf gains military skills, etc., use this data for a diachronic analysis. See how often things like worshipped deities, hair color, life ideals and behaviour, change - or if they are constant forever.

Goal 2. A Dwarf-Therapist-like spreadsheet overview of what kinds of pets, items, materials, food, drinks my dwarves like.
Example for pet preference: Obok and Tulon like dogs, Kib likes cows, Udib likes rabbits. Script would output a table like this:
Code: [Select]
PET NAME | NAME1           | NAME2            | NAME3
dog      | Obok Kűbukmamot | Tulon Orrunlikot |
cow      | Kib Tathtatled  |                  |
rabbit   | Udib Gutirurist |                  |


Here is a sketch of what I have in mind in a Python-like pseudo code (Python is currently the only language I can code in). Such script would probably have to run in the background parallel to the game:


Goal 1: A Dwarf's personal file recording his skill values
Code: [Select]
if calendarDateChanged():
    // dump the data about each dwarf into a file named like the dwarf
    for dwarf in fortressMembers:
        file = openFileInAppendMode(dwarf.name+".txt")
        print("Date:", calendar.currentDate(), file)
        print("Strength:", dwarf.strength.getValue(), file)
        print("Willpower:", dwarf.willpower.getValue(), file)
        // ... etc., dump all the info I want to keep track of piece by piece
        // it should probably be an XML dump instead, as I believe there are XML parsers ready to use.
        file.flush() // makes sure the print buffer is actually written into the file on the harddrive


And here is the sketch of the part which would serve to make a spreadsheet of the dwarves favourite pets, items, materials, food, etc.:

Goal 2: Spreadsheet of pets, items, materials, food, drinks which would list the names of dwarves who prefer it
Code: [Select]
// create a dictionary of pets, items, materials. In python a dictionary is an unordered list of (key,value) pairs, which can be evaluated: value = dictionary[key].
// example: pets[dog] would return a set of names of dwarves who like dogs
// initialize the dictionaries
pets = dict()
items = dict()
materials = dict()
for dwarf in fortressMembers:
    // if this dwarf's pet is already listed in the pet dictionary, just add the dwarf to the set of dwarves who like this pet
    try:
        pets[dwarf.favouritePet()].add(dwarf.name)
    // if this dwarf's pet is not yet in the pet dictionary, ...
    except keyError:
    // ... create a new dictionary entry for this pet
        // and assign this dictionary entry a set containing this dwarfs name (later more likeminded dwarves can be added to this set)
        pets[dwarf.favouritePet()] = set([dwarf.name])
    // same procedure for items
    try:
        items[dwarf.favouriteItem()].add(dwarf.name)
    except keyError:
        items[dwarf.favouriteItem()] = set([dwarf.name])
    // ... and materials
    try:
        materials[dwarf.favouriteMaterial()].add(dwarf.name)
    except keyError:
        materials[dwarf.favouriteMaterial()] = set([dwarf.name])

    // =====
    // obviously I would avoid copy-pasting the same procedure for pets, items and materials. I just wrote it down for easier readability.
    // I'd actually use something like this:
    // make tuples of dictionaries and dwarfs interests
    // dictionariesAndInterests = zip(list(pets, items, materials), list(dwarf.favouritePet(), dwarf.favouriteItem(), dwarf.favouriteMaterial())
    // for dictionayOfInterest, dwarfsFavourite in dictionariesAndInterests:
    //     try:
    //         dictionayOfInterest[dwarfsFavourite].add(dwarf.name)
    //     except keyError:
    //         dictionayOfInterest[dwarfsFavourite] = set([dwarf.name])
    // =====
// now dump the whole overview:
// heavily simplified
for dictionary in list(pets, items, materials):
    for key, value in dictionary:
        print(key, value)

If this can be done, can you introduce me to the structure of DF memory and the names of variables which bear the information I'd like to gather?

35
DF General Discussion / Re: Speedrunning Dwarf Fortress – Ideas
« on: November 02, 2017, 05:39:40 pm »
I just want to add that RNG does not necessarily break the game for speedrunning. Many randomised games are speedrun. Just search youtube for speedruns of Spelunky, Crypt of the Necrodancer, Tetris or Minecraft. Sure, Randomness plays a role, yet it does not prevent people from having fun speedrunning such games. In Angband speedruns, which are turn-based and RNG rules everything, players compete to kill Morgoth in the least amount of turns.

36
DF Gameplay Questions / Re: Will smoothed Damp Ice Wall melt?
« on: November 02, 2017, 05:32:22 pm »
Thanks, my dwarves managed to smooth the aquifer bearing sandstone tile by tile, while chipping away at the frozen aquifer water. Aquifers don't leak diagonally. Moat stays dry.

37
DF Gameplay Questions / Will smoothed Damp Ice Wall melt?
« on: October 31, 2017, 04:17:15 am »
I have accidentally dug into an aquifer during a cold or freezing season. The water running out of instantly froze and created a Damp Ice Wall. I know that If I smooth the aquifer walls, it will no longer leak, but sadly I can't reach the aquifer walls now since they are surrounded by Ice Walls. Will smoothing the Ice Walls prevent them from thawing?

Spoiler: image (click to show/hide)

38
DF Gameplay Questions / Re: Can't make quicklime
« on: October 29, 2017, 03:59:03 am »
Do you think you could adapt this script for displaying also all the mechanisms linked to a construction? Then you could highlight one of the linked mechanisms and by pressing a key zoom the view on the linked trigger.

No longer would it be necessary to turn on the (N)otes, see what have you called this bridge, go to your levers, turn on Notes again, see what lever corresponds to it, and then move the cursor over it to operate the the lever.

You could goto the bridge you want to operate, see list of linked triggers and jump to the highlighted trigger, then pull it right away.

39
DF Gameplay Questions / Re: Overwhelmed, how do you guys do it?
« on: October 29, 2017, 03:04:17 am »
As feelotraveller said, go slower is the key.

Dwarf Fortress is probably the only game in the world which you play PAUSED. For the most part, at least, until the framerate drops low enough that you can let it run while you're thinking what to do next.

My playstyle may be somewhat exceptional because I really care a lot to achieve 0 idlers, pretty much at all times. When I started learning DF back in the days of 0.31.x, the sieges always terribly crushed me. My fortresses didn't have this or that, were utterly unprepared for attacks, the only thing they had was a lot of idlers. This way I realised that there is actually a big time pressure to get your fort ready in the relatively short couple of years you generally have until you get sieged. (Back then I did not know that sieges only start once you reach 80 dwarves, I thought they are time-triggered. Anyway, you can't send away migrants, so reaching 80 is still a sort of a ticking time bomb for me.)

Basically anytime I see some idlers which don't instantly pick something to do I pause and think what else could I let them do. I don't mind enabling and disabling jobs for them, be it just for a short time. If I see that they could smooth this hall, I make them stone detailers and let them do it. Or I make them haul things which sooner or later will needed to be hauled, dumped, or I let them build some defense walls, make rock blocks for future constructions, carve rooms, make furniture for future migrants, or process plants or anything. I later learned that once you realise you need more military, it is usually too late. Training your military to an acceptable level takes almost a decade. You can never start too early.

If you see idlers and you really have no idea what to do with them, make a squad and let them train. No weapons and armor yet? See, that's what you could let dwarves make as well. No hospital, no well, no soap yet? Dude, you must have been really sleeping all the time.  ;)

40
I had once a caravan which got spooked by a pile of dead enemy corpses after a siege. There may have been the one or another megabeast as well among them.

41
DF General Discussion / Re: Speedrunning Dwarf Fortress – Ideas
« on: October 29, 2017, 02:45:08 am »
Wow, I didn't think that it could happen so quickly. Tantrum is so very random then, not good for speedrunning.

42
DF General Discussion / Re: Speedrunning Dwarf Fortress – Ideas
« on: October 28, 2017, 03:44:16 pm »
A tantrum spiral run would last forever given tantrum spirals haven't been a thing for years now.

I haven't got a tantrum spiral yet, but I have had a dwarf throwing a tantrum. If I had the task to make any dwarf throw a tantrum asap, I don't really know how I would achieve it. I am correcting the line to just "First Tantrum".

43
DF General Discussion / Speedrunning Dwarf Fortress – Ideas
« on: October 28, 2017, 01:32:25 pm »
I'd like to hear your ideas about speedrunning Dwarf Fortress. We have a big player community, but I haven't seen any DF speedruns yet. I'm not a speedrunner myself, but I like to watch speedruns. Maybe there are more of you who would gravitate towards the speedrunning kind of playing DF. My idea of speedrunning DF would be speedrunning it measured by the in-game time, since it is fundementally a turn-based game.

Here is some speedrun goals I could come up with for the start, I'm sure you'll have some more to add.

Spoiler: Fortress Mode runs (click to show/hide)

Spoiler: Adventure Mode runs (click to show/hide)

Any other ideas? How would we best standardize measuring the run time? Is it possible to count the number of ticks (or steps) in a DFHack spript or somthing like that?

44
DF Gameplay Questions / Re: splints or casts?
« on: October 25, 2017, 06:33:56 am »
I don't know the answer, but the wiki article says there is a slight difference:

Casts are [...] used to keep broken bones in their proper place until healed.

Splints immobilize limbs that have sustained bone fractures. They allow the broken limb to be utilized until it is fully healed. Dwarves will be able to leave the hospital and resume their normal duties once securely splinted up since by this stage their wounds have already been cleaned, sutured and dressed.

45
DF Gameplay Questions / Re: Can't make quicklime
« on: October 25, 2017, 05:26:19 am »
That seems very useful, thanks. How do you run such a script?

Should I save it in \Dwarf Fortress 0.43.05\hack\scripts as, say, links.lua and then what? Type links in the DFHack command line?

Pages: 1 2 [3] 4 5 ... 29