Staredit Network > Forums > SC1 UMS Theory and Ideas > Topic: StarCraft Mathematics
StarCraft Mathematics
Aug 18 2019, 1:15 am
By: jjf28  

Aug 18 2019, 1:15 am jjf28 Post #1

Cartography Artisan

Jump to Guide Start

Quick Reference Sheet

Any uppercase letter e.g. "A", "B", "C": A variable or counter, something that has an unknown value in game.
Any lowercase letter e.g. "a", "b", "c": A constant, a value hard-coded in the map.

All division (e.g. "A/B") in this guide and generally in the context of StarCraft should be assumed to be integer division (that is, it by default implies "FLOOR(A/B)") unless otherwise specified or unless wrapped explicitly e.g. "ROUND(A/B)" or "CEILING(A/B)".

Operation Shorthand: Defining what you are doing. You're writing down what you're trying to achieve and maybe using math to simplify or get it closer to something you can do in countoff shorthand.
Countoff Shorthand: Defining how you are doing it. You're writing down something that represents and can be directly converted into triggers.

Note that the same symbol may mean something different in operation shorthand than it does in countoff shorthand, and that neither may perfectly align with what you'd expect from regular mathematics or in programming.


Operation Shorthand

% ___________________ "Modulus" finds the remainder from division of the left hand side by the right hand side

= ___________________ The assignment operator (the variable on the left hand side is assigned the value the expression on the right hand side evaluates to)
+= __________________ Adds the value on the right hand side to the value on the left hand side
-= __________________ Subtracts the value on the right hand side from the value on the left hand side
*= __________________ Multiplies the value on the left hand side by the value on the right hand side
/= __________________ Divides the value on the left hand side by the value on the right hand side
%= __________________ Sets the value on the left hand side to the remainder from division of the left hand side by the right hand side

? _______________________________ If the expression on the left evaluates to true, then do the operation on the right
(expression) ? (op1) : (op2) ____ If (expression) evaluates to true, then do (op1), if it evaluates to false, then do (op2)

== __________________ Comparing whether the expression on the left hand side equals the expression on the right hand side
!= __________________ Checks whether the expression on the left hand side is not equal to the expression on the right hand side
>= __________________ Checking whether the expression on the left hand side evaluates to a value greater than or equal to the value the expression on the right hand side evaluates to
<= __________________ Checking whether the expression on the left hand side evaluates to a value less than or equal to the value of expression on the right hand side evaluates to


... = ... A@ ... ____ When the operation is complete A must have the value it had before the operation started
A = B _______________ The value of B gets stored in A, when the operation is complete the value of B is undefined
A = B@ ______________ The value of B gets stored in A, when the operation is complete the value of B is unchanged
A = b*B/C ___________ The value that "b*B/C" evaluates to gets stored in A, when the operation is complete the values of B and C are undefined

A = A31 + A30 + ... + A1 + A0 ___ Expansion into conditional bits
A31 is 231 if A >= 231, else A31 is 0
A30 is 230 if A-A31 >= 230, else A30 is 0
A29 is 229 if A-A31-A30 >= 229, else A29 is 0
... and so on till A0


Countoff Shorthand

Shorthand ____ Net Effect ________ Wording

A # B ________ B += A, A = 0 _____ "Countoff A into B" (using an unspecified base, usually binary)
A #2 B _______ B += A, A = 0 _____ "Countoff A into B using binary countoffs"
A #3 B _______ B += A, A = 0 _____ "Countoff A into B using ternary countoffs"
A #4 B _______ B += A, A = 0 _____ "Countoff A into B using quaternary countoffs"

A # B,C ______ B += A, C += A, A = 0 _____________________________ "Countoff A into B and C"
A,B # 0 ______ A -= MIN(A, B), B -= MIN(A, B) ____________________ "Countoff A and B to zero"
A,B # C ______ C += MIN(A, B), A -= MIN(A, B), B -= MIN(A, B) ____ "Countoff A and B into C"

A # bB __________ B += b*A, A = 0 _____ "Countoff b times A into B"
aA # B __________ B += A/a, A = 0 _____ "Countoff A over a into B"
aA # bB _________ B += bA/a, A = 0 ____ "Countoff A into B scaled by a and b"

aA # bB,cC ______ B += bA/a, C += cA/a, A = A%a ________________________________________________________ "Countoff A into B and C scaled by a, b, and c"
aA,bB # cC ______ C += c*MIN(A/a, B/b), A -= MIN(A/a, B/b), B -= MIN(A/a, B/b) _________________________ "Countoff A and B into C scaled by a, b, and c"
aA,bB # cC,dD ___ C += c*MIN(A/a, B/b), D += d*MIN(A/a, B/b), A -= MIN(A/a, B/b), B -= MIN(A/a, B/b) ___ "Countoff A and B into C and D scaled by a, b, c, and d"

A # B,C # A ____ B += A ____ "Countoff A into B and C then countoff C into A"


- If you didn't understand a word on this post, good, that's why I wrote the next 10 posts


Post has been edited 5 time(s), last time on Aug 23 2019, 3:07 am by jjf28.



TheNitesWhoSay - Clan Aura - github

Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

Aug 18 2019, 1:15 am jjf28 Post #2

Cartography Artisan

Table of Contents


1. Introduction
2. Counters and Countoffs
3. Symbols and Trigger Equivalents
4. Comparisons
5. Addition and Subtraction
6. Multiplication
7. Introduction to Division
8. Integer Division
9. Rounded Division
10. Fractions


Post has been edited 8 time(s), last time on Aug 23 2019, 3:05 am by jjf28.



TheNitesWhoSay - Clan Aura - github

Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

Aug 18 2019, 1:15 am jjf28 Post #3

Cartography Artisan

1. Introduction


Welcome to the world of StarCraft math! If you've ever tried to do math in StarCraft you've probably noticed that blizzard did not give us many tools. In fact... I can quickly list just about everything useful that they did give us...

A >= c ____ "A is at least c" _______ Current Player has suffered at least 2 deaths of Cantina.
A <= c ____ "A is at most c" _______ Current Player has suffered at most 1 deaths of Cantina.
A == c ____ "A equals c" __________ Current Player has suffered exactly 0 deaths of Cantina.
A += c ____ "Add c to A" __________ Modify death counts for Current Player: Add 1 for Cantina.
A -= c _____ "Subtract c from A" ____ Modify death counts for Current Player: Subtract 1 for Cantina.
A = c ______ "Set A to c" __________ Modify death counts for Current Player: Set to 1 for Cantina.

You may have noted something major that is absent here (besides the obvious lack of multiplication and division)... there's no direct way to compare two different variables! Moreover while we may have the ability to add or subtract constants from variables and set a variable to a constant, there's no direct way add or subtract one variable from another, nor set one variable equal to another.

But they did give us these variables (or Death Counters if you prefer, denoted in this guide by capital letters), and they did give us the ability to compare them against constants, and manipulate them using constants - and a long time ago clever mappers found out this was enough to copy values from one place to another by using Binary Countoffs, and even more clever mappers discovered that all mathematical operations were possible, with enough effort.

In this guide I'll be walking you through performing variable to variable comparisons, performing addition, subtraction, multiplication, and division, as well as giving a less formal introduction to fractions and decimal values.

Post has been edited 7 time(s), last time on Aug 22 2019, 2:41 pm by jjf28.




Aug 18 2019, 1:15 am jjf28 Post #4

Cartography Artisan

2. Counters and Countoffs


A counter is anything in a StarCraft map that can be easily used to hold and manipulate a value, the most prolific, general use counter is the death count for units that are unable to die in game (or that do not add to their counter when they die in game), such as the death count for Cantina. There are a handful of other values you might call counters, such as the scores (especially "custom" score, though it's recommended that you reserve that for values you need to display in the leaderboard), player minerals and gas, and others.

There are three things you need to know about death counters...

1.) A death counter is nothing more than a positive integer*, you can set it to any value between 0 and 4294967295.
2.) Every unit has eight death counters**: deaths for Player 1, deaths for Player 2, ... , and deaths for Player 8.
3.) A death counter is unaffected by whether the player in question is in the game, and is also unaffected when that player leaves the game. The only complication you need to worry about is whether the unit can actually die in game, in which case your counter may be messed up.

*But my tool shows me a negative value!
**What about Player 9-12, or 13+?



Countoffs, in their simplest form, are a way of moving a value from one counter to another.

Understanding Countoffs and Binary Countoffs


But you can use a countoff for so much more than just copying a value, you can easily multiply by a constant by adding a multiple of the value you subtracted, or easily find the greater of two values by subtracting from both until one of them reaches zero, but we'll get into that fun later. First we need to understand how StarCraft's triggers run, and understand a quirk that unlocks two more kinds of countoffs.

Ternary and Quartic Countoffs


Post has been edited 23 time(s), last time on Aug 22 2019, 2:42 pm by jjf28.



TheNitesWhoSay - Clan Aura - github

Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

Aug 18 2019, 1:15 am jjf28 Post #5

Cartography Artisan

3. Symbols and Trigger Equivalents


Before I start actually showing you how to do anything mildly complex we desperately need to address something: countoffs are too long to keep writing out, when I wrote out binary countoffs for P1-Cantina to P1-Cave earlier, that was 415 lines of display space, and even writing P1-Cantina and P1-Cave is a little excessive. If we do a few of those countoffs in an operation we'll quickly run out of forum space, and brain space cause we're just seeing the same pattern over and over, not seeing the jist of the logic in a compact space - we need improved notation. I'm definitely up for debate on much of this if you feel there's improvements to be had, but for numerous reasons I've settled on much of the following...

First the basic trigger operations I listed earlier...
A >= c ____ "A is at least c" _______ Current Player has suffered at least 2 deaths of Cantina.
A <= c ____ "A is at most c" _______ Current Player has suffered at most 1 deaths of Cantina.
A == c ____ "A equals c" __________ Current Player has suffered exactly 0 deaths of Cantina.
A += c ____ "Add c to A" __________ Modify death counts for Current Player: Add 1 for Cantina.
A -= c _____ "Subtract c from A" ____ Modify death counts for Current Player: Subtract 1 for Cantina.
A = c ______ "Set A to c" __________ Modify death counts for Current Player: Set to 1 for Cantina.

For P1-Cantina we can substitute A, for the at least condition we can write A >= c, at most: A <= c, exactly: A == c, for the addition action: A += c, subtraction: A -= c, and set to: A = c.

Also for representing actions that follow if certain conditions pass we can paste a question mark between, e.g.

A >= 4 && B >= 4 ? A -= 4, B -= 4

A >= 4 B >= 4 ? A -= 4, B += 4
Players

  • (some players)
  • Conditions

  • A >= 4
  • B >= 4
  • Actions

  • A -= 4
  • B -= 4


  • Which in turn, if A is P1-Cantina and B is P1-Cave, and we want this to run under player 1, would represent...


    A >= 4 B >= 4 ? A -= 4, B += 4
    Players

  • Player 1
  • Conditions

  • Player 1 has suffered at least 4 deaths of Cantina.
  • Player 1 has suffered at least 4 deaths of Cave.
  • Actions

  • Modify death counts for Player 1: Subtract 4 for Cantina.
  • Modify death counts for Player 1: Subtract 4 for Cave.


  • At this point, we could write out an entire countoff from P1-Cantina ("A") into P1-Cave ("B") like so...

    A >= 2147483648 ? A -= 2147483648, B += 2147483648
    A >= 1073741824 ? A -= 1073741824, B += 1073741824
    A >= 536870912 ? A -= 536870912, B += 536870912
    A >= 268435456 ? A -= 268435456, B += 268435456
    A >= 134217728 ? A -= 134217728, B += 134217728
    A >= 67108864 ? A -= 67108864, B += 67108864
    A >= 33554432 ? A -= 33554432, B += 33554432
    A >= 16777216 ? A -= 16777216, B += 16777216
    A >= 8388608 ? A -= 8388608, B += 8388608
    A >= 4194304 ? A -= 4194304, B += 4194304
    A >= 2097152 ? A -= 2097152, B += 2097152
    A >= 1048576 ? A -= 1048576, B += 1048576
    A >= 524288 ? A -= 524288, B += 524288
    A >= 262144 ? A -= 262144, B += 262144
    A >= 131072 ? A -= 131072, B += 131072
    A >= 65536 ? A -= 65536, B += 65536
    A >= 32768 ? A -= 32768, B += 32768
    A >= 16384 ? A -= 16384, B += 16384
    A >= 8192 ? A -= 8192, B += 8192
    A >= 4096 ? A -= 4096, B += 4096
    A >= 2048 ? A -= 2048, B += 2048
    A >= 1024 ? A -= 1024, B += 1024
    A >= 512 ? A -= 512, B += 512
    A >= 256 ? A -= 256, B += 256
    A >= 128 ? A -= 128, B += 128
    A >= 64 ? A -= 64, B += 64
    A >= 32 ? A -= 32, B += 32
    A >= 16 ? A -= 16, B += 16
    A >= 8 ? A -= 8, B += 8
    A >= 4 ? A -= 4, B += 4
    A >= 2 ? A -= 2, B += 2
    A >= 1 ? A -= 1, B += 1

    And we should know exactly how to write this in triggers. But even this is very long and repetitive, and since it's a very common pattern we should condense it even further and say that we can write the entire operation above as

    A #2 B ____ "Countoff A into B using binary countoffs"

    As we get further down the rabbit hole it's not even useful to know what kind of countoffs we're using, in fact, that just adds confusion when we know we're moving one value into another, so it will be very helpful to say it just as

    A # B ____ "Countoff A into B", and leave the specific choice of binary, ternary, or quartic countoffs till the absolute last step.

    When we're writing out countoffs or even countoff shorthand with #, we're not really describing what we're trying to accomplish, we're describing the how of something, what we're trying to accomplish with A # B, is assigning B the value of A, which we write as...
    B = A

    B = A is the operation we're trying to perform, so we call this "operation shorthand", while A # B is how we do it, and in StarCraft 99% of the time that involves countoffs, so we call this "countoff shorthand"

    Operation Shorthand: Defining what you are doing. You're writing down what you're trying to achieve and maybe using math to simplify or get it closer to something you can do in countoff shorthand.
    Countoff Shorthand: Defining how you are doing it. You're writing down something that represents and can be directly converted into triggers.

    Commonly we want multiple copies of a variable, not just one copy, we do this with countoffs that look like...


    A >= 2147483648 ? A -= 2147483648, B += 2147483648, C += 2147483648
    A >= 1073741824 ? A -= 1073741824, B += 1073741824, C += 1073741824
    ...
    A >= 8 ? A -= 8, B += 8, C += 8
    A >= 4 ? A -= 4, B += 4, C += 4
    A >= 2 ? A -= 2, B += 2, C += 2
    A >= 1 ? A -= 1, B += 1, C += 1

    Which we can shorten as...
    A # B,C

    Note that when we do something like A # B, we're destroying the value of A and moving it to B, often times in an operation we don't want to destroy our input values, if the goal is to preserve the input values we need to indicate that... I've decided to do that with the @ symbol.
    B = A@

    Placing A into B without destroying A isn't actually too difficult, we can just do something like this
    A # B,C
    C # A

    We countoff A into B and C, then C back into A. Because this is very common I've added an additional shorthand for this...

    A # B,C # A

    This sort of countoff chaining would become very ambiguous as to what is getting counted off into the second instance of A, therefore a secondary # symbol only uses the last variable from the previous countoff as the input

    so A # B,C # A can only mean...
    A # B,C
    C # A

    It does not mean
    A # B,C
    B,C # A
    or anything else.

    Post has been edited 9 time(s), last time on Aug 22 2019, 2:42 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:15 am jjf28 Post #6

    Cartography Artisan

    4. Comparisons


    In this section I'll be describing comparisons, during comparisons, there will always be one point at which there will be one or more conditions you can check to confirm whether your comparison evaluates to true or false, this set of conditions will be encased in square brackets "[]".

    To perform comparisons all we're really doing is counting off both values towards 0.

    A >= 8 && B >= 8 ? A -= 8, B -= 8
    A >= 4 && B >= 4 ? A -= 4, B -= 4
    A >= 2 && B >= 2 ? B -= 2, B -= 2
    A >= 1 && B >= 1 ? B -= 1, B -= 1

    When we do this we'll subtract equal amounts from both A and B, and when one of A or B equals zero the conditions will stop being satisfied, or in other words the countoff will end. The net effect of the operation was: A -= MIN(A, B), B -= MIN(A, B)

    When this happens we know the value that reached zero was the smaller value (or if both reached zero, that the values were equal). Using this we can derive tons of comparison operations.



    J == K

    J@ == K

    J@ == K@

    J >= K

    J@ >= K

    J >= K@

    J@ >= K@

    J <= K

    J@ <= K

    J <= K@

    J@ <= K@

    J > K

    J@ > K

    J > K@

    J@ > K@

    J < K

    J@ < K

    J < K@

    J@ < K@


    Post has been edited 13 time(s), last time on Aug 22 2019, 2:42 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #7

    Cartography Artisan

    5. Addition and Subtraction

    One quick thing to note before we go over addition and subtraction... a countoff, while classically thought of as a way of moving a variable to another place, is really just subtracting itself from its current place and adding it to a new place; if that new place is not zero when you started your countoff, then you're not setting new = old, you're adding old to new (new += old).

    Ergo...
    "A += B" is the same operation as "A = B" when "A = B" meets the pre-condition that A = 0 (which is always assumed to be the case with result holders and temporary variables before the start of an operation in this guide). Keep this in mind as the guide will not always show both.

    R += J

    R += J@

    R += c*J

    R += c*J@

    R = J + K

    R = J@ + K

    R = J@ + K@

    R -= J

    R -= J@

    R = J-K

    R = J@-K

    R = J-K@

    R = J@-K@

    R += ABS(J - K)

    R += ABS(J@ - K)

    R += ABS(J - K@)

    R += ABS(J@ - K@)

    R -= ABS(J - K)

    R -= ABS(J@ - K)

    R -= ABS(J - K@)

    R -= ABS(J@ - K@)


    Post has been edited 7 time(s), last time on Aug 22 2019, 3:07 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #8

    Cartography Artisan

    6. Multiplication


    R *= c

    R *= J

    R *= J@

    R *= R

    R = J*K

    R = J*K@

    R = J@ * K@

    R = J*J

    R = J@ * J@


    Post has been edited 5 time(s), last time on Aug 19 2019, 2:38 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #9

    Cartography Artisan

    7. Introduction to Division


    For division we're essentially going to be using the partial quotient method, so please familiarize yourself with this method of division if you're not already:
    Intro to Partial Quotient method of divison

    Now let's setup our problem...
    R = J / K

    Code
            R
        ______
    K |   J      |
                  |
                  |
                  |


    Since we don't know the values of J and K we simply have to guess at what multiple of K will fit into J, check whether it fits into J, and if it does, subtract it from J and add it to R.

    But what numbers should we guess? You likely already have an intuitive idea that it's going to be the powers of two, and you'd be right! Given that we don't know the value of R (we might know a range it will fall in, but we still don't know the specific value), we need to be asking ourselves what is the smallest set of numbers we can use to cover all possible values of R using successive addition - this is of course is the same question we asked earlier when deriving binary countoffs - and the answer is the powers of two.

    Code
            R
        ______
    K |   J      | 2^31
                  | 2^30
                  | ...
                  | 2^0


    First it helps to consider J as both the initial dividend, and the current remainder after we've subtracted a multiple of K from it.

    So now the question is a bit simpler... how do we check whether 231*K fits into J? Well we know it will fit if the following is true: "J >= 231*K" and we already know we can multiply a counter by a constant, and that we can perform a greater than or equals to check. So for this first step we could do...

    J # T,U # J // Do a non-destructive copy from J to T
    K # 231U, 231V, W # K // Take 231*K into U and V, and use W to restore the previous value of K
    T,U # 0 // Check whether J (T) >= 231K (U)
    U == 0 ? V # -J // If J was >= 231K, subtract 231K from J
    U == 0 ? R += 231 // If J was >= 231K, add 231 to R

    And because J now holds the remainder (or original dividend if J was < 231K), and K still holds the divisior, we can repeat this for 230, 229, ... 20 to finish the division, let's generalize what we're doing here:

    for i = 31 to 0 {
    __ J # T,U # J // Do a non-destructive copy from J to T
    __ K # 2iU, 2iV, W # K // Take 2i*K into U and V, and use W to restore the previous value of K
    __ T,U # 0 // Check whether J (T) >= 2iK (U)
    __ U == 0 ? V # -J // If J was >= 2iK, subtract 2iK from J
    __ U == 0 ? R += 2i // If J was >= 2iK, add 2i to R
    }

    However... we're doing a lot of work here, there are 6 countoffs per power of two, each countoff is up to 32 triggers, and these 6 countoffs will be repeated 32 times, 32*5*32 = 6144 triggers, surely we can do better somehow... The first optimization we could make is rather than taking 2iK into separate variables U and V, we could leave K where it is, and instead subtract 231 times more from J (T) than we do from K, same comparison, just a different approach, and also copy into the temporary variable (W above, U below) in the same step for restoring K.

    for i = 31 to 0 {
    __ J # T,U # J // Do a non-destructive copy from J to T
    __ K, 2iT # U, 2iV // Check if J (T) >= 2iK
    __ K == 0 ? V # -J // If J was >= 2iK, subtract 2iK from J
    __ K == 0 ? R += 2i // If J was >= 2iK, add 2i to R
    __ U # K // Add back however much we subtracted from K earlier
    }

    We've saved us a countoff! But still 32*5*32 = 5120 triggers, still quite unreasonable. The next thing we can try is subtracting from J as we do the J >= 2iK check, if J was < 2iK we need to add back whatever we took away (which we know based on the value of T below), if J was >= 2iK then the subtraction can be kept and we can add to R. In the below, A is a switch.

    for i = 31 to 0 {
    __ K, 2iJ # T
    __ K == 0 ? A = true, R += 2i // J >= 2iK, add 2i to R
    __ K >= 1 ? A = false // J < 2iK
    __ A ? T # K // 2iK fit into J, only restore the divisor, K, for the next round
    __ !A ? T # K, 2iJ // 2iK did not fit into J, restore both the divisor, K, and the previous value of J
    }

    Now we're starting to look reasonable, only 32*3*32 = 3072 triggers; though we can still do better. See that second to last countoff above? Where we're only restoring the divisor K? If instead of that we just took 32 copies of the divisor, we don't need that countoff anymore. Of course we're then using 31 more death counters as temporary storage space, but that's a small price to pay for a thousand triggers in my opinion.

    K # T31, T30, ... , T0
    for i = 31 to 0 {
    __ Ti, 2iJ # U
    __ Ti == 0 ? R += 2i
    __ Ti >= 1 ? U # 2iJ
    }

    Now we're down to a blessed 32*2*32 + 32 = 2080 triggers, but wait! there's more! As we go through each iteration of the loop, we know that J is certainly going to be less than some value, when i=31, J <= 4294967295 (the limit of a counter), then 231 will be subtracted, so when i=30, J <= 2147483647, when i=29, J <= 1073741823, so for a given value of i, J <= 2i+1 - 1, so we don't need the full 32-trigger binary countoff at each step. K on the other hand, has a minimum of 1 (else we're dividing by 0) and a maximum of 4294967295 at each step, it doesn't change as we go through each iteration.

    Code
    i ____ MIN(K) ____ MAX(K) ____ MIN(J) ____ MAX(J)
    31 ____ 1 _____ 4294967295 ___ 0 _____ 4294967295
    30 ____ 1 _____ 4294967295 ___ 0 _____ 2147483647
    29 ____ 1 _____ 4294967295 ___ 0 _____ 1073741823
    28 ____ 1 _____ 4294967295 ___ 0 _____ 536870911
    27 ____ 1 _____ 4294967295 ___ 0 _____ 268435455
    26 ____ 1 _____ 4294967295 ___ 0 _____ 134217727
    25 ____ 1 _____ 4294967295 ___ 0 _____ 67108863
    24 ____ 1 _____ 4294967295 ___ 0 _____ 33554431
    23 ____ 1 _____ 4294967295 ___ 0 _____ 16777215
    22 ____ 1 _____ 4294967295 ___ 0 _____ 8388607
    21 ____ 1 _____ 4294967295 ___ 0 _____ 4194303
    20 ____ 1 _____ 4294967295 ___ 0 _____ 2097151
    19 ____ 1 _____ 4294967295 ___ 0 _____ 1048575
    18 ____ 1 _____ 4294967295 ___ 0 _____ 524287
    17 ____ 1 _____ 4294967295 ___ 0 _____ 262143
    16 ____ 1 _____ 4294967295 ___ 0 _____ 131071
    15 ____ 1 _____ 4294967295 ___ 0 _____ 65535
    14 ____ 1 _____ 4294967295 ___ 0 _____ 32767
    13 ____ 1 _____ 4294967295 ___ 0 _____ 16383
    12 ____ 1 _____ 4294967295 ___ 0 _____ 8191
    11 ____ 1 _____ 4294967295 ___ 0 _____ 4095
    10 ____ 1 _____ 4294967295 ___ 0 _____ 2047
    09 ____ 1 _____ 4294967295 ___ 0 _____ 1023
    08 ____ 1 _____ 4294967295 ___ 0 _____ 511
    07 ____ 1 _____ 4294967295 ___ 0 _____ 255
    06 ____ 1 _____ 4294967295 ___ 0 _____ 127
    05 ____ 1 _____ 4294967295 ___ 0 _____ 63
    04 ____ 1 _____ 4294967295 ___ 0 _____ 31
    03 ____ 1 _____ 4294967295 ___ 0 _____ 15
    02 ____ 1 _____ 4294967295 ___ 0 _____ 7
    01 ____ 1 _____ 4294967295 ___ 0 _____ 3
    00 ____ 1 _____ 4294967295 ___ 0 _____ 1


    Technically our check is whether K == 0 (Ti == 0), so you'd think we need enough to take K to zero each time, but remember the countoff will cease all action once either J or K reach zero, so we only need J to be able to reach zero to perform our J >= 2iK check. Ergo when i=31 we require 32 triggers #31, i=30 we require 30 triggers #30, ..., i=0 we require 1 trigger.

    K # T31, T30, ... , T0
    for i = 31 to 0 {
    __ Ti, 2iJ #i U
    __ Ti == 0 ? R += 2i
    __ Ti >= 1 ? U #i 2iJ
    }

    Now our trigger count looks even nicer, though a bit harder to calculate.
    32 + (32 + 1 + 32) + (31 + 1 + 31) + (30 + 1 + 30) + ... + (2 + 1 + 2) + (1 + 1 + 1)
    32 + 32 + (32 + 32) + (31 + 31) + (30 + 30) + ... + (2 + 2) + (1 + 1)
    64 + (2*32 + 2*31 + 2*30 + ... + 2*2 + 2*1)
    64 + (64 + 62 + 60 + ... + 6 + 4 + 2)
    64 + (64 + 2) + (62 + 4) + (60 + 6) + ... + (34 + 32)
    64 + 16*66

    Bringing us to a beautiful 1120 triggers. Ok maybe not stunningly beautiful but much nicer than it might otherwise be. If we want to take the count down even further the only option is for us to the range in which J and K fall, lower values for either mean less iterations of the loop are necessary. For every countoff of K you can skip (n countoffs total) you save 67n-n2 triggers, meaning if your K is no larger than 65535, then you'll save 16 countoffs (816 triggers) and the whole "R = J/K" operation will only take you 304 triggers!

    One tiny optimization remains for the last iteration of the loop... we don't necessarily need J to hold the proper remainder (in some cases we might want it to, but that depends on the specific operation and which variables you've chosen to preserve). Moreover that countoff #0 is only one trigger, when the next trigger is going to be a condition check based on the single constant we subtracted in the previous trigger, we can combine the two triggers.

    Giving us a final algorithm...

    R = J / K
    -----------------------------------
    K # T31, T30, ... , T0
    for i = 31 to 1 {
    __ Ti, 2iJ #i U
    __ Ti == 0 ? R += 2i
    __ Ti >= 1 ? U #i 2iJ
    }
    T0 == 1 && J >= 1 ? R += 1

    Post has been edited 19 time(s), last time on Aug 23 2019, 3:43 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #10

    Cartography Artisan

    8. Integer Division


    R /= c (integer division)


    R /= J (integer division)


    R /= J@ (integer division)


    R = J / K (integer division)


    R = J@ / K (integer division)


    R = J / K@ (integer division)


    R = J@ / K@ (integer division)


    R /= c, J = R % c (integer division with remainder)


    R /= J, J = R % J (integer division with remainder)


    R /= J, L = R % J (integer division with remainder)


    R /= J@, L = R % J@ (integer division with remainder)


    R = J / K, J = J % K (integer division with remainder)


    R = J / K, K = J % K (integer division with remainder)


    R = J / K, L = J % K (integer division with remainder)


    R = J@ / K, K = J@ % K (integer division with remainder)


    R = J@ / K, L = J@ % K (integer division with remainder)


    R = J / K@, J = J % K@ (integer division with remainder)


    R = J / K@, L = J % K@ (integer division with remainder)


    R = J@ / K@, L = J@ % K@ (integer division with remainder)


    R %= c


    R %= J


    R %= J@


    R = J % K


    R = J@ % K


    R = J % K@


    R = J@ % K@


    Post has been edited 32 time(s), last time on Aug 22 2019, 9:05 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #11

    Cartography Artisan

    9. Rounded Division


    R = ROUND(R / c)


    R = ROUND(R / J)


    R = ROUND(R / J@)


    R = ROUND(J / K)


    R = ROUND(J@ / K)


    R = ROUND(J / K@)


    R = ROUND(J@ / K@)


    R = ROUNDUP(R / c)


    R = ROUNDUP(R / J)


    R = ROUNDUP(R / J@)


    R = ROUNDUP(J / K)


    R = ROUNDUP(J@ / K)


    R = ROUNDUP(J / K@)


    R = ROUNDUP(J@ / K@)


    Post has been edited 12 time(s), last time on Aug 23 2019, 2:38 am by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 18 2019, 1:16 am jjf28 Post #12

    Cartography Artisan

    10. Fractions and Decimal Values


    Fractions are super simple - really all there is to it is you're maintaining the value above the dividing line and the value below the dividing line, when you need to use the value you can use one of the division algorithms to get the nearest (or ceiling/floor'd value, ceiling given by ROUNDUP, and floor given by the regular integer division).

    Maintaining a decimal value is a bit more tricky, there are many ways to represent a decimal value, aka a floating-point number, but in StarCraft the most useful approach is typically to use your number, perhaps rounded ever so slightly, multiplied by a power of two, for more precision than this allows, you can maintain the fraction using two counters.

    A value multiplied by 20 has no decimal values, by 21 you have 0.0 and 0.5 (0/2, 1/2), by 22 you have 0.00, 0.25, 0.50, and 0.75 (0/4, 1/4. 2/4, 3/4), by 24 you have 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875 (0/8, 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8), and so on.

    This approach gives you the power to check against constants multiplied by the same value, e.g. if you have a value 6.25, and you're using 22 as your multiplier, then you store your 6.25 in a counter as 25, then if you need to check if your counter is greater than 6, you do COUNTER >= 24, you need to check if it's less than 6.5, you do COUNTER <= 26.

    Any manipulation of the value (e.g. arithmetic with other counters) simply requires you to scale the other values involved accordingly, say your value is A, and you've scaled it up by 22... and you have another value B, which is an integer/unscaled, performing B -= ROUND(A) would look like...

    ...
    A >= 32 ? B -= 8
    A >= 16 ? B -= 4
    A >= 8 ? B -= 2
    A >= 4 ? B -= 1
    A >= 2 ? B -= 1 // round!

    Post has been edited 1 time(s), last time on Aug 23 2019, 3:02 am by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 19 2019, 5:27 pm Wormer Post #13



    Ten posts? Forreal? :cool2: I had to read this thing yet, but I like the established notation yet



    Some.

    Aug 22 2019, 6:40 am T-warp Post #14

    Unlimited N-word pass winner

    Quote from jjf28
    3. Symbols and Trigger Equivalents
    A >= c ____ "A is at least c" _______ Current Player has suffered at least 2 deaths of Cantina.
    A <= c ____ "A is at most c" _______ Current Player has suffered at most 1 deaths of Cantina.
    A == c ____ "A equals c" __________ Current Player has suffered exactly 0 deaths of Cantina.
    A += c ____ "Add c to A" __________ Modify death counts for Current Player: Add 1 for Cantina.
    A -= c _____ "Subtract c from A" ____ Modify death counts for Current Player: Subtract 1 for Cantina.
    A = c ______ "Set A to c" __________ Modify death counts for Current Player: Set to 1 for Cantina.
    Do I understand it correctly that "A" is a pointer (EPD value) and "c" is constant (is Cantina some special value)?

    Also, if your map requires some advanced mathematics, you can detour (or replace) death action function (or unused action type like "pause game", in init map trigger cycle) to add special values to support stuff like ... basically anything you can write in ASM. It won't work in remastered, but I think if we designed it perfectly (division, mutltiplication, addition, subtraction, movement), we could propose it (invocation of such action with non native pointer could check binary code for known values) to developers. Having these operations done in single trigger action would be incredible for many reasons.

    Post has been edited 1 time(s), last time on Aug 22 2019, 6:53 am by T-warp.




    Aug 22 2019, 1:18 pm jjf28 Post #15

    Cartography Artisan

    Quote from T-warp
    Do I understand it correctly that "A" is a pointer (EPD value) and "c" is constant (is Cantina some special value)?

    A is a counter, aka some 4-byte value in memory that you're changing (could be a death counter, could be a value somewhere in memory if you're using EUDs/EPDs), it would be inappropriate to call it a pointer. If current player was being used like a pointer you could say we're dereferencing a pointer, if A was using some extended unit or player (but not a dynamic current player) we'd be updating something at a static address, if the thing at that static address happened to be a pointer then we'd be updating a pointer, or if it was the dynamic current player value we'd be updating something we use as a psedo-pointer. The letter c is indeed a constant, Cantina is nothing special, just one of the first unused units in the list alphabetically speaking.

    Quote from T-warp
    Also, if your map requires some advanced mathematics, you can detour (or replace) death action function (or unused action type like "pause game", in init map trigger cycle) to add special values to support stuff like ... basically anything you can write in ASM. It won't work in remastered, but I think if we designed it perfectly (division, mutltiplication, addition, subtraction, movement), we could propose it (invocation of such action with non native pointer could check binary code for known values) to developers. Having these operations done in single trigger action would be incredible for many reasons.

    I'm not too interested in writing ASM for 1.16.1, if the SC:R developers are up for it it's ~20 lines of code to add to the death action, using one of the unused fields and using the disambiguator they're already using for bitmasks - adding such functions has already been proposed and our ASM wouldn't be useful to them.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Aug 22 2019, 9:00 pm T-warp Post #16

    Unlimited N-word pass winner

    Quote from jjf28
    I'm not too interested in writing ASM for 1.16.1
    We would write it just once, no more than 64 bytes total.

    Quote from jjf28
    if the SC:R developers are up for it it's ~20 lines of code to add to the death action
    Actually, no. In order to support both vanilla and remastered stuff, I was considering manipulating 0x512818, which is function pointer to pause game trigger action function. It would require like 3 lines of code (assuming they didn't mess with original action functions) to perform integrity check (or a few more to cache check results).

    After that, we wouldn't need any counters at all to move values in memory, which would reduce trigger count and complexity in general.




    Options
      Back to forum
    Please log in to reply to this topic or to report it.
    Members in this topic: None.
    [01:56 am]
    Oh_Man -- cool bit of history, spellsword creator talking about the history of EUD ^
    [09:24 pm]
    Moose -- denis
    [05:00 pm]
    lil-Inferno -- benis
    [10:41 am]
    v9bettel -- Nice
    [2024-4-19. : 1:39 am]
    Ultraviolet -- no u elky skeleton guy, I'll use em better
    [2024-4-18. : 10:50 pm]
    Vrael -- Ultraviolet
    Ultraviolet shouted: How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
    hey cut it out I'm getting all the minerals
    [2024-4-18. : 10:11 pm]
    Ultraviolet -- :P
    [2024-4-18. : 10:11 pm]
    Ultraviolet -- How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
    [2024-4-17. : 11:50 pm]
    O)FaRTy1billion[MM] -- nice, now i have more than enough
    Please log in to shout.


    Members Online: Oh_Man, Roy