riverraidrust
riverraidrust copied to clipboard
fixes panics
Fixes #12, fixes #29. With the help of God mode #55 , I was able to let the game crash a couple of times for debugging purposes.
While examining the stack traces of multiple panics I found the following:
The function that crashes is gen_range
, however on some occasions it was called by create_enemy
while on other occasions it was called by create_fuel
.
Both create_enemy
and create_fuel
call rng.gen_range(world.map[0].0..world.map[0].1)
.
In our case this range sometimes is empty which means that the program eventually comes to a state where the left border of the river is greater than (at the right side of) the right border.
Possible scenario of reaching such an unlucky state:
Assume that next_left
border is at position 13 and next_right
border is at position 16 and after
world.next_left = rng.gen_range(world.next_left.saturating_sub(5)..world.next_left + 5);
world.next_right = rng.gen_range(world.next_right - 5..world.next_right + 5);
next_left
is at position 17 (original 13 plus 4) and next_right
is at position 13 (original 16 minus 3).
Now, world.next_right.abs_diff(world.next_left)
will evaluate to 4 so the following block
if world.next_right.abs_diff(world.next_left) < 3 { world.next_right += 3; }
won't be executed.
At the next iterations left
and right
will be adjusted accordingly. So if we are unlucky enough, that state where left
is at the right side of right
can arise and if create_fuel
or create_enemy
call rng.gen_range(world.map[0].0..world.map[0].1)
at this specific time, the program will panic.
How did I approach this:
after the next_right
and next_left
have been calculated, if next_left
is beyond next_right
:
if world.next_left >= world.next_right
then I need to untangle them. However, those cases can arise when we are close to the borders of the screen and thus we must first check whether we have enough space to bring next_right
at the right side of next_left
:
if world.next_left <= world.maxc - 4
(we already know at this point that next_left
is greater than next_right
)
then move next_right
at the right side of next_left
(world.next_right = world.next_left + 3
)
else
(if there is not enough space to bring next_right
over to the right side of next_left
while also having a 3 character gap between them)
move the next_left
at the left side of the next_right
(world.next_left = world.next_right - 3;
)
We know for sure that there is enough space to expand to the left side since there was not enough space to expand at the right side. I did not account for the scenario where there is not enough space on both sides, i.e. very very small terminal width.
Now that they are untangled, the last piece of code:
if world.next_right.abs_diff(world.next_left) < 3 { world.next_right = world.next_left + 3; }
ensures the 3 character distance between them.
This last bit is important because even though we create the gap manually in the previous block, that block might not be executed (case of if world.next_left >= world.next_right
== false), i.e. they were not tangled, just too close.
This way we know that next_left
is at the left side of next_right
while also ensuring they have, at least, a 3 character gap.