Difference between revisions of "User talk:Bomb Bloke:Firing Accuracy"

From UFOpaedia
Jump to navigation Jump to search
(New page: {{tocright}}Consider this Bomb Bloke's draft/discussion about firing accuracy. This topic has been dragging on for quite some time now, so I figure it to about time I h...)
 
Line 172: Line 172:
  
 
::::: XCOM 1.2 had some source code comments indicating that the C source targeted Watcom C.  That means the "easy rand" available is the C standard library rand(), which without processing returns an integer from 0..RAND_MAX, where RAND_MAX may be as low as 32,767.  -- [[User:Zaimoni|Zaimoni]] 12:36, 17 April 2009 (CDT)
 
::::: XCOM 1.2 had some source code comments indicating that the C source targeted Watcom C.  That means the "easy rand" available is the C standard library rand(), which without processing returns an integer from 0..RAND_MAX, where RAND_MAX may be as low as 32,767.  -- [[User:Zaimoni|Zaimoni]] 12:36, 17 April 2009 (CDT)
 +
 +
: 55 degrees horizontal deviation range corresponds to 440 1/8 degree units; quick-but-lousy-RNG C source for the eighths-of-degrees input to the tangent function at accuracy 0 should be <code>(rand()%147+rand()%147+rand()%147)-219</code> .  This plausibly is from a "deviation output" function that returns 73 at accuracy 0. Corresponding psuedocode:
 +
<pre>const int tmp = acc_func(...);
 +
const int rng_scale = 2*tmp+1;
 +
const int rng_bias = 3*tmp;
 +
 +
....
 +
 +
(rand()%rng_scale+rand()%rng_scale+rand()%rng_scale)-rng_bias;</pre>
 +
 +
:  -- [[User:Zaimoni|Zaimoni]] 13:58, 26 December 2010 (CST)
  
 
===Vertical Error vs Horizontal Error===
 
===Vertical Error vs Horizontal Error===

Revision as of 19:59, 26 December 2010

Consider this Bomb Bloke's draft/discussion about firing accuracy. This topic has been dragging on for quite some time now, so I figure it to about time I had a stab at it.

My view on the matter is this: When you fire a gun, UFO works out the horizontal and vertical angles between your unit and the target, then modifies these depending on your overall accuracy stat.

Say for example you have a perfect 100% (or better) "accuracy". The lines are nearly unmodified, and so near guaranteed to hit (assuming there are no obstructions, in which case you should get a "No LOF!" message when you attempt to fire. Note that the game doesn't ALWAYS give this message and will sometimes allow you to attempt "impossible" shots. In these cases, less-then-perfect aim might still allow the shot to land).

As your rated accuracy decreases, target size and distance become all important: As size decreases and distance increases, the range of angles that could land a hit fall. As accuracy decreases, the range of angles that a shot can take rises and so it becomes less likely that a given shot will fall within the "hitting" range of angles.

(As for why multiple angles can "hit", think of your target as a dartboard: Perfect aim would result in a bullseye, approximate aim would still hit the board, and bad aim would miss altogether. However, in X-Com, you get full "points" so long as the bullet hits the target at all).

That is to say, the ultimate firing accuracy formula would be (range of acceptable hitting angles)/(range of possible firing angles). Assuming, of course, that when firing you're just as likely to fire at your maximum "worst" angle as you are your "best", the range of angles that can hit is determined by target distance and size, and the range of angles that can be fired along is determined by your rated firing accuracy.

However, as it turns out, the angle your shots take isn't entirely random - they follow a bell curve. You're more likely to at least hit near the target then you are to get a massive "air ball" shot. So the formula in truth will end up looking a bit more complex then that.

Firing Point Origin

To get the absolute range of angles a unit can fire along, you first need to know where a unit is firing from. To this end I created a copy of my game folder and modified a save game, some terrain files, and the LOF templates.

A basic map of a tile. As it turns out the Y axis really points the other way.

Now, when we talk about UFO maps, we generally refer to their dimensions in terms of how many tiles there are. However, each tile is really made up of smaller "points": A 16x16x24 point space. This means that a 30x30x4 map has 3,600 tiles in it, and 22,118,400 points within those. When a unit fires, he doesn't aim from his tile at his target's tile, he aims from a point within his tile at a point within his target's tile.

This first test was aimed at discovering exactly where a shot originates from, by creating tiles on units to selectively block their LOF. By seeing which tiles did and which did not, it could be seen exactly (or, at least, as accurately as I think I'm going to get it) where the shots were coming from.

The Empire's got nothing on BB's clone army.

The first row of units served as targets for the second, which stood on tiles similar to the large earth blocks you see in bases but with one layer removed from each. Each therefore entirely blocked LOF so long as the removed layer wasn't at the same height as the unit's firing origin point. The only unit able to fire through the gap was the one sitting on the tile with the third layer down removed.

However, when defining tiles with LOFTemps, you can only stack the layers 12 high. Presumably the game "double stacks" them to make the resulting 24 point high object. Because of this, the removed layers accounted for two possible Z co-ordinates, meaning the exact value there is still unknown. Might be able to work something out to get it more precise, I dunno.

The third row stood on walls facing to the east/west. Unlike the second row, in this example I moved a single thin wall through each tile, as opposed to moving a "gap". This meant that only one unit would not be able to fire, and that was the one sitting on the ninth tile (made up of my layers from LOFTemps[15]).

LOFtemps used, as opposed to the usual set.

The forth row followed more or less the same concept, with the walls facing north/south. Only the first unit was unable to fire (LOFTemps[23]).

So! With this information, getting a (x,y,z) co-oridinate is easy enough: It's either (8,15,18) or (8,15,19) (the Z co-ord being imprecise due to the 12 LOFTemp layer limitation).

Note that this value only goes for units with a facing of north. When I get time I'll go through and work it out for the other directions. Furthermore, Sectoids are likely to have a lower firing point (as their guns are rendered lower on the screen).


Origins.png

I found an interesting piece of code (offset 40D8E0 for the curious). The formula for the starting zpos when firing looks like this: unitpos.zpos*24+unitref.terrain_height-unitref.standing_height+27-unitref.floating_height and for kneeling units: unitpos.zpos*24+unitref.terrain_height-unitref.kneeling_height+27-unitref.floating_height (unitref.terrain_height is offset 0x27 in unitref.dat) I did a live check with a debugger, for a standing unit on the lowest level it gives this: 3*24+0-16+27-0=77 Since the engine seems to use zpos=0 for the ceiling of the topmost level, then the floor must be at offset 4*24=96. floor_level-firing_level=96-77=19, in accord with your empirical results.

Now for xpos and ypos, the engine uses several tables, indexed by the facing orientation (offset 0xa in unitref.dat): .data:0046B5E4 dw 8,0Eh,0Fh,0Fh, 8, 1, 1, 1 for xpos, and .data:0046B5C4 dw 1, 1, 8,0Fh,0Fh,0Fh, 8, 1 for ypos. When facing north, the shot should come from 8x1x19. Note that it is in disaccord with your experience. Maybe you don't use the same point of origin as the engine? Seb76 08:44, 3 May 2008 (PDT)


You are correct. Moving my origin to match yours brings my data into line (I was limited to guessing it's correct location). Awesome to see the ceiling size confirmed at long last. :D - Bomb Bloke 02:52, 8 May 2008 (PDT)


Big units use 2 other tables located at offsets 46B604 and 46B624. I did not try to change these values to see if it changes the position where the shots come from. Seb76 08:44, 3 May 2008 (PDT)


The xpos/ypos information agrees with my own in vivo research (it explains several LOS different than LOF paradoxes I have explicitly verified). -- Zaimoni 11:49, 3 May 2008 (CDT)

Rated Accuracy Vs Angle Range

BBFiringPointTest4.png
BBFiringPointTest5.png

Just a few results before I go off camping for the weekend. First off, note that the logger used on this occasion is one I wrote up a year ago. It's not entirely accurate, and needs to approximate values (this is because I can only get it to think in terms of angles between tiles, as opposed to angles between points - as a result you see "steps" in the line graphs provided here). I've thought up some improvements for it (like sticking a roof in the map used so I can get some decent vertical angle stats), but even if I implemented those it'd still never be "accurate".

That said, I got 2550 results for 0% firing accuracy overnight and 1876 for 50% today. I've got the system in concern running trials for 25% until tomorrow.

The graphs displayed summarise these angles. Essentially they go from "those that went to the left" across to "those that went to the right". The longer the line stays at a given level, the more shots took that angle.

At 0% accuracy, the largest horizontal angle I got AWAY from the target was 27.5 degrees (that is to say, a shot could go anywhere within a range of 55 degrees), the average angle being 7.32 degrees. With the side graph you can get an idea of the distribution: Your shots are more likely to hit or at least get close then they are to be far away.

Bumping the rated accuracy to 50% lowered the maximum angle to 17.01 degrees (average of 2.75). Again, you're a lot more likely to get close to the target then you are to reach the possible extremes.

I've since done tests at 100% accuracy and found that the same curve exists, just squished right down. You still have a chance to miss, but only by slight amounts. A formula should be apparent in there somewhere.

At a glance I think it's an exponential equation divided by your rated accuracy? With a few more tests I suppose it'd be more apparent.

My theory is this that the program first runs "was shot accurate?" check. If yes, then shot deviates 0 and hits the target. If shot misses, calculate path of bullet. Path of bullet follows formula that gives that bell curve.
I rather suspect that at 25% accuracy, 25% of your shots will be on target, at 50%, 50% will be on target, so on and so forth.
There might be some additional weird "random drift" that makes 100% and higher accuracy still miss. *Shrugs*. - Jasonred 11:07, 15 March 2009 (EDT)
I am absolutely convinced that there is no "was shot accurate?" check. If there was one, I reckon it'd be apparent on the graphs here. I could be wrong, but I believe I've seen no data that supports the theory. No, finding that a certain accuracy rating "seems right" when firing from a certain distance is not supporting data, in that this is the case regardless of whether an "accurate shot" check exists. - Bomb Bloke 11:25, 15 March 2009 (EDT)
Despite the fact it will make your life even harder, I have to remind you that there is also PARTIAL COVER to factor into the tests... what happens when the target is behind a window (modded to be indestructible?), behind a hedge, only his head is visible poking out through the floor (bug here lol)... sometimes a shot will go THROUGH a window, sometimes it will HIT the window... - Jasonred 11:07, 15 March 2009 (EDT)
Remember, the "accuracy formula" I'm looking to determine simply returns an angle, which, dependant on your overall accuracy score an the random number generator, will go off on some who knows what direction. Whether that angle leads to the target is dependent on the visible target area versus the angles you'll get based on your accuracy stat (this "target area" concept is the purpose of the next section down, mostly unwritten because it's pointless until THIS formula is figured out).
That is to say, there's no real formula that can tell you the REAL chance to hit a target without actually inputting all the tile data in addition to using the "angles formula" I'm after here. Luckily, once the angle formula is determined, it shouldn't be too hard to have my battlescape editor calculate the "true" chance of a shot hitting. - Bomb Bloke 11:25, 15 March 2009 (EDT)


I wonder if it's possible that the game's maximum firing angle (e.g. at zero accuracy) is one radian - about 57.3 degrees? This is near your observed limit of 55 degrees. The game engine would probably use trig functions based on radians as I believe they are the most efficient. Given the power of the computers they were programming for, this vector work was pushing the envelope so they would need to be efficient. - Spike 11:28, 15 March 2009 (EDT)
I would say that yes, given the close proximity (and the inherent inaccuracy of my logger, due to the inability to pick up on the precise point a bullet hit), one radian is correct.
The COS.DAT page will probably be relevant at some point, dunno if you've read that. - Bomb Bloke 12:14, 15 March 2009 (EDT)
Also could you clarify the axes on these 2 graphs/histograms? I'm having a lot of trouble understanding them. Is it possible that the vertical axis should be frequency, and the angle should be on the horizontal axis? Or am I being dumb? If I'm understanding them correctly, we are not seeing a linear distribution of error angles - as you would get from say
ActualAngle=AngleToTarget+(rand(57)-(57/2)) 
what we are getting is angles that cluster together near the correct aimpoint, and frequency falls off quickly as you move away from the correct aimpoint.
You are reading the charts correctly. The vertical axis shows how often a specific angle was picked to fire along by the game, and the horizontal axis shows the angles themselves. The reason the charts are symetrical is because shots can either go to the left or right of the target (an angle of 0 means it hit). Even with 0 FFA, you're a lot more likely to send a bullet near the target then you are to send one on the maximum diverging angle - hence my statement, "I think it's an exponential equation divided by your rated accuracy". - Bomb Bloke 12:14, 15 March 2009 (EDT)
Guess: The graphs look not unlike a tangent or inverse tangent function. That might suggest that the firing function injects a linear amount of perpendicular drift (horizontal and maybe vertical) onto the bullet's vector. Maybe try graphing the tan() or arctan() of the data from the "zero accuracy" tests, and see if you get a horizontal straight line.
arctan (0.5), which might be relevant if maximum "perpendicular error" is 1 map square wide per map distance unit, is 26.565 degrees, near to your value of 27.5 degrees. Arctan (0.25), which corresponding to half that "perpendicular error", half a map square wide per map distance unit (and maybe corresponding to 50% FFA????), is 14.036 degrees, again near to your value of 17 degrees for 50% FFA. But only 'near', and higher. Hmm. Your data have a horizontal granularity of 1/16th of a map square distance unit, is that right? So there could be some data slightly outside the true upper limit for angles. It depends on the range to your target in your test setup I guess. Spike 11:28, 15 March 2009 (EDT)
This is where my brain starts to melt, mind you, it's 3:15am here so I'm probably not good for any number crunching right now. But I reckon you're onto something even if I can't wrap my brain around what!
I put the target about 25 squares back (middle of the map). Really can't remember. Ideally I'd've set him to be a pixel in size, but back then the "using LOFTemps for unit size" thing was just a theory I had, not a proven fact (I never got around to updating this page when it WAS proven, that's how long these notes have been here).
Yes, granularity is a 16th of a standard user-visible map tile. On the horizontal plane anyway. Can't remember if I set it to 24ths on the vertical, but I probably did. The problem is, of course, that it can only pick up data according to whether a bullet hit a tile or not (as opposed to data on where the tile was hit), so although the range of angles should be "close to accurate" there's still those nasty steps everywhere which have to be "guesstamated".
I never really bothered much with the vertical data as I was mostly "proving the concept", figured I'd get to that later. I know I logged it (I was even able to draw an ellipse to show where all the bullets would fall, your maximum vertical angles are of course no where near your maximum horizontal ones), but I can't find the charts in concern. They weren't that good anyway as the test map had no ceiling.
I distinctly remember logging and graphing the 100% FFA data but can't find that either. Probably deleted it from my system (in the misguided belief that I'd uploaded it all here). I cannot find any of my related logs at all, in fact, just a single zip file with the logger, test map and automated script file ready to go. Again, this may be because I've deleted them, or because I have nearly ten thousand files in my UFO directory. My main one, anyway. I have a few such directories. - Bomb Bloke 12:14, 15 March 2009 (EDT)
OK. The curve you graph above for "zero accuracy" looks not unlike what happens if you graph tan(x) (in degrees), where x is from +0.5 to +0.5. But I think that's a coincidence, since your graph is not a standard histogram - you are showing "frequency" by the length of lines on the x axis, rather than by the height of bars along the y axis as in a standard histogram. In order to turn your graph into a standard histogram it would need to be more or less inverted I think.
The link to the "zero accuracy" data set is broken now. From what you said above, the data is lost now, which is a shame as it would be nice to do a histogram of tan(a) for all the angles "a" in that data set. You might see a flat line, linear distribution. Or not. If the angles are "bunching up" in frequency toward the target centre, then that suggests a linear perpendicular distribution of angles. With a linear perpendicular distribution (eg varies by +/- 0 to N units, perpendicular to the path to target, per unit of distance), you would expect a "bunched" angular distribution, i.e. if you sorted all the angles in the data set, the separation between one angle and the next widest would steadily increase.
The Z component is going to complicate things and prevent finding an exact solution. For example, say that the Z (vertical) error/cone and X (lateral) error/cone are not independent of each other. For example, they might be constrained to add up to some constant N that's related to FFA: Z + Y < N. Or in fact, it might be more like (vertical x 4 + horizontal x 1) < N (since we suspect the actual 'bullet group' is an oval, wider than it is high, rather than a circle). A circle is almost the simplest case, but again unless Z and X are totally independent, you're not going to find a close fit for the function without knowing both terms. Hopefully the oval is very 'flat' and so the horizontal-only function will be a good enough approximation of the data to get in the right ballpark. Or even better, maybe the Z component is a totally independent function. Hope so.
Let me make a prediction or a series of predictions then for maximum angle off-target:
  • 0% FFA = 1.0 perpendicular error per unit distance = +/ 0.500, arctan(0.500) = +/- 26.6 degrees
  • 50% FFA = 0.5 perpendicular error per unit distance = +/ 0.250, arctan(0.250) = +/- 14.0 degrees
  • 75% FFA = .25 perpendicular error per unit distance = +/ 0.125, arctan(0.125) = +/- 7.1 degrees
  • in general, horizontal maximum angle off target = +/- arctan(((100-FFA)/100)/2)
Well that's most likely wrong but it's good to have a starting point for testing! Spike 17:40, 15 March 2009 (EDT)

Hey, guess what? First thing on completing a fresh round of tests, I discover all my old logs.

Of course.

Originals (with no roof on the map so the vertical shot data is a bit iffy): 0% - 25% - 50% - 75% - 100%

New data (with a roof, vertical shot data might actually be worth something): 0% - 25% - 50% - 75% - 100%

Comma separated data, one shot per line. First three columns are the absolute tile x/y/z co-ords of where the shot ended up hitting (keeping in mind the shooter is at tile 24/49/2). Next three columns are the x/y/z of where the shot landed, relative to where the shooter is. Final two columns are the important ones, they cover the horizontal and vertical angles the shot took, respectively.

Yes, the wiki doesn't represent it very neatly. Use the edit button to get the line breaks back.

Hrm. Looks like the max angle at 75% comes out to 11.53 degrees... Not 7.1. :(

- Bomb Bloke 09:37, 18 March 2009 (EDT)


BBFiringPointTest6.png

Here is a graph that somewhat better displays the results of my initial 0FA results. The blue line represents the recorded numbers (2550 different values). The red line represents an equal number of results generated by the spreadsheet formula "SIN(RAND()*1.5)*28.64788975*IF(RAND()>0.5;-1;1)".

(Which is, effectively, "The sine of a random number between -1.5 and 1.5 multiplied by half a radian").

Unfortunately the two formulas don't line up (even if you mirror 'em on the diagonal), but they're fairly close. Much closer then the exponential equations I tried graphing, at any rate. I'm thinking I might be able to get something better still with TAN, but I don't seem to be having much luck with that...

- Bomb Bloke 02:18, 15 April 2009 (EDT)

tan eyeballs great if you assume the distribution going in is some flavor of "sum of linear distributions". tan-1(25°)=0.466307658155; halving this and taking the tangent again gets me ~13.1°, which is reached about 1/16th of the way across. I'm assuming some sort of discretization error for the asymmetry. (My initial gut reaction was "sum of two linear distributions", but that isn't correct; that would have landed about 1/8th of the way across. The quick-and-dirty way to fake a bell curve is to sum three linear distributions.) -- Zaimoni 11:52, 15 April 2009

(CDT)

Per geometric check; tan works if the incoming angle is from a sum of three linear distributions. That is: tan(K*rand()*rand()*rand()) should recover the graph up to discretization error, for some K. -- Zaimoni 12:02, 15 April 2009 (CDT)
Is there any chance it's some function of (Kx rand(x), Ky rand(y), Kz rand(z)? Spike 17:53, 15 April 2009 (EDT)
It depends on what you mean...
I was presuming rand() was a zero-parameter function that needed "massaging" to get to any range other than its default. C rand() would be 0..RAND_MAX. XCOM almost certainly has a wrapper that returns values in a range 0..N. Given when XCOM was written, I don't think it has any sort of floating-point RNG. I wrote K to subsume all of the range-massaging that was needed.
That said, a general sum of three linear distributions would have been tan(K1*rand()+K2*rand()+K3*rand()-AVERAGE) [ignoring overflow issues], where AVERAGE is the statistical average expected from K1*rand()+K2*rand()+K3*rand(). The geometric check almost certainly wouldn't pass if the three constants were obviously distinct. The actual expression I'd try retrofitting is tan(K*(rand()+rand()+rand())-AVERAGE). -- Zaimoni 14:07, 16 April 2009 (CDT)
If rand() was to receive any parameter at all, it'd be a seed; but calling the function three times should result in three different numbers anyway (typically you seed the RNG first).

I'd be surprised if even the original game didn't have a floating RNG. Even back on the Spectrums/Commodores/BBCs you could generate within most any range by typing something like RNG*N.

I tried graphing TAN(RAND()*1.5) then TAN(1.5*(RAND()+RAND()+RAND())-2.25), though the first formula wasn't right and the second was worse. :( It's a bit too steep (a little like what you get from 1/x but flopped around). Maybe something a bit higher then 1.5 would sort that out, but I dunno how high you can legally go with TAN? - Bomb Bloke 20:36, 16 April 2009 (EDT)
tan goes unbounded at ±π/2 radians i.e. ±90° Given that the initial bounds appear to be ~±25°, I'd calibrate things so that we were looking at a total span of 50° i.e. ~0.872 radians. In radians, with rand() being a uniform distribution 0..1: TAN(0.872*(rand()+rand()+rand())-0.436) would be my guess.
XCOM 1.2 had some source code comments indicating that the C source targeted Watcom C. That means the "easy rand" available is the C standard library rand(), which without processing returns an integer from 0..RAND_MAX, where RAND_MAX may be as low as 32,767. -- Zaimoni 12:36, 17 April 2009 (CDT)
55 degrees horizontal deviation range corresponds to 440 1/8 degree units; quick-but-lousy-RNG C source for the eighths-of-degrees input to the tangent function at accuracy 0 should be (rand()%147+rand()%147+rand()%147)-219 . This plausibly is from a "deviation output" function that returns 73 at accuracy 0. Corresponding psuedocode:
const int tmp = acc_func(...);
const int rng_scale = 2*tmp+1;
const int rng_bias = 3*tmp;

....

(rand()%rng_scale+rand()%rng_scale+rand()%rng_scale)-rng_bias;
-- Zaimoni 13:58, 26 December 2010 (CST)

Vertical Error vs Horizontal Error

Hmmm.... I don't know about you guys, but I notice that in my games, when firing from Level 0 at enemies on level 0, my soldiers seem like their vertical deviation tends to be very small, even with high inaccuracy. Their horizontal deviation seems much much larger. Is this my imagination?

- Jasonred


You're quite right, they're two different things. There's enough error there to miss at point blank range against a short target, but still nothing like what you get in the horizontal scheme of things.

- Bomb Bloke 08:46, 17 April 2009 (EDT)

Target Size

Units are cylinders, made up of a LOFTEMPS.DAT layer determined by UnitRef[48]/[52], stacked according to their Height.

The full purpose of UnitRef[52] is still in doubt, though it always seems to mirror the value held at [48]. My personal guess is that one is (or at least, was supposed to be) used when bullets come in on one angle, and the other value is used when bullets come in on a perpendicular-ish angle (hence allowing the cylinder to act more like a squished tube, which is closer to the shape of a real person - or it would do if 48/52 didn't always match).

So, yeah, set [48] to 0, all your units get set to an empty LOFTemps record, they cannot be seen/hit by the aliens. Hurrah.

See Also

Pages relevant to this subject: