Live data from Hacker News

Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

reddit.com

61–70 of 267 posts

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#61

Earlier quoted context omitted.

Note that the April 2023 patch is live, and perhaps one of the biggest changes to the meta ever. I don't think anyone really knows how the new meta will shake out. Build orders will absolutely change this patch. Though I guess that only matters if you were hoping to get to a competitive level. Definitive Edition does have a very good tutorial mode for people interested in online combat, and "Extreme" AI is 100% legit…

What’s new? Seems kind of crazy to introduce a meta changing past to an old game like this that has managed to stay popular.

> old game

They have been releasing expansion adding new civilizations every ~6 months or so together with a bunch of balance changes.

> stay popular

It didn't. Not too such an extent as it is now. For years it had only community support and a fairly small player base. Microsoft resurrected it not so long ago.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#62
post #30

Earlier quoted context omitted.

Seems all that could have been avoided by using rationals in general and the condition for living being >0 instead of dying The more I write code, the more I notice how rarely one truly should want floats in most kinds of programs and how they almost always carry a whole bag of problems with them.

You mean fixed point numbers presumably, because at the time using integers that use a variable amount of memory was totally out of the question. Today it’s still terrible for performance because you can’t use caches and simd effectively when numbers can’t be packed directly in arrays.

Does AoE2 do that? Packing numbers directly into arrays? Or does it rather deal with objects, which are variable size anyway? I am not sure how much high performance work has been done in AoE2. Maybe not that much.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#63

Earlier quoted context omitted.

> The formula was probably `return new_maxhp * (old_hp / old_maxhp);` ... > Due to floating point rounding errors, convert_hp(1, 55, 55) equals 0.999999940395355224609375, which is less than 1, which means the unit dies. Ahh. The old "Multiplying and dividing is associative... for the set of reals, not for the subset that you can actually afford to compute with." So it should have been `(new_maxhp * old_hp) / old_max…

Seems all that could have been avoided by using rationals in general and the condition for living being >0 instead of dying The more I write code, the more I notice how rarely one truly should want floats in most kinds of programs and how they almost always carry a whole bag of problems with them.

The big problem with rationals is that it's easy to end up with both nominator and denominator growing without bounds, which IMO is an even bigger pitfall than those of floating-point or fixed-point.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#64
post #29

Earlier quoted context omitted.

Could you explain to a noob why does this happen, exactly? Is it because 55/55 != 1, or 1*1 != 1? I understand float can't store certain integer/finite decimal precisely, but why would a/a not be 1 if both numerator and denominator are the same (imprecise) number?

it's because the calculation (1/ 55) is less than the real number 1/55, so when you multiply it by 55 you get a result less than 1. for an easier to follow example, say you can only work with two decimal places, hp was 1, old max hp was 3 and new max hp was also 3. if you do (3 * 1) / 3 you get 3 / 3 = 1 as intended. if instead you do 3 * (1 / 3) you get 3 * 0.33 = 0.99 which is less than 1

This effect becomes more pronounced, when substracting (but probably also adding) small amounts from big amounts, because more bits are needed to store the big number's more significant digits and floats then lose bits for the less significant digits. That is why, if one needs precision, needs to sort numbers first and then start calculating with the smallest numbers first, working ones way up to the bigger numbers.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#65

Earlier quoted context omitted.

> The formula was probably `return new_maxhp * (old_hp / old_maxhp);` ... > Due to floating point rounding errors, convert_hp(1, 55, 55) equals 0.999999940395355224609375, which is less than 1, which means the unit dies. Ahh. The old "Multiplying and dividing is associative... for the set of reals, not for the subset that you can actually afford to compute with." So it should have been `(new_maxhp * old_hp) / old_max…

Seems all that could have been avoided by using rationals in general and the condition for living being >0 instead of dying The more I write code, the more I notice how rarely one truly should want floats in most kinds of programs and how they almost always carry a whole bag of problems with them.

> Not sure in what case it makes sense to have dying at smaller than 1. To the user all the numbers are integers, so why not use rationals instead of floats?

If killing unit takes X attacks, user buys 10% attack damage upgrade, and it still takes X attacks they will be annoyed. If rounding down takes that to x-1 attacks it might be preferable.

That being said you're absolutely right. just use HP*100 in calculations and display HP/100 to the user, and when you need to round into specific direction do it explicitly

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#66

Earlier quoted context omitted.

Aren't rationals subject to exponential blowup in storage/precision? IMO the best approach for business logic values is picking the smallest meaningful unit and representing it with integers. Precision and storage requirements become very easy to reason about.

> IMO the best approach for business logic values is picking the smallest meaningful unit and representing it with integers. How would you design an RTS unit stats system to avoid this AoE Monk HP bug using only integer types?

`(new_maxhp * old_hp) / old_maxhp` avoids the original problem when using integers. Though you still need to make sure your type is big enough to not overflow.

Or using fixed-point (which are a relatively simple abstraction over integers): `old_hp * (new_maxhp * old_maxhp)`.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#67

Earlier quoted context omitted.

Aren't rationals subject to exponential blowup in storage/precision? IMO the best approach for business logic values is picking the smallest meaningful unit and representing it with integers. Precision and storage requirements become very easy to reason about.

> IMO the best approach for business logic values is picking the smallest meaningful unit and representing it with integers. How would you design an RTS unit stats system to avoid this AoE Monk HP bug using only integer types?

     max(1,...

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#68

Earlier quoted context omitted.

> Seems all that could have been avoided by using rationals in general It's 2023: I'm still surprised that so many new languages/platforms still only provide only basic integer and IEEE-754 types for numerics: Rust, C#, Java, Swift and others still lack built-in, runtime-provided, nor even standard-library-provided rational types, which I assume really should be the preferred type for most business/domain/application…

Aren't rationals subject to exponential blowup in storage/precision? IMO the best approach for business logic values is picking the smallest meaningful unit and representing it with integers. Precision and storage requirements become very easy to reason about.

Did this exponential blowup ever prove troublesome in practice? I think it can be easily solved by strategically placed rounding steps. But explicit rounding is advisable in many cases anyway.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#69
post #22

Earlier quoted context omitted.

Wow, the logic to model these kinds of social structures makes my head hurt. But you would have to do it. And I wonder about dead children. I remember one game where there could be no dead children.

The logic is less complex than you'd think. It's generally something like "has flag paranoid, has flag child (patched to has flag alive child), then 1% yearly chance of fearing a plot." It feels complex, thinking of the actual logic killed a fair amount of the fun for me. >And I wonder about dead children. I remember one game where there could be no dead children. Probably Skyrim, though it's not rare. Crusader Kings…

> The logic is less complex than you'd think. It's generally something like "has flag paranoid, has flag child (patched to has flag alive child), then 1% yearly chance of fearing a plot." It feels complex, thinking of the actual logic killed a fair amount of the fun for me.

But doesn't this overlook the emergent complexity from many actors running these simple rules, plus interactions between them?

A lot of the shenanigans in Dwarf Fortress arise from relatively simple actors following their relatively simple plan, until those plans clash or interact with each other and crazy things start happening - such as prioritizing drinking booze over pulling a lever to lock out a monster.

Re: Aztec monks with 1/55 HP no longer die when picking up or dropping a relic

#70
post #59

Earlier quoted context omitted.

Well, in this case, if a unit is converted to a different type and back, it would just keep its (absolute) value of HP (in this case 1), no need to scale anything.

You haven’t thought it through yet. In this case, HP _is_ an integer, and so is the MaxHP for the unit type. And yet the bug still occurs! Storing the HP as an integer does not prevent the problem from ever occurring, because fundamentally the user still expects HP to be proportional. That is, suppose a unit is sitting there at full health, and you research a tech that increases that unit’s max hp? Should the unit no…

>That is, suppose a unit is sitting there at full health, and you research a tech that increases that unit’s max hp? Should the unit now be at less than full health, as if it had been in combat? Users probably don’t like that.

>Suppose it is at full health, and then it gets downgraded to a type with less maximum health. Does it stay at it’s current HP? Users probably won’t like being attacked with supercharged units that have extra HP; they’ll think that the other player was cheating somehow.

>What if it is damaged and at half health, then gets upgraded. Should it be fully healed? Gain HP equal to the difference between the new and old maximum HP? Or should it gain half of that, so that it stays at half health?

     if (oldHP == maxOldHP) { newHP = newMaxHP }
     else { newHP = max(1,scale(oldHP,oldMaxHP,newMaxHP))
not exactly complex
Post reply on HN