2D visibility algorithm not works well for overlapped case - javascript

I find the visibility algorithm in one great website
Since it is quiet long ago article, I am not sure that the author will give the answer.
As you can see, the interactive playground(try it yourself) does not work well in right upper side, especially overlapping edges.
// Pseudocode of main algorithm
var endpoints; # list of endpoints, sorted by angle
var open = []; # list of walls the sweep line intersects
loop over endpoints:
remember which wall is nearest
add any walls that BEGIN at this endpoint to 'walls'
remove any walls that END at this endpoint from 'walls'
figure out which wall is now nearest
if the nearest wall changed:
fill the current triangle and begin a new one
Here is also part of working JavaScript code
// Main algorithm with working Javascript code
export const calculateVisibility = (origin, endpoints) => {
let openSegments = [];
let output = [];
let beginAngle = 0;
endpoints.sort(endpointCompare);
for(let pass = 0; pass < 2; pass += 1) {
for (let i = 0; i < endpoints.length; i += 1) {
let endpoint = endpoints[i];
let openSegment = openSegments[0];
if (endpoint.beginsSegment) {
let index = 0
let segment = openSegments[index];
while (segment && segmentInFrontOf(endpoint.segment, segment, origin)) {
index += 1;
segment = openSegments[index]
}
if (!segment) {
openSegments.push(endpoint.segment);
} else {
openSegments.splice(index, 0, endpoint.segment);
}
} else {
let index = openSegments.indexOf(endpoint.segment)
if (index > -1) openSegments.splice(index, 1);
}
if (openSegment !== openSegments[0]) {
if (pass === 1) {
let trianglePoints = getTrianglePoints(origin, beginAngle, endpoint.angle, openSegment);
output.push(trianglePoints);
}
beginAngle = endpoint.angle;
}
}
}
return output;
};
In my opinion, it happens since the algorithm thinks right edge is shown whole part when it is the closest edge although it can be hidden from whole edges.
I think I can modify the algorithm to use the current closest edge and newer current closest edge but not sure...
How should I fix it?

Related

Is there an efficient in-place algorithm to separate an array into left and right by a given condition?

It's pretty common to want to separate an array into a pair of arrays, one side pass and another side fail. The basic algorithm goes something like this:
function partition(list, cond) {
let left = [];
let right = [];
for (let item of list) {
if (cond(item)) {
left.push(item);
} else {
right.push(item);
}
}
return [left, right];
}
In my case, that condition is a bit more specialized - it's just an equal-length bitset where for each position, set = left, unset = right:
function partition(list, bitset) {
let left = [];
let right = [];
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
left.push(list[i]);
} else {
right.push(list[i]);
}
}
return [left, right];
}
Thing is, I want to do this in-place and just have those where the corresponding bit in the bitset is true to the left, those where it's false to the right in the same array. (The entries do need to retain their order - it's important. Otherwise, it'd be pretty obvious.) This is the most efficient version I've come up with so far:
// The return value is the start offset for the rejects
function partition(list, cond) {
let right = [];
let rightStart = 0;
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
list[rightStart++] = list[i];
} else {
right.push(list[i]);
}
}
for (let i = 0, j = rightStart; i < list.length; i++) {
list[j] = right[i];
}
return right_start;
}
Is it possible to do this with only O(1) space while still keeping it O(n) time, and if so, what would that algorithm look like?
The paper ...
Katajainen, J., Pasanen, T. Stable minimum space partitioning in linear time. BIT 32, 580–585 (1992).
https://doi.org/10.1007/BF01994842
... presents an algorithm for stable in-place partitioning in O(n) time and O(1) extra space.
The algorithm you are looking for is known as stable partition. In constant space it has O(n log n) time complexity. See for example here (it talks C++, but in this context it doesn't matter).
An implementation may look along the lines of
stable_partition(begin, end, predicate)
if (end - begin == 0)
return begin
if (end - begin == 1)
return predicate(*begin)? begin: end
mid = begin + (end - begin)/2
left_partition_point = stable_partition(begin, mid, predicate)
right_partition_point = stable_partition(mid, end, predicate)
return rotate(left_partition_point, mid, right_partition_point)
where rotate returns a position at which the leftmost element landed.

Javascript Snake Game (food spawn location)

Assume I have a "grid" of 20ish by 20ish. The way that I randomly spawn food is with the following formula:
Math.floor(Math.random() * ((max - min) / 20) + 1) + 20 * min;
This basically ensures that food will spawn neatly within one of the grid cells.
I want to avoid spawning food on top of the snake's body. I haven't found the best way to do this while maintaining distributed randomness.
Solutions that don't quite work:
I've seen some people do this but it doesn't work because it doesn't guarantee that food will never spawn on the snake, it just makes it less likely. getRandomPoint() might still get me a random point on the snake.
for (let i = 0; i < snake.length; i++) {
if (foodPosition.x === snake[i].x && foodPosition.y === snake[i].y) {
foodPosition.x = getRandomPoint();
foodPosition.y = getRandomPoint();
}
}
This next one doesn't work either because food can still spawn on any cell of the snake that hasn't been checked yet. For example, when i = 0, it only checks if foodPosition.x and foodPosition.y are equal to snake[0].x and snake[0].y. On this iteration of the loop when i = 0, food can still spawn on the cell occupied by snake[2].x, snake[2].y for instance (I think?).
for (let i = 0; i < snake.length; i++) {
while (foodPosition.x === snake[i].x && foodPosition.y === snake[i].y) {
foodPosition.x = getRandomPoint();
foodPosition.y = getRandomPoint();
}
}
There are some other things I've tried but I'll leave it at that. What is a good way to do the kind of thing I'm trying to do?
I would generate a list of possible locations where the food is allowed to spawn. Something like the set of all locations minus the set of snake body locations.
I've done something similar with Minesweeper in JavaScript and p5.js. Two mines cannot occupy the same cell. As I generated mines, I removed their location from the list of possible locations.
let numRows = 20;
let numColumns = 20;
let possibleFoodLocations = [];
for (let row = 0; row < numRows; row++) {
for (let column = 0; column < numColumns; column++) {
let isLocationValid = snake.every(bodyPart => {
return row !== bodyPart.y || column !== bodyPart.x;
});
if (isLocationValid) {
possibleFoodLocations.push({ x: column, y: row });
}
}
}
Note that this will take numRows * numColumns * snake.length * 2 operations, so it will become inefficient to repeatedly perform when any of those values are large. The best method, I think, would be a combination of this when these values are large, and choosing a random point when these values are small.
I'd add a flag in your solution and then it will work
let colliding = true;
let new_x, new_y;
while(colliding) {
colliding = false;
new_y = getRandomPoint();
new_x = getRandomPoint();
for (let box of snake) {
if (new_x == box.x && new_y == box.y) {
colliding = true;
break;
}
}
}
foodPosition.x = new_x;
foodPosition.y = new_y;
Would it work for you to get your random point and then check to make sure it doesn't match any of the snake's points? If it does, you could just get another random point.

quickselect into JavaScript

I have scoured the internet for all the various versions of quicksort implementation to translate into JavaScript and many of them do not successfully port.
I haven't been able to figure out if this is due to me not knowing a nuance about Java or C++, or the examples that people posted are broken.
I am not optimizing for performance, but how readable and logical it is to me.
I have arrived at this implementation, but I noticed that it does not work.
Outputs are random (likely due to the Math.random()), but as I follow the algo, I get frustrated with this following test case.
Outputs range from 999, 3, 100, 2, and 1000. I cannot follow the logic and would really appreciate someone explaining what's happening to give such erratic results.
function swap(array, idxA, idxB) {
var temp = array[idxA]
array[idxA] = array[idxB]
array[idxB] = temp
}
function partitionStart(arr, left, right, pivotIdx) {
var storeIdx = left, pivotVal = arr[pivotIdx];
swap(arr, pivotIdx, right)
for (var i = left; i <right; i++) {
if (arr[i] < pivotVal) {
swap(arr, storeIdx, i)
storeIdx++
}
}
swap(arr, pivotIdx, right);
return storeIdx;
}
function quickSelectLoop(arr, k) {
var pivotIndex, pivotNewIdx, pivotDist,
left = 0, right = arr.length-1
while(true) {
pivotIndex = Math.floor(Math.random()*arr.length)
pivotNewIdx = partitionStart(arr, left, right, pivotIndex)
pivotDist = pivotNewIdx - left
if (pivotDist === k) {
return arr[pivotNewIdx-1]
} else if (k < pivotDist) {
right = pivotNewIdx -1
} else {
k -= pivotDist+1
left = pivotNewIdx + 1
}
}
}
var test2 = [1000,999,1,2,3,100,105]
console.log(quickSelectLoop(test2, 4))
expected output from quickSelect(test2, 4) => 100 since 100 is the 4th smallest element in the collection
Your current implementation has multiple flaws. I don't really understand what is the idea of your current code, so I'll try to explain how I understood your code, and then provide a corrected one.
partitionStart - partitions part of array from left to right indices using item at pivotIdx as parts separator. Returns index of separation sepIdx, such that every item before sepIdx is less than pivot item, and every item after it is greater or equal to it.
quickSelectLoop - selects k-th smallest item from the given array.
Function relies on invariant that all items between left and right, while being in arbitrary order, are array's left..right smallest items, e.g.
if left = 0, right = 2, initial array = {0,1,2,3,4}, then
arr = [A,B,C,x,x], where {A,B,C} = {0,1,2}, so arr = [2,1,0,4,3] and arr = [0,1,2,3,4] are both correct.
Corrected code with commentaries:
function partitionStart(arr, left, right) {
// You were passing pivotIdx here, I think that selecting pivotIdx
// should be this method's responsibility, so I moved the code here
// Also you were taking pivotIdx ignoring left and right - fixed that
var pivotIdx = Math.floor(Math.random() * (right - left + 1)) + left;
var storeIdx = left, pivotVal = arr[pivotIdx]
// You had a swap of pivot with the right here,
// which allowed you to traverse 1 item less in a cycle,
// but with the cost of one line of code - removed that
for (var i = left; i <= right; i++) {
if (arr[i] < pivotVal) {
swap(arr, storeIdx, i)
storeIdx++
}
}
// Here was a strange swap of pivot back from right to its position,
// now it is not needed.
return storeIdx;
}
function quickSelectLoop(arr, k) {
var pivotDist;
var left = 0, right = arr.length - 1;
while(right !== left) {
// Maintaining: left <= k <= right, while enclosing left to right
pivotDist = partitionStart(arr, left, right)
// You were returning arr[k] here if pivotDist == k,
// but that is incorrect considering function's invariant -
// we can't make such a conclusion unless left == right.
// I corrected that check - it is now located in the while loop.
if (k < pivotDist) {
right = pivotDist - 1;
} else {
// You were adding 1 here, which is incorrect,
// because item at pivotDist may be the answer as well.
left = pivotDist;
}
}
// left == right, and we maintained 'left <= k <= right', so we have an answer
return arr[k]
}
jsfiddle

shuffling cards with Javascript and inconsistent array values?

I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.
The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}
while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.

Optimize looping through 2 arrays javascript canvas game

I'm working on my first javascript canvas game, and I wonder is there a better way for comparing collisons between objects in 2 arrays. For example i have an array with rockets, and array with enemies, the code is working, but i think when arrays length becomes much larger it will have effect on the performance. Example 100 rockets through 100 enemies is 10000 iterations per frame
for (i in rockets){
rockets[i].x+=projectile_speed;
for (j in enemies){
if(collision(rockets[i], enemies[j])){
enemies[j].health-=5;
sound_hit[hit_counter-1].play();
hit_counter--;
if (hit_counter==0){
hit_counter=5;
}
rockets.splice(i,1);
if (enemies[j].health <= 0) {
score += enemies[j].score;
sound_explode[Math.floor(Math.random()*25)].play();
enemies[j].isDead = true;
}
} else if(rockets[i].x >= width){
rockets.splice(i,1);
}
}
}
If you want to test every rocket on every player its not really possible to do differently, without knowing more about position of players and rockets.
If you keep the collision function fast, this should though be no problem at all.
I can only think of two easy improvements on this:
when a collision is found use continue since looping over the rest of players should not be necessary (unless players is allowed to collide)
instead of splice'ing the rockets array multiple times, build a new one, excluding all "dead" rockets.
You should also consider using forEach, map and filter to make the code a bit easier to read:
rockets = rockets.filter(function(rocket) {
rocket.x+=projectile_speed;
if(rocket.x >= width) {
return false;
}
var enemy = enemies.find(function(enemy) { return collision(rocket, enemy) });
if(enemy) {
enemy.health-=5;
sound_hit[--hit_counter].play();
if (hit_counter==0){
hit_counter=5;
}
if (enemy.health <= 0) {
score += enemy.score;
sound_explode[Math.floor(Math.random()*25)].play();
enemy.isDead = true;
}
return false;
}
return true;
});
What you could try to do is to reduce the number of tests by grouping the enemies and rockets, so that you only have to test the elements in the same group.
Here is a simple implementation to show what I mean, this only partitions in X-direction, because your rockets only seem to travel horizontally:
var groupWidth = 100; // do some experiments to find a suitable value
var rocketGroups = [];
var enemyGroups = [];
// initalize groups, not shown (array of array of rocket/enemy),
// but here are some other function examples...
function addToGroups(element, groups) {
groups[element.x / groupWidth].push(element);
}
function move(element, groups, distance) {
if (element.x / groupWidth != (element.x + distance) / groupWidth) {
// remove element from the old group and put it in the new one
}
element.x += distance;
}
// Note: this is only to show the idea, see comments about length
function checkCollisions() {
var i,j,k;
for (i = 0; i < rocketGroups.length; i++) {
for (j = 0; j < rocketGroups[i].length; j++) {
for (k = 0; k < enemyGroups[i].length; k++) {
// only compares elements in the same group
checkPossibleCollision(rocketGroups[i], enemyGroups[i], j, k);
}
}
}
}

Categories