Maximize the efficiency of a nested loop when iterating through units on a Cartesian plane

In a virtual game world, there exists a multitude of unit objects stored in an array.

These units are positioned on a 2D map with x and y coordinates.

let units = [
    {id: 1, x=3450, y = 1456},
    {id: 2, x=5560, y = 2423},
    {id: 3, x=1321, y = 3451}
]

Every second, the game tasks each unit with compiling a list of other units within a specified distance for potential interactions like combat or evasion.

As the number of units grows into the thousands, the current method of each unit checking distances with every other unit becomes increasingly inefficient due to the exponential increase in tests required.

After researching similar issues online, an attempt was made to group units into row/column cells and only conduct distance tests on potentially relevant units. However, it was discovered that organizing these groups took longer than expected and did not provide significant benefits.

A testable version of the current code is provided below, taking approximately one second to complete on a standard browser. Suggestions for optimizations are welcomed to enhance performance significantly.

//create the world
let mapWidth = 5000;
let mapHeight = 2000;
let releventDistance = 200;
let unitCount = 5000;

//function to create a new unit at a random position on the map
function newUnit(id){
    let newUnit = {};
    newUnit.id = id;
    newUnit.x = Math.floor(Math.random() * mapWidth);
    newUnit.y = Math.floor(Math.random() * mapHeight);
    //array of 'relevant' neighbors represents other units close enough for interaction
    newUnit.neighbours = [];
    return newUnit;
}

//simple distance calculation
function distance(unit1, unit2){
    let dx = unit1.x - unit2.x;
    let dy = unit1.y - unit2.y;
    return Math.sqrt(dx * dx + dy * dy);    
}

//array to store units
var myUnits = [];

//populate the unit array
for (let i = 0; i < unitCount; i++){
  myUnits.push(newUnit(i));
}
console.log(unitCount + " units created");

//perform full scan using nested loops
let timeStamp1 = new Date();
myUnits.forEach(unit => {
    myUnits.forEach(unit2 => {
        //avoid testing a unit against itself
        if(unit.id != unit2.id){
            let unitDist = distance(unit, unit2);
            if (unitDist <= relevantDistance){
               unit.neighbours.push({unit : unit2, distance : unitDist});
            }
        }
    })
})

//print results
console.log((new Date() - timeStamp1) + "ms: to complete bruteforce fullscan");

//calculate average number of neighbors
let totalNeighbourCount = 0;
myUnits.forEach(myUnit => {totalNeighbourCount += myUnit.neighbours.length});
console.log(Math.floor(totalNeighbourCount / myUnits.length) + ": average number of neighbours");

Answer №1

To optimize the process, consider iterating only from the index plus one for the inner loop to avoid revisiting already visited pairs.

This strategy involves adding the pair to each neighboring unit.

//initialize the world
let mapWidth = 5000;
let mapHeight = 2000;
let relevantDistance = 200;
let unitCount = 5000;

//function to create a new unit at a random position on the map
function newUnit(id){
    let newUnit = {};
    newUnit.id = id;
    newUnit.x = Math.floor(Math.random()*mapWidth);
    newUnit.y = Math.floor(Math.random()*mapHeight);
    //list of 'relevant' neighbors; other units close enough to interact with
    newUnit.neighbours = [];
    return newUnit;
}

//simple distance calculation function
function distance (unit1, unit2){
    let dx = unit1.x - unit2.x;
    let dy = unit1.y - unit2.y;
    return Math.sqrt(dx * dx + dy * dy);    
}

//array to store units
var myUnits = [];

//generate units
for (let i =0; i<unitCount; i++){
    myUnits.push(newUnit(i));
}
console.log(unitCount + " units created");

let timeStamp1 = new Date();

for (let i = 0, l1 = myUnits.length - 1; i < l1; i++) {
    const unit = myUnits[i];
    for (let j = i + 1, l2 = myUnits.length; j < l2; j++) {
        const unit2 = myUnits[j];
        let unitDist  = distance(unit, unit2);
        if (unitDist <= relevantDistance) {
            unit2.neighbours.push({ unit: unit, distance: unitDist });
            unit.neighbours.push({ unit: unit2, distance: unitDist });
        }
    }
}

//output results
console.log((new Date() - timeStamp1) + "ms: time taken for full scan");

//calculate average number of neighbors and display
let totalNeighbourCount = 0;
myUnits.forEach(myUnit => {totalNeighbourCount += myUnit.neighbours.length});
console.log(Math.floor(totalNeighbourCount/myUnits.length) + ": average number of neighbours");

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering the "Not all code paths return a value" TypeScript error when attempting to manipulate a response before returning it, however, returning the response directly works without any issues

Encountering an issue where manipulating/process response and return triggers an error in TypeScript with the message "Not all code paths return a value.". Data is fetched from a backend API using RxJS lastValueFrom operator, along with lodash functions as ...

Use JavaScript or jQuery to implement the position absolute styling

I am currently working on a website where the navigation is aligned to the right side. However, I am facing an issue where the last menu item dropdown extends beyond the page because it is absolutely positioned to the left of its parent element. I am act ...

Is it acceptable to use std::map<std::set<long>, double> as well as std:map< std::pair<long, long>, double> as data types in C++?

Within the realm of std::map, we find a key and its corresponding mapped value. In the specific Data Type I am referring to, the key would be either std::set<long> or std::pair<long, long>. Is it valid to consider that in maps, values are orga ...

Tips for overlapping children in a column flex direction while maintaining the parents' positioning

Currently, I am working with two parent flex items, arranged with flex-direction: column. Within the first parent, there are two children. However, one of the children is optional and may be removed at times. I aim to have the optional child displayed on ...

Execute the jQuery function .hover() only when the user is actively hovering over the

Although I have experience with jQuery, I am encountering a slight issue. I want the function to trigger only when I hover over the element without it being activated if I slightly move the mouse while already hovering over it. In simple terms, I would li ...

How can a modal component be displayed in Nuxt3/Vue3 when it is triggered from the header component?

Currently, I am facing a challenge in calling the modal based on my header component while using nuxt3/vue3. I attempted to place the modal inside the header component and utilize the Teleport function to teleport the modal into the body based on the value ...

What is the best location to initialize an array of Class in Android Studio?

I keep encountering this error message when attempting to run my code. (I've excluded most of the other lines for clarity) 03-26 22:23:51.800 2425-2425/? E/RCPManagerService: PackageReceiver onReceive() Failed to load meta-data, NullPointer: null 03 ...

The toggleCategories function seems to be malfunctioning as it is only showing the sequence number as 0 in ReactJS

I am currently working on a portfolio using the React framework. One of the features I have implemented is a project page where multiple projects are displayed within tabs. However, I am facing some issues with the functionality. toggleCategories(){ ...

Tips for implementing a loop within the file input change event

Hello everyone, I need assistance with the code below. function fileValidation() { var fileInput = document.getElementById('filech'); var filePath = fileInput.value; var allowedExtensions = /(\.jpg|\.jpeg|\.png|\.gif)$ ...

Minimal amount of code needed to achieve this with jQuery

Typically, I specialize in backend development and don't have much experience with javascript and jQuery. Currently, I am working on a project that involves 3 radio buttons, and I am looking to display an image next to the selected radio button using ...

What steps can I take to address the issue with the jquery dropdown?

I am struggling with implementing JavaScript functionality on my website. Specifically, I have a dropdown menu in the hamburger menu that is causing some issues. Whenever I click on the services option, the dropdown opens but quickly closes again. I'v ...

Is it possible to detect when a scrollable div is "in focus" on Firefox?

When using Firefox, a scrollable div can become "focused" and respond to mousewheel and Page Up/Down keys, although technically divs cannot be focused according to the HTML5 specification. This means that even though events are triggered on the div, it is ...

The slow rendering of Threejs is causing the browser to become unresponsive

Utilizing ThreeJS, I successfully created a captivating 3D scene with ten thousand particles arranged in a specific layout. Rendering these particles in the 3D world was seamless thanks to ThreeJS. However, I encountered an issue where the browser would di ...

How can Django implement a textarea widget with a character counter/limiter using JavaScript?

I am currently exploring the use of a textarea form field that involves implementing some custom JavaScript code to count and restrict the character limit. The maximum length and size of the textarea are dynamic, meaning these attributes can be changed aft ...

Is there a way to retrieve the title, description, and image URL of a URL through Ajax, similar to how Facebook shares a link?

Currently, I am developing a project that involves allowing users to submit a URL. The system will then extract the title, images, and description from the provided URL and offer the option to toggle between different images. Upon submission, these extrac ...

Encountering an Angular 5 (IE11) Error: Unhandled Promise Rejection - Route Matching Error

I've been encountering issues with IE11 in Angular 5 for a few days now. I've enabled polyfills: import 'core-js/es6/symbol'; import 'core-js/es6/object'; import 'core-js/es7/object'; import 'core-js/es6/functio ...

Retrieving user profile upon login using AJAX and PHP

I need to implement a way to fetch user profile data using AJAX and PHP upon login, without refreshing the page. Below is my login.php code. Once the login is successful, I want to display an alert with the user's name and email. <?php inclu ...

What is the most efficient way to fill an array containing a maximum of 10,000 numbers using a for loop in C?

I have made progress on my code. The random number generator is working fine, but I'm stuck on how to load the user's desired numbers into an array. Additionally, I need to ensure that the user inputs a quantity between 2 and 10,000. #include &l ...

What could be causing my fetch post request to only hit on breakpoint and not otherwise?

In my React .Net application, I am using the following code snippet: handleAdd(userId, name) { name = encodeURIComponent(name); fetch('api/deck/create/' + userId + '/' + name, { method: 'POST' }).then(); } This ...

Script to identify compatibility with Bootstrap 4 in Javascript

As I work on developing a website utilizing Bootstrap 4 for its UI design, I have encountered the issue that Bootstrap 4 does not support IE 8 and certain older versions of browsers. In order to address this, I am looking to implement an error page that w ...