Related
I am trying to build a battleship game and using functions.
I wish to create and randomise 1 & 0 in my array every time I run the function as seen in the array below
Since it is a battlefield game, is there any way to make the 1s be in a row /column of 4/3/2/1? , to mimic the different sizes of the battleships
let battelfield = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,1,0,0,0],
[1,0,0,0,0,0,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0],
[1,0,0,1,0,0,0,0,0,0],
[1,0,0,1,0,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,0]
]`
For a battleship, the way I would do it would be (assuming your grid is already filled with 0s):
For each ship
randomly select a starting position
randomly select a direction (up, down, left, right)
add your ship (by changing however many 1s you need to, based on the size of the ship).
The checks you need to add would be:
At step 1, make sure there isn't a boat there already, in which case pick again.
At step 2, make sure you're not going to hit the side of your game board, or another ship, in which case try another direction. If all 4 directions have been tried and there isn't enough space for a ship, back to step 1.
I usually don't give full answers when OP doesn't really show they tried but I liked the challenge.
The idea is to:
Set your empty board.
Choose a random point in the board where the ship will start
Choose direction (H or V)
With the random point and direction, make sure there is room for the ship according to the limits of the board
Create a list of positions the ship will take
Test all positions to make sure they are free
Set the positions on the board as filled.
At any given time, if a check is not fulfilled I've put continue; this will stop the current iteration and back to the beginning of the while. That way, the code runs until it finds a spot, and return to leave the loop.
Also, I've made a 1d array instead of 2d because it felt easier for mathing it out and manipulations. Feel free to convert to 2D afterward, or not.
let battlefield = new Array(10*10).fill(0);
placeShip(3);
placeShip(4);
placeShip(4);
placeShip(5);
console.log(battlefield);
function placeShip(length){
while(true){
const start = Math.round(Math.random()*99);
if(battlefield[start]==='1') continue;
const orientation = Math.random() <=.5?'H':'V';
// Fill the positions where the ship will be placed.
const positions = new Array();
if(orientation === 'H'){
// First make sure we have room to place it
if(10-((start+1) % 10) < length)continue;
for(let p=start;p<start+length;p++){
// Set for each length the position the ship will take.
positions.push(p);
}
}else if(orientation === 'V'){
// Same as for H but we divide by 10 because we want rows instead of cells.
if(10-((start/10) % 10) < length)continue;
for(let p=start;p<start+length*10;p+=10){
// Set for each length the position the ship will take.
positions.push(p);
}
}
// Now let's check to make sure there is not a ship already in one of the positions
for(let i=0,L=positions.length;i<L;i++){
if(battlefield[positions[i]]!=="0")continue;
}
// Now let's put the ship in place
for(let i=0,L=positions.length;i<L;i++){
battlefield[positions[i]] = 1;
}
return;
}
}
I'm looking to divide a rectangle up into smaller cells while trying to keep the cells close to the same size. Iām trying to keep the cells widths and heights similar. Aspect ratio of 1:1 or closest to that.
Examples:
Number of blocks = 1
return rectangle
Number of blocks = 2
return splitCell(rectangle)
Number of blocks = 3
const cells = splitCell(rectangle)
return [...splitCell(cells[0]), cells[1]]
Number of blocks = 4
const cells = splitCell(rectangle)
return [...splitCell(cells[0]), ...splitCell(cells[1])]
Number of blocks = 5
This is where it starts getting tricky. Firstly the one row has a different number of cells. Secondly the cells in row 1 are a different size to row 2.
?
Number of blocks = 41
?
The split cell function
const splitCell = (cell): Array => cell.width > cell.height ? splitByWidth(cell) : splitByHeight(cell)
My initial solution was to find the largest sided cell and divide that but at a certain point most cells have the same dimensions so it ends up with a group of smaller cells on one side:
Something like:
for (let i = 1; i < n; i++) {
// findBiggest(cells)
// splitCell(biggestCell)
// add splitCell in to result array
}
But I am looking for a solution where most of the cells have a similar aspect ratio
Ideally, you would like to use N squares of area W.H/N, i.e. of side ā(W.H/N). So the number of squares per sides should be U=ā(N.W/H)) in width and V=ā(N.H/W)) in height, or the closest integers.
So you can form the subdivision in U.V squares, and add or remove one cell per row or per column (and rearrange evenly) to adjust to N.
As a fine tuning, you can also adjust the heights of the two different row types by using a penalty value (that rates the discrepancy to the ideal ratio; for instance, the absolute difference of the area and the ideal area W.H/N) and minimize the total penalty.
I have an ordered data set of decimal numbers. This data is always similar - but not always the same. The expected data is a few, 0 - 5 large numbers, followed by several (10 - 90) average numbers then follow by smaller numbers. There are cases where a large number may be mixed into the average numbers' See the following arrays.
let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];
let expectedData = [35.267,32.267,9.267,9.332,9.186,9.220,9.141,9.107,30.267,9.114,9.098,9.181,9.220,4.012,0.132];
I am trying to analyze the data by getting the average without high numbers on front and low numbers on back. The middle high/low are fine to keep in the average. I have a partial solution below. Right now I am sort of brute forcing it but the solution isn't perfect. On smaller datasets the first average calculation is influenced by the large number.
My question is: Is there a way to handle this type of problem, which is identifying patterns in an array of numbers?
My algorithm is:
Get an average of the array
Calculate an above/below average value
Remove front (n) elements that are above average
remove end elements that are below average
Recalculate average
In JavaScript I have: (this is partial leaving out below average)
let total= expectedData.reduce((rt,cur)=> {return rt+cur;}, 0);
let avg = total/expectedData.length;
let aboveAvg = avg*0.1+avg;
let remove = -1;
for(let k=0;k<expectedData.length;k++) {
if(expectedData[k] > aboveAvg) {
remove=k;
} else {
if(k==0) {
remove = -1;//no need to remove
}
//break because we don't want large values from middle removed.
break;
}
}
if(remove >= 0 ) {
//remove front above average
expectedData.splice(0,remove+1);
}
//remove belows
//recalculate average
I believe you are looking for some outlier detection Algorithm. There are already a bunch of questions related to this on Stack overflow.
However, each outlier detection algorithm has its own merits.
Here are a few of them
https://mathworld.wolfram.com/Outlier.html
High outliers are anything beyond the 3rd quartile + 1.5 * the inter-quartile range (IQR)
Low outliers are anything beneath the 1st quartile - 1.5 * IQR
Grubbs's test
You can check how it works for your expectations here
Apart from these 2, the is a comparison calculator here . You can visit this to use other Algorithms per your need.
I would have tried to get a sliding window coupled with an hysteresis / band filter in order to detect the high value peaks, first.
Then, when your sliding windows advance, you can add the previous first value (which is now the last of analyzed values) to the global sum, and add 1 to the number of total values.
When you encounter a peak (=something that causes the hysteresis to move or overflow the band filter), you either remove the values (may be costly), or better, you set the value to NaN so you can safely ignore it.
You should keep computing a sliding average within your sliding window in order to be able to auto-correct the hysteresis/band filter, so it will reject only the start values of a peak (the end values are the start values of the next one), but once values are stabilized to a new level, values will be kept again.
The size of the sliding window will set how much consecutive "stable" values are needed to be kept, or in other words how much UNstable values are rejected when you reach a new level.
For that, you can check the mode of the values (rounded) and then take all the numbers in a certain range around the mode. That range can be taken from the data itself, for example by taking the 10% of the max - min value. That helps you to filter your data. You can select the percent that fits your needs. Something like this:
let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];
expectedData.sort((a, b) => a - b);
/// Get the range of the data
const RANGE = expectedData[ expectedData.length - 1 ] - expectedData[0];
const WINDOW = 0.1; /// Window of selection 10% from left and right
/// Frequency of each number
let dist = expectedData.reduce((acc, e) => (acc[ Math.floor(e) ] = (acc[ Math.floor(e) ] || 0) + 1, acc), {});
let mode = +Object.entries(dist).sort((a, b) => b[1] - a[1])[0][0];
let newData = expectedData.filter(e => mode - RANGE * WINDOW <= e && e <= mode + RANGE * WINDOW);
console.log(newData);
I have been a lurker on this site for a while while I have been designing a small program in JavaScript. I have run into an issue that I cant seem to solve though, and I need your help!
You can find my question at the bottom of the post. in layman's terms, I need help setting up a function loop to compare three values against three values in a nested array, where each value can be compared to all three values in the array.
I am working on a type of calculator to find the best way to cut a 3D rectangular piece of material to get the best yield, given a certain cut size I need. I have many different potential sizes of material to cut from, sorted in a nested array, like this:
data[i][x] where i = number key of material arrays available, and x(each array) is set like so
[material ID#, height, width, length, volume]
What I have is a cut size i need to cut out of the parent material, which consists of: height, width, length.
The problem I have run into is the best way to align the cut size in the parent material (comparing each value against the other: height, width, length) to return the highest yield possible.
I will mention that I do have a few constraints.
First, whatever lines up in the height measurement in the parent material can only be 1 cut size tall. The other two dimensions (length and width) can be multiple sizes each if they fit.
so, to reiterate constraints:
Height must be 1 unit.
Width can be multiple units
Length can be multiple units
The orientation of the cut piece does not matter, so long as these constraints are held to.
Length or width can be a decimal, but only whole cut sizes will count.
Below, I have placed some code I designed to do this, but the problem is it is not nearly dynamic enough, as you can see. it only compares height to height, width to width, and so forth.
The input for this is as follows:
cutheight;
cutwidth;
cutlength;
data[i][x]; // each nested data array is like so: [material ID key, height of material, width of material, length of material, volume of material]
for (i = 0; i < data.length; i++) {
if((data[i][1] > cutheight) && (data[i][2] > cutwidth) && (data[i][3] > cutlength)) {
insertSort(results, data[i], (Math.min((data[i][4] * (Math.ceil((numCutPiecesNeeded) / ((Math.floor(data[i][2]/cutwidth)) * (Math.floor(data[i][3]/cutlength)))) ) ))) ); // the last step in this function determines pieces yielded per material
}
}
function insertSort(array, match, materialVolumeMin){
//itterate through the results and see where the new match should be inserted
for( j = 0; j < array.length; j++){
// if the billetVolumeMin of match is less than the materialVolumeMin of the item at position j
if(array[j][5] > materialVolumeMin){
//insert the match at position j
array.splice(j, 0, match);
array[j].push(materialVolumeMin);
return true;
}
}
// if the materialVolumeMin of match is not less than anything in array then push it to the end. This should also cover the case where this is the first match.
// push match
match.push(materialVolumeMin);
array.push(match);
// set match index 5 to the materialVolumeMin for future comparison
//array[array.length -1].push(materialVolumeMin);
return true;
}
What this code does is return all the nested arrays with a new value attached to the end, which is in effect the total volume of material needed to get the cuts you need out of it. I use this volume as a way of sorting the arrays to find the most cost effective (or highest yield) material to use.
I believe this is more of a logic question, but I have no idea how to do this. I do know that it would be better to have multiple cuts available on the height axis as well, but due to various factors this is not possible.
I also believe that the height should be found first, by comparing all three dimensions of the cut size to the parent material, and finding the one that has the least waste
How I see this maybe happening:
Math.min((data[i][1]-cutheight), (data[i][1]-cutwidth), (data[i][1]-cutlength));
From there, I really dont know. I appreciate any and all help, advice, or suggestions. Thank you!
Edit: I dont think I was clear in my Question. The code above is how im going about the problem now, but is by no means how I want to actually do it in the finished program.
My question is:
How can I set up a function to compare my three sizes to each possible material size in my nested array, and return the best match, where data[i][5] (which is data[i][4] (volume of material) times the number of cut pieces I need, divided by how many fit in said parent material) is the smallest out of all of my possible choices.
Suppose if you are given a bunch of points in (x,y) values and you need to generate points by linearly interpolate between the 2 nearest values in the x axis, what is the fastest implementation to do so?
I searched around but I was unable to find a satisfactory answer, I feel its because I wasnt searching for the right words.
For example, if I was given (0,0) (0.5 , 1) (1, 0.5), then I want to get a value at 0.7; it would be (0.7-0.5)/(1-0.5) * (0.5-1) + 1; but what data structure would allow me to find the 2 nearest key values to interpolate in between? Is a simple linear search/ binary search if I have many key values the best I could do?
The way I usually implement O(1) interpolation is by means of an additional data structure, which I call IntervalSelector that in time O(1) will give the two surrounding values of the sequence that have to be interpolated.
An IntervalSelector is a class that, when given a sequence of n abscissas builds and remembers a table that will map any given value of x to the index i such that sequence[i] <= x < sequence[i+1] in time O(1).
Note: In what follows arrays are 1 based.
The algorithm that builds the table proceeds as follow:
Find delta to be the minimum distance between two consecutive elements in the input sequence of abscissas.
Set count := (b-a)/delta + 1, where a and b are respectively the first and last of the (ascending) sequence and / stands for the integer quotient of the division.
Define table to be an Array of count elements.
For i between 1 and n set table[(sequence[j]-a)/delta + 1] := j.
Repeat every entry of table visited in 4 to the unvisited positions that come right after it.
On output, table maps j to i if (j-1)*d <= sequence[i] - a < j*d.
Here is an example:
Since elements 3rd and 4th are the closest ones, we divide the interval in subintervals of this smallest length. Now, we remember in the table the positions of the left end of each of these deta-intervals. Later on, when an input x is given, we compute the delta-interval of such x as (x-a)/delta + 1 and use the table to deduce the corresponding interval in the sequence. If x falls to the left of the ith sequence element, we choose the (i-1)th.
More precisely:
Given any input x between a and b calculate j := (x-a)/delta + 1 and i := table[j]. If x < sequence[i] put i := i - 1. Then, the index i satisfies sequence[i] <= x < sequence[i+1]; otherwise the distance between these two consecutive elements would be smaller than delta, which is not.
Remark: Be aware that if the minimum distance delta between consecutive elements in sequence is too small the table will have too many entries. The simple description I've presented here ignores these pathological cases, which require additional work.
Yes, a simple binary search should do well and will typically suffice.
If you need to get better, you might try interpolation search (has nothing to do with your value interpolation).
If your points are distributed at fixed intervals (like in your example, 0 0.5 1), you can also simply store the values in an array and access them in constant time via their index.