v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

40d:Macro design

From Dwarf Fortress Wiki
Revision as of 05:24, 12 November 2009 by Briess (talk | contribs) (→‎Macro Delay)
Jump to navigation Jump to search

Playing Dwarf Fortress means lots of typing. Macros should likewise mean a lot of planing, hence a design. This page is to go over the design issues when creating a macro and how to avoid problems and slow/massive code. The design of the operation/structure that you creating a macro for this page will not help you. You should be most familiar with it though as you've probably typed it in hundreds of times before bothering to turn it into a macro.


Macro Delay

Its been seen on the forums, and on this wiki where macros take a long time, and in some instances hours to run. The stated solution is to change an 'erroneous' leftover tag MACRO_MS in \data\init\init.txt from 150 to 0. The game as shipped includes no preset macros so the default init is set to a value that the human player can see each move as they debug any mistakes in the first macro they create. Most macros on any system commonly run better with some delay between commands; a MACRO_MS:1 seems to be a good run-time setting.

Planing

Design is all about planning ahead of time to achieve a better result than simply 'winging it'. There are thousands of ways to do the exact same thing, but with a design we would like to pick the best or near-best solution. In the case of DF's macros we want to see our macro run in the fewest steps possible, and getting the most out of each command.

Start by planning out you design as series of steps to create the final result. Then code each step individuality, while taking note of the before/after steps. Like the complete macro, each step has many possible different coding solutions. Try to pick one that is simple (short) but also goes best with the step before it and will end where its advantageous for the next step. Try to keep a step to a single type of operation, this will save the time of continuously switching between modes.

Example: Fields (Boxes\area-selections) have four corners. Your code traverses from one corner to the opposite to define the field, it doesn't matter which corners you start at. So pick a start and stop point of you field that's good for the next or previous (or both) step(s). This saves a lot of time spent walking the cursor around.

Repeats

Command repetition is only a coding short hand. The following scripts are identical in the time it takes them to execute.

[MACRO:CURSOR_UP:20]

[MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1] [MACRO:CURSOR_UP:1]

While it's much easier to read the condensed version, but to DF, its the same in every other way.

Movement

Most of the time a macro takes to run is moving the cursor around. This can be sped up through the use of the "_FAST" versions of the move command and by moving diagonally

Standard Movement

There is a "_FAST" version of every cursor command. "_FAST" movement jumps ten squares in the same time a normal movement takes to travel on square. This the same as holding the shift key the the game UI. The following code,

[MACRO:CURSOR_UP:33]

is the same as 33 moving commands however,

[MACRO:CURSOR_UP_FAST:3]
[MACRO:CURSOR_UP:3]

is the exact same action in only four commands


Back Peddling

When the 1's digit is higher than 5 backing up can achieve faster run times

[MACRO:CURSOR_UP:38]

Goes to

[MACRO:CURSOR_UP_FAST:3]
[MACRO:CURSOR_UP:8]

Brings 38 commands down to just 11, however this can be better optimized by backing up.

[MACRO:CURSOR_UP_FAST:4]
[MACRO:CURSOR_Down:2]

Is the exact cursor change but in only 6 moves (nearly half). This can have side effects however (covered below) and must be done carefully.


Diagonal Movement

Most people using modern computers are spoiled with their Tetris(TM) shaped, arrow key block. The number pad has arrows too, and on a select few keyboards the 1 3 7 9 keys have arrows as well. It doesn't matter if your keyboard has arrows on it or not to DF, you can move with the 1 3 7 9 keys so we can also code diagonal movement in our macros too.

[MACRO:CURSOR_UP:8]
[MACRO:CURSOR_RIGHT:8]

can be replaced by

[MACRO:CURSOR_UPRIGHT:8]

This Dropped the command count from 16 to 8. It also works for non-squares too

[MACRO:CURSOR_UP:12]
[MACRO:CURSOR_RIGHT:16]

can be replaced by

[MACRO:CURSOR_UPRIGHT:12]
[MACRO:CURSOR_RIGHT:4]

This saved us 12 unneeded commands.

The "_FAST" works in the game and in your scripts on diagonals too. Further bringing down the the previous example command count to just,

[MACRO:CURSOR_UPRIGHT_FAST:1]
[MACRO:CURSOR_UPRIGHT:2]
[MACRO:CURSOR_RIGHT:4]

So From 38 command to just 7, with a little planning.

Diagonal movement commands are written as Vertical then Horizontal with no separating "_"

Cursor Return

When designing your script its a idea to plan where to leave off. The two common ideas ones,

  • Back where the script started, which is less disorienting to the user.
  • In position for an instant repeat of the macro, if this makes sense.
If you macro makes a 3 square wide section of hallway to the left, then put the end the cursor in position to allow for a repeat call of the macro to add a second section.

Both are better than just stopping the cursor wherever it happens to be.



Problems to avoid

A step to far

When your script doesn't run as planned, check if you went to far on a movement action. To make a 5x5 box the code is

//written for ease of reading
[MACRO:SELECT:1]
[MACRO:CURSOR_LEFT:4]   
[MACRO:CURSOR_UP:4]
[MACRO:SELECT:1]

Those are 4's not 5's. The cursors current position it always 1, since it's movement your defining not size. Its and easy concept but also easy to forget/mess up.

Edge Collision

Edge Collision can be helpful in several situations. When you hit a edge you stop there, no matter how far your script wants to travel that way. This is per command and if your not expecting to hit a wall, it will corrupt that execution of the script. There is no way to avoid this. And in most cases its the script-runner's fault not the script designer. However as a designer you can run into trouble with the Back Peddling optimization mentioned above. Back Peddling can jump you out of the working area of your script and then return, as long as there is enough extra space around your script area to do so. If an edge happens to be there, instant problem.

If your scrip is made inside a 17x4 rectangle, getting across it with the following,
[MACRO:CURSOR_RIGHT_FAST:2]
[MACRO:CURSOR_LEFT:3]

Is fine as long and edge wasn't 19 squares away.

I would recommend to only use back peddling that does not go outside of your script's work area.

With Back Peddling movements that are larger than 10 spaces a simple change of order of operations can fix the issue. Here is the above example, with an order changes

[MACRO:CURSOR_RIGHT_FAST:2]
[MACRO:CURSOR_LEFT:3]

is really just

[MACRO:CURSOR_RIGHT_FAST:1]
[MACRO:CURSOR_RIGHT_FAST:1]
[MACRO:CURSOR_LEFT:3]

so we can reorder it to

[MACRO:CURSOR_RIGHT_FAST:1]
[MACRO:CURSOR_LEFT:3]
[MACRO:CURSOR_RIGHT_FAST:1]

This is the same run time/#commands, yet the cursor says inside the script area.

For script areas of 6, 7, 8, or 9 your just out of luck and I would suggest not Back Peddling, but which a script area so small speed isn't that much of an issue.
The Back Peddling "LEFT:3" command travels over the same area the previous "_FAST" command covered. If it had been placed first you would have Back Peddled out the other side.