Query for "surrounding hex tiles" based on axial coordinates - sql

After reading Red Blob Games' excellent article on heaxgon tile maps and their coordinates.
I am wondering how one would write a SQL-query that returns the tiles surrounding a centered tile up to a range of X. (assuming the "axial coordinates" covered in the article)
A simple idea I first had was
WHERE x BETWEEN tile_x - 1 AND tile_x + 1 AND y BETWEEN tile_y - 1 AND tile_y + 1
But this will return too many tiles, in a way that creates a shape more like a rhombus rather than a circle, which is what I need.
Unfortunately, I haven't found a conclusive answer to this, maybe someone here can give me a hint.
I already thought about some tricks on the sum of the coordinates and wether they are larger of lower than the range, but this won't work with the axial coordinates.

From the diagrams in the linked article, it seems that something like
where (x between tile_x and tile_x + 1) and (y between tile_y - 1 and tile_y + 1)
or (x = tile_x - 1) and (y = tile_y)
should work
If you want to find the tiles (tile_x, tile_y) within a distance n from a given tile (x, y), it will be easier if the x coordinate is modified by adding 0.5 to the x coordinate of each row having an odd distance from the given tile, such that the symmetry is increased:
-1.5 -0.5 0.5 1.5
-2 -1 0 1 2
-2.5 -1.5 -0.5 0.5 1.5 2.5
-3 -2 -1 0 1 2 3
-2.5 -1.5 -0.5 0.5 1.5 2.5
-2 -1 0 1 2
-1.5 -0.5 0.5 1.5
This can be achieved using the expression tile_x + 0.5 * tile_y%2
As the number of tiles within the given distance is reduced by one
from row to row, the limits of (modified) x coordinates in a given
row is n - abs(tile_y - y)/2.
Then the tiles is within a distance n if
abs(tile_y - y) <= n
and abs(tile_x - x + 0.5 * (tile_y-y)%2) <= n - abs(tile_y - y)/2
In sql:
SELECT tile_x, tile_y
FROM tiles
WHERE ABS(tile_y - y) <= n
AND ABS(tile_x - x +0.5*(tile_y-y)%2) + ABS(tile_y - y) / 2 <= n

After some more reading, I found that the article I linked actually alread solves that problem, although somewhat hidden, so here is a more "straight forward" answer.
(Nonetheless, Terje D.'s answer works fine, I just post this because I feel it's a little easier to understand than the "sorcery" of his answer)
In the article from Red Blob Games, he actually describes the formula that calculates the distance between two given hexes:
function hex_distance(Hex(q1, r1), Hex(q2, r2)) {
return (abs(q1 - q2) + abs(r1 - r2)
+ abs(q1 + r1 - q2 - r2)) / 2;
}
I translated this into a function in MySQL
CREATE FUNCTION `rangeBetweenTiles`(tile1q int, tile1r int, tile2q int, tile2r int) RETURNS int(11)
DETERMINISTIC
BEGIN
RETURN (abs(tile1q - tile2q) + abs(tile1r - tile2r)
+ abs(tile1q + tile1r - tile2q - tile2r)) / 2;
END
And use this in another function:
CREATE FUNCTION `isInRange`(tile1q int, tile1r int, tile2q int, tile2r int, `range` Int) RETURNS tinyint(1)
DETERMINISTIC
BEGIN
RETURN rangeBetweenTiles(tile1q, tile1r, tile2q, tile2r) <= `range`;
END
Which can then be used very easily in a select statement:
select *
from tiles
where isInRange(:tile_q, :tile_r, positionQ, positionR, :n)
And it works perfectly fine for any :n

Related

Repeating numbers with modulo -1 to 1 using positive and negative numbers

Repeating numbers with modulo
I know I can "wrap" / loop numbers back onto themselves like 2,3,1,2,3,1,...
by using modulo.
Example code below.
a=[1:8]'
b=mod(a,3)+1
But how can I use modulo to "wrap" numbers back onto themselves from -1 to 1 (-1,-.5,0,.5,1). Some test numbers would be a=[1.1,-2.3,.3,-.5] it would loop around and the values would be between -1 to 1.
I guess a visual example would be bending an x,y plane from -1 to 1 into a torus (how it loops back onto itself).
I was thinking of how a sin wave goes 0,1,0,-1 and back again but I wasn't sure how I could implement it.
PS: I'm using Octave 4.2.2
This can be accomplished by offsetting the value before taking the modulo, then reversing the offset after.
For example, if the target range is [a,b) (the half-open interval such that b is not part of the interval), then one can do:
y = mod( x - a, b - a ) + a;
For example:
a = -1;
b = 1;
x = -10:0.01:10;
y = mod( x - a, b - a ) + a;
plot(x,y)

How to find branching given time and depth for iterative deepening?

My professor posed the following question and I really don't know how to start solving this problem. Any help is really welcomed.
Let the space of the tree be a tree with a uniform branching b (each node has exactly b children). We are exploring the space with iterative deepening, starting with the root of the tree. The program finds the first solution at a depth of 3, in 0.2 seconds, and the next solution at a depth of 5 in 10 seconds. We know that the third solution is at depth 9. Estimate approximately how much time we can expect the program to need in order to find the third solution.
Remember school math and sum of geometric progression.
Tree looks like (example for b=3 children)
N
N N N
N N N N N N N N N
Number of nodes at K top levels is (1 + b + b^2 + b^3... + b^(k-1))
S(k) = (b^k - 1) / (b - 1)
We can see for k=3 and k=5
S(5) / S(3) = 10 / 0.2
(b^5 - 1) / (b^3 - 1) = 10 / 0.2 = 50
Approximation (neglecting -1 term for not so small powers)
b^5 / b^3 = b^2 ~ 50
To find result for k=9
b^9 / b^5 = b^4 ~ 2500
So time is 10*2500 = 25000 seconds ~ 7 hours

Calculate two angles difference (3d game)

I want to record speed of angle movement in a 3D game.
So we have the X axis, where we move from 0-360 with no border, when we are on 359 and move further we hit 0 again.
The game stores the 0-360 in -180-180 instead of 0-360
To calculate the speed I have to record two stages and compare there difference with the time it took, to get the movement speed.
But how do I get the difference.
the difference from 80-120 is = 40 we can just calculate by minusing them.
but the difference from -175 to 175 is = 10, but how do I calculate that? Cause minus them will give -180, but the difference is actually 10.
Simply add 180 to each value and then take the absolute value of the difference.
Dim delta = Math.Abs((180 + final) - (180 + initial))
EDIT: Not sure whether you always want positive values and you want to differentiate between the direction, e.g. if the movement is 270 degrees in one direction is that actually 90 degrees in the other direction. I think that you actually need to define the problem a bit more clearly because it's open to interpretation at the moment.
One approach is to use a little trigonometry. I'm not entirely sure what the VB way of doing this is, so I'll just use pseudocode. If you assume a1=175 and a2=-175, this should work.
θ1 ← a1 * π / 180
θ2 ← a2 * π / 180
δ ← acos( cos(θ1)*cos(θ2) + sin(θ1)*sin(θ2) ) * 180 / π
If you are averse to the use of the trigonometry, you can use some conditionals instead
if a1 < 0 then
θ1 ← 360 - ((-a1) mod 360)
else
θ1 ← a1 mod 360
if a2 < 0 then
θ2 ← 360 - ((-a2) mod 360)
else
θ2 ← a2 mod 360
δ ← ( MAX(θ1, θ2) - MIN(θ1, θ2) ) mod 360
if δ > 180 then
δ ← 360 - δ
Both of these will return δ the smallest angle between the two angles (i.e. it will be in the range [0, 180]). You'll probably get better performance with the second method though there may be some edge cases you also need to check.
angle1 and angle2 are in the range -179...180 and the difference should return the smallest in absolute value among the numbers
(angle2-angle1)+k*360
where k varies over the integers. So to the difference (175-(-175))=350 some other associated candidates are -730, -370, -10, 710. Obviously, the sought after result would be -10.
The range of the difference in the first term in general is -359...359, so to get a result without sign indefiniteness, in a first step add 360+180=540 and compute the now guaranteed positive remainder mod 360
diff = (angle2-angle1+540) mod 360
The inserted 360 cancels under the mod operation, the 180 gives a shift of +180 that must be removed in the final result
diff = diff - 180
is now in the range -180...180 as required.
In the example, this calculates as
diff = (175-(-175)+540) % 360 - 180
= 890 % 360 -180
= 170 - 180
= -10
as required.
The other way around, exchanging 175 and -175,
diff = (-175-175+540) % 360 - 180
= (-350+540) % 360 - 180
= 190 % 360 - 180
= 190 - 180
= 10
I made a solution:
Private Function Calcdif(ByVal firstAngle As Single, ByVal secondAngle As Single) As Single
Dim difference As Single = secondAngle - firstAngle
Select Case difference
Case Is < -180
difference += 360
Case Is > 180
difference -= 360
End Select
If secondAngle = firstAngle Then
Return 0
Else
Return (Math.Abs(difference))
End If
End
End Function

Objective-C: Divide two integers and return a rounded integer value

How can I divide two NSIntegers, for instance, 13 / 4 and round the result to the next integer = 3?
I have seen some samples converting the integers to float and back to integer.
But what is the recommended way with the least amount of code to do it?
Assuming x >= 0 and y > 0:
If you want to round down: x / y
If you want to round up: (x + y - 1) / y
If you want to round to nearest: (x + y / 2) / y

Implementing Wilson Score in SQL

We have a relatively small table that we would like to sort based on rating, using the Wilson interval or a reasonable equivalent. I'm a reasonably smart guy, but my math fu is nowhere near strong enough to understand this:
The above formula, I am told, calculates a score for a positive/negative (thumbs up/thumbs down) voting system. I've never taken a statistics course, and it's been 15 years since I've done any sort of advanced mathematics. I don't have a clue what the little hat that the p is wearing means, or what the backwards Jesus fish beneath z indicates.
I would like to know two things:
Can this formula be altered to accommodate a 5-star rating system? I found this, but the author expresses his doubts as to the accuracy of his formula.
How can this formula be expressed in a SQL function? Note that I do not need to calculate and sort in real-time. The score can be calculated and cached daily.
Am I overlooking something built-in to Microsoft SQL Server?
Instead of trying to manipulate the Wilson's algorithm to do a 5 star rating system. Why don't you look into a different algorithm? This is what imdb uses for their top 250: Bayesian Estimate
As for explaining the math in the Wilson's algorithm, below was posted on the link in your first post. It is written in Ruby.
require 'statistics2'
def ci_lower_bound(pos, n, power)
if n == 0
return 0
end
z = Statistics2.pnormaldist(1-power/2)
phat = 1.0*pos/n
(phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
end
If you'd like another example, here is one in PHP:
http://www.derivante.com/2009/09/01/php-content-rating-confidence/
Edit: It seems that derivante.com is no longer around. You can see the original article on archive.org - https://web.archive.org/web/20121018032822/http://derivante.com/2009/09/01/php-content-rating-confidence/ and I've added the code from the article below.
class Rating
{
public static function ratingAverage($positive, $total, $power = '0.05')
{
if ($total == 0)
return 0;
$z = Rating::pnormaldist(1-$power/2,0,1);
$p = 1.0 * $positive / $total;
$s = ($p + $z*$z/(2*$total) - $z * sqrt(($p*(1-$p)+$z*$z/(4*$total))/$total))/(1+$z*$z/$total);
return $s;
}
public static function pnormaldist($qn)
{
$b = array(
1.570796288, 0.03706987906, -0.8364353589e-3,
-0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5,
-0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8,
0.3657763036e-10, 0.6936233982e-12);
if ($qn < 0.0 || 1.0 < $qn)
return 0.0;
if ($qn == 0.5)
return 0.0;
$w1 = $qn;
if ($qn > 0.5)
$w1 = 1.0 - $w1;
$w3 = - log(4.0 * $w1 * (1.0 - $w1));
$w1 = $b[0];
for ($i = 1;$i <= 10; $i++)
$w1 += $b[$i] * pow($w3,$i);
if ($qn > 0.5)
return sqrt($w1 * $w3);
return - sqrt($w1 * $w3);
}
}
As for doing this in SQL, SQL has all these Math functions already in it's library. If I were you I'd do this in your application though. Make your application update your database every so often (hours? days?) instead of doing this on the fly or your application will become very slow.
Regarding your first question (adjusting the formula to the 5-stars system) I would agree with Paul Creasey.
conversion formula: [3 +/- i stars -> i up/down-votes] (3 stars -> 0)
example: 4 stars -> +1 up-vote, 5 stars -> +2, 1 -> -2 and so on.
I would note though that instead of the lower bound of the interval that both ruby and php functions compute, I would just compute the much more simple wilson midpoint:
(x + (z^2)/2) / (n + z^2)
where:
n = Sum(up_votes) + Sum(|down_votes|)
x = (positive votes)/n = Sum(up_votes) / n
z = 1.96 (fixed value)
Taking Williams link to the php solution http://www.derivante.com/2009/09/01/php-content-rating-confidence/ and making your system such that it just postive and negative (5 stars could be 2 pos, 1 start could be 2 neg perhaps) then it would be fairly easy to convert it to T-SQL, but you'd be much better off doing it in the server side logic.
The author of the first link recently added an SQL implementation to his post.
Here it is:
SELECT widget_id, ((positive + 1.9208) / (positive + negative) -
1.96 * SQRT((positive * negative) / (positive + negative) + 0.9604) /
(positive + negative)) / (1 + 3.8416 / (positive + negative))
AS ci_lower_bound FROM widgets WHERE positive + negative > 0
ORDER BY ci_lower_bound DESC;
Whether this can be accommodated to a 5-star rating system is beyond me too.
I have uploaded an Oracle PL/SQL implementation to https://github.com/mattgrogan/stats_wilson_score
create or replace function stats_wilson_score(
/*****************************************************************************************************************
Author : Matthew Grogan
Website : https://github.com/mattgrogan
Name : stats_wilson_score.sql
Description : Oracle PL/SQL function to return the Wilson Score Interval for the given proportion.
Citation : Wilson E.B. J Am Stat Assoc 1927, 22, 209-212
Example:
select
round(29 / 250, 4) point_estimate,
stats_wilson_score(29, 250, 0.10, 'LCL') lcl,
stats_wilson_score(29, 250, 0.10, 'UCL') ucl
from dual;
******************************************************************************************************************/
x integer, -- Number of successes
m integer, -- Number of trials
alpha number default 0.95, -- Probability of a Type I error
return_value varchar2 default 'LCL' -- LCL = Lower control limit, UCL = upper control limit
)
return number is
z float(10);
phat float(10) := 0.0;
lcl float(10) := 0.0;
ucl float(10) := 0.0;
begin
if m = 0 then
return(0);
end if;
case alpha
when 0.10 then z := 1.644854;
when 0.05 then z := 1.959964;
when 0.01 then z := 2.575829;
else return(null); -- No Z value for this alpha
end case;
phat := x/m;
lcl := (phat + z*z/(2*m) - z * sqrt( (phat * (1-phat) ) / m + z * z / (4 * (m * m)) ) ) / (1 + z * z / m);
ucl := (phat + z*z/(2*m) + z * sqrt((phat*(1-phat)+z*z/(4*m))/m))/(1+z*z/m);
case return_value
when 'LCL' then return(lcl);
when 'UCL' then return(ucl);
else return(null);
end case;
end;
/
grant execute on stats_wilson_score to public;
The Wilson score is actually not a very good of a way of sorting items by rating. It's certainly better than just sorting by mean review score, but it still has a lot of problems. For example, an item with 1 negative review (whose quality is still very uncertain) will be sorted below an item with 10 negative reviews and 1 positive review (which we can be fairly certain is bad quality).
I would recommend using an adaptation of the SteamDB rating formula instead (by Reddit user /u/tornmandate). In addition to being better suited to this sort of thing than the Wilson score (for reasons that are explained in the linked article), it can also be adapted to a 5-star rating system much more easily than Wilson.
Original SteamDB formula:
( Total Reviews = Positive Reviews + Negative Reviews )
( Review Score = frac{Positive Reviews}{Total Reviews} )
( Rating = Review Score - (Review Score - 0.5)*2^{-log_{10}(Total Reviews + 1)} )
5-star version (note the change from 0.5 (a 50% score with up/down votes) to 2.5 (a 50% score with 5-star ratings)):
( Total Reviews = total count of all reviews )
( Review Score = mean star rating of all reviews )
( Rating = Review Score - (Review Score - 2.5)*2^{-log_{10}(Total Reviews + 1)} )
The formula is also much more understandable by non-mathematicians and easy to translate into code.