Introduction
In many games, especially RPGs, stats are very important. Attack, defense, resistance, damage, penetration of armor, slips, etc. affect the damage caused to the enemy or you receive from the enemy. Most often, players prefer to stick to tactics - “the more and more immediately, the better.” Such an approach is most likely caused not by a well-thought-out character development strategy, but by the lack of a detailed analysis of the game, laziness, or lack of information about the specific nature (specific calculation formula) of the influence of the “stats” on certain indicators. Moreover, very often, according to the idea of ​​the creators of the game, it is impossible to increase all the characteristics at the same time, and therefore choosing the right “what and where to“ pump in ”becomes especially important.
Next, we will discuss a method that in some cases will allow us to obtain an explicit formula for the dependence of some parameters on others (for example, the power of spells on intelligence, or the percentage reduction in damage received on protection). This method is applicable where we have the ability to change one parameter and observe the changes that depend on it, the second one. Moreover, this method is applicable in the case when the average value of the second parameter strictly depends on the first, but the second parameter itself is a random variable.
The method will be described by the example of calculating the power of pet spells from the intellect, and the percentage of damage the player takes from the total amount of protection in the game ArcheAge. Actually, the basis of the method is the “Method of Least Squares”, which is very widely known and very often used in various fields. Wolfram Mathematica (any version) will be used for calculations. Actually, a step-by-step description of what needs to be done to obtain the law in question is the main value of this article. Those familiar with the OLS and Wolfram Mathematica can go directly to examples.
')
Least Squares (OLS)
The MNC method is described in great detail in the literature; I will only describe the essence in general terms. Let we know the general form of the dependence of one quantity on another. From where you can learn the general view, I will explain later. At the moment, for example, we take the dependence of the form y = a * x ^ 2 + b * x + c. Where y is one value, and x is another. In this case, a, b, c are some parameters. And in order to completely surpass the dependence of one quantity on another, it is necessary to determine these parameters, because the type of dependence itself is assumed to be known.
In the simplest case, one can find out the value of one quantity for a particular value of another quantity from observations, experiments, or some other sources. You need three such pairs to create a complete system of equations and solve it with respect to the three unknown parameters a, b, c. Moreover, in some cases this is enough in games.
Everything gets complicated when one more term is added to the dependency function - namely, some random variable. y = a * x ^ 2 + b * x + c + Random_Valichina. It can be introduced by game developers specifically, such as the variation in damage, but it may have another reason. The fact is that the exact value of a certain function may have in its record the number of digits larger than the size of the output field in the game menu. In this case, a rounded value is displayed, and one cannot say the exact value is greater or less than what we can read in the game interface field. Thus, we can say that rounding adds some random variable to us (it can be both negative and positive, but on average it is zero).
When a random variable is added to the “exact” dependence function y = a * x ^ 2 + b * x + c, then the actually observed, measured values ​​of y will not lie on the curve a * x ^ 2 + b * x + c, whatever there were parameters. If neither the dispersion is too large (the mean value of the spread), the random values, the actually observed values ​​marked on the coordinate plane will lie quite “close to the curve a * x ^ 2 + b * x, if we even know the parameters ab and c. Some points can even get on this curve, because it is quite possible that the random variable at some point simply assumed the value of zero. But how then can we find the parameters of our function if even the points that we know do not lie on it? MNC is to choose the parameters so that the distance from the points to the curve is minimal! This is the main essence of the MNC.
Here it is worth clarifying that the distance from the point to the curve means not the distance to the nearest point of the curve, but the difference between the value of the point (the point is a variable pair - the observed value of the function) and the “exact” value of the function for the same value as the variable . Generally speaking, in the ideal case, this difference is equal to the specific value of the very Random_Valichina at a given point. It is also necessary to clarify what is needed so that the sum of all the differences between the observed values ​​and the “exact” values ​​should be minimal. Unfortunately, there is often a temptation to throw out the most “inconvenient” points that lag far behind the supposed “exact” curve so that everything else looks better. This can not be done, unless of course you are absolutely not sure that the measurement was taken erroneously. And one more important point - the distance in this case is measured as a strictly positive value. It doesn't matter if the observed point is higher than necessary or lower - the main thing is how far.
General view of the dependence, as mentioned earlier y = a * x ^ 2 + b * x + c + Random_Valichina. Moreover, from observations we know the observed value of pairs of both x, y. We can measure steam as much as we want, and preferably the more the better! (why so, read the literature). To find the difference between the observed and the “exact” value, one should subtract the measured values ​​of the y_measure values ​​and a * x_measure ^ 2 + b * x_measure + c. That is, we believe that the variable is known precisely for us, and that with the exact parameters abc a * x_size ^ 2 + b * x_size + c is like the “exact” value of our function. From the resulting difference, you need to take the module to get the absolute value as already mentioned. (It is worth noting that, in fact, “accurate” means a curve that is closest to all points. Actually, we cannot calculate the exact value. But for want of Actually, it remains to be content with what can be calculated.
If you are not used to this turn of events, then you should get used to it. When there is no ideal, we use the best of what we have.) But it is inconvenient to work with the module, and it is simpler to square the resulting difference instead of the module. And sum up all the squares of differences. The resulting sum will be one very long function with many terms, but only three variables. It remains only to find the values ​​of abc for which this function will be minimal. Which is equivalent to finding the extrema of a function of several variables. Which in turn (at first) glance is a trivial task of the analysis mat. This is the least squares method.
The apparent simplicity of the method plays a pretty bad trick when you need to write an algorithm for computing. This is really quite a challenge, with a lot of pitfalls. However, there are already implemented algorithms, which we will use, due to the fact that our goal is to use the OLS method, and not to write the next algorithm for its implementation.
Wolfram mathematica
If you have never worked with programs like Mathematica MATLAB and Maple, then it's time to start. If it’s very simple (so that you don’t be afraid to master them), then these programs are needed to solve systems of differential equations symbolically, or integrate symbolically, or draw a graph, and all this is done by a “polite request” in one line. Just think, you write the equation, in a symbolic form (NOT NUMBER, all with letters, variables, parameters) and you get the same answer - in a general symbolic form. Finding parameters using the ANC method is also possible. (... and not only them, but these are details). I advise you to try to play with Wolfram Mathematica. Maybe someone will be more interesting if you find out the fact that from Mathematica there is access to databases of social networks, such as VKontakte. (It is clear that only open data, but still) You can do some research yourself, using data from real people. Their tastes, professions, interests, frequency of posts, and everything you want regarding sociology and people's behavior. There are a lot of articles on how to work with Mathematica, but what is especially nice is the enormous number of examples in the built-in help - literally for all occasions. This greatly simplifies the mastering of Mathematica (I will not respond so flatteringly about MATLAB: he, of course, has some advantages, but still my choice is for Mathematica).
A few words about those functions that will be applied below. To find the parameters of a function of a known type, in the presence of a set of observable (measured) data (to which a certain random value is added), the FindFit function is used. To display an array of points, use the ListPlot function. For plotting, simply Plot is used. Arrays are usually enclosed in curly brackets {}, access to an array element is done through double square brackets [[]]. You can also use various functions to create an array, for example Table. To display a graph and an array of points in one figure, the Show [{}] function from an array of two elements (or more) can be used, each of which can be any function for graphical display of data.
Examples
In most cases, games do not use too complex functions for the dependencies of some parameters on others. The most commonly used linear functions are ax + b, or a relation of the form (ax + b) / (cx + d). There is no clear rule for searching functions. The developer can, if desired, make a very complex and confusing function, which is almost impossible to guess. However, such cases are extremely rare. The ratio of the form (ax + b) / (cx + d) is often used where the value to be limited from above is calculated, for example, the reduction of the damage received from the protection value. Indeed, it makes no sense to introduce such a thing as damage reduction of more than 100%. In such cases, when there are values ​​bounded above, it is best to start trying exactly with a function of the form y = (ax + b) / (cx + d).
Let's try to find the dependence of the percentage of reduction of damage received on the value of protection. For this we need to get a pair of numbers (protection, percentage). This is done quite simply. All items are removed from the character. And then in turn, in different quantities, in different combinations are put on back. Thus, we vary the value of protection, and watch how the percentage changes. We write the results into an array, in this form.
OurDefSource = {{637, 10.73}, {689, 11.5}, {462, 8.02}, {585, 9.94}, {358, 6.33}, {317, 5.64}, {281, 5.03}, {99, 1.83} , {0, 0}, {3668, 40.9}, {1287, 19.54}, {495, 8.54}, {2471, 31.8}, {4596, 46.44}};
After that, for the convenience of further graphical representation of points, it will be useful for us to find the minimum and maximum protection values. And also sort our data. Transpose from an array of two dimensional arrays makes an array of two one-dimensional arrays, to then calculate the minimum and maximum protection values.
Def = Sort [OurDef, # 1 [[1]] <# 2 [[1]] &];
MaxDef = Last [Transpose [Def] [[1]]];
MinDef = First [Transpose [Def] [[1]]];
Then we set the general view of our function, and create an array of all the parameters that need to be found.
Fdef [x _]: = (a * x + b) / (c * x + d);
CoefsFdef = {a, b, c, d};
Then we use FindFit to search for parameters of a function of a certain type, using the data obtained experimentally.
CoefsFdefFit = FindFit [Def, Fdef [x], CoefsFdef, x]
After that, we show the form of the function with the parameters found (for this, “/.” Is used)
Fdef [x] /. CoefsFdefFit
As a result, we get: (-1.1499 + 7.32728 x) / (388.234 + 0.0732995 x)
I must say, the view is not very beautiful. And I’ll say right away that not very nice coefficients are used very rarely, due to the fact that it’s much easier for developers to work with some compact, visual coefficients. But the result we received is a fraction, which we can reduce, something to put out for brackets. We will lead to a more beautiful view. If you look closely, you can see that the coefficients in front of x in the numerator and denominator are the same. Divide the coefficient of x in the numerator of both parts of the fraction.
(-0.156933+ x) / (52.9848_ + 0.0100036 x)
As we remember, the variable x means the amount of protection in units. Typical values ​​for x are on the order of thousands. Therefore, the numerical constant in the numerator can be neglected, which means that the general form of the function is actually somewhat different than we assumed, namely, without a constant in the numerator. As for the denominator, the multiplier in front of x is very similar to 0.01. The term in the denominator is very similar to 53.00. Having made all these assumptions, and also multiplying the denominator by 100, we find that the percentage of damage taken is 100 * x / (5300 + x), where - x is the total amount of protection.
To check how "beautiful" the formula corresponds to reality, we find the difference between the experimental points and the values ​​of our function.
OurDiff = Fgood [x _]: = 100 * x / (5300 + x);
OurDiff = Table [Fgood [Def [[i]] [[1]]] - Def [[i]] [[2]], {i, 1, Length [Def]}]
Max [OurDiff]
The maximum difference value is 0.00493997, which is less than half of the last significant (displayed) figure in the percent value. Which is quite satisfactory.
The result can be displayed on the chart.
Show [{ListPlot [Def, PlotStyle -> {Blue}], Plot [Fgood [x], {x, MinDef, MaxDef}, PlotStyle -> {Green}]}]
Full code for Mathematica:
OurDefSource = {{637, 10.73}, {689, 11.5}, {462, 8.02}, {585, 9.94}, {358, 6.33}, {317, 5.64}, {281, 5.03}, {99, 1.83}, {0, 0}, {3668, 40.9}, {1287, 19.54}, {495, 8.54}, {2471, 31.8}, {4596, 46.44}}; Def = Sort[OurDefSource, #1[[1]] < #2[[1]] &]; MaxDef = Last[Transpose[Def][[1]]]; MinDef = First[Transpose[Def][[1]]]; Fdef[x_] := (a*x + b)/(c*x + d); CoefsFdef = {a, b, c, d}; CoefsFdefFit = FindFit[Def, Fdef[x], CoefsFdef, x] Fdef[x] /. CoefsFdefFit OurDiff = Fgood[x_] := 100*x/(5300 + x); OurDiff = Table[Fgood[Def[[i]][[1]]] - Def[[i]][[2]], {i, 1, Length[Def]}] Max[OurDiff] Show[{ListPlot[Def, PlotStyle -> {Blue}], Plot[Fgood[x], {x, MinDef, MaxDef}, PlotStyle -> {Green}]}]
A similar approach also works successfully in calculating the formula for calculating the damage of a spell Poisonous breath of a pet. The turned witch from the Inta bonus from gear. In this example, the general form of the function will be somewhat different. If we assume that the formula for the pet will be similar to the formula for the character, then the inta is first converted to the power of spells, then something else is added to this power (from gear or buffs), after which the resulting spell power is converted by some formula to damage from a particular spell. And often the damage from spells has a constant part, which does not depend on the power of spells. Thus, the formula is supposed to be
a51 + b51 * (c51 * x + y)
where a51 is a constant in the calculation of a specific sang; b51 is a multiplier in the formula sang; c51 is the coefficient of proportionality between spell power and inta; x is inta and y is a bonus boost to spell power from gear. FindFit is able to work for a function of several variables. As a result, we get:
Damage to the spell Toxic Breath = 1669 + 4.8 * (1.25 * Inta + Bonus Power);
It is worth noting that, in contrast to the character, the coefficient of proportionality of the spell power and inta is not 0.2 and 1.25, which means that the spell power from the spells of pets grows almost 6 times faster.
For comparison:
Lesser Hurricane 654 + 1.92 * (1.25 * x + y);
“Little Arrow” 980 + 2.88 * (1.25 * x + y);
findings
Thus, using a fairly simple method, you can get information about the laws and principles of the game. Along the way, as was shown, we learned not only the dependence of one parameter on another, but also noticed something that we were not even looking for, like the coefficient c51. Now, knowing the exact formula for the calculation, you can analyze and model which parameters and how to improve. Thus, the character development strategy becomes more meaningful.
This method does not apply to hacking the code, nor to anything else illegal. But agree, this method allows you to see what is not clearly shown for all the others.
Conclusion
Matan helps even in games. I hope the described example of the use of MNCs in games will increase your interest in matan.