There are 20 red balls sitting in 20 positions on 4 shelves. I'd like to move each ball to a new position on its own shelf by generating a new array that contains the new positions for all 20 balls. I am using a function (below) that allows me to do this. However, I find that this function keeps crashing; when I print out values it is generating, it seems to hang at the last coordinate of available position but then doesn't exit the "while" loop.
func generateNewLocationArray(previous: [String: CGPoint]) -> [String : CGPoint] {
var refreshedLocations = [String : CGPoint]()
for (location, _) in previous {
let node = fgNode.childNode(withName: location) as? LocationNode
var newLocation = generateRandomLocation(check: refreshedLocations)
let previousLocation = previous[(node?.name!)!]
while (newLocation == previousLocation) || (newLocation.y != previousLocation?.y) {
newLocation = generateRandomLocation(check: refreshedLocations)
}
node?.position = newLocation
refreshedLocations[location] = newLocation
}
return refreshedLocations
}
What I'm trying to achieve:
The function takes in an array of CGPoints, this is the "previous"
array, of positions where all the balls were.
It then creates a brand-new array, called "refreshedLocations".
It loads an existing node, which is located at a position contained in the previous array.
It then generates a new position for that node. This position is obtained from a pre-set array of 20 positions. This new location cannot be the same position the node is currently in; nor can it have a different y-coordinate (so the balls stay on the shelf).
Until this criteria is met, it keeps generating a new position.
It then loads this position into the refreshedLocations array, and the next node is also checked against this array to ensure there are no repeated positions.
The problem again: this code works with 10, or 15 balls. But when the number is pushed to 20, it seems more likely to hang. It will identify how to move all the balls around to new positions, but it gets stuck in the "while" loop more often than not. What's going wrong here?
EDIT: This is the function which returns a random location, but first checks whether the array that is being generated already contains the point being added.
func generateRandomLocation(check: [String : CGPoint]) -> CGPoint {
let randomIndex = GKRandomDistribution(lowestValue: 0, highestValue: allPositions.count - 1)
var position = allPositions[randomIndex.nextInt()]
while check.values.contains(position) {
position = allPositions[randomIndex.nextInt()]
}
return position
}
As far as I can see,
while (newLocation == previousLocation) || (newLocation.y != previousLocation?.y)
is always going to be true if you get to the last ball on a row and the only position left is the one it is already in. You can fix it by detecting how many positions on the shelf have been filled and if it is n - 1 out of n and the only position left is the one the ball is in, just swap it with a randomly selected other ball on the same shelf.
However, there must be a better way of doing this. I'd try a modified Fisher Yates shuffle.
Firstly, organise the balls by shelf and position. Then, for each shelf of n balls (numbered 0 to n - 1)
select a random ball from the first n - 2 balls and swap it with ball n - 1
select a random ball from the first n - 3 balls and swap it with ball n - 2
select a random ball from the first n - 4 balls and swap it with ball n - 3
and so on. Stop when the random selection would be from 0 balls. At the end of this process, all the balls will have changed position.
Repeat for each shelf.
Related
I have successfully created a script that draws a line from one pivot point to another based on some conditions.
The conditions are:
The next (or following next) pivot points must have a high that is lower than the original pivot point high
The next (or following next) pivot points must have its wick "touching" the original wick
It is not allowed to draw a line if there is any higher high in the range between the original pivothigh wick and the current pivothigh wick. If several conditions are meet, a "fan of lines" from the original pivot high can be created.
I asked a question about this before here Creating a fan of lines in pinescript
I managed to do this using arrays. Result can been seen here. Purple lines are "hand drawn" and the white dashed lines comes from the script.
So far so good. BUT I have 3 problems/issues:
When a new candle is created the script fails due to "range out of bounds". I know it comes from the var declarations of the arrays on lines 19-22. If on the 1h or above timeframe it's not a big issue but on 1m it's very annoying. But deleting the var it doesn't compile correctly.
It's slow. Takes a couple of seconds to compute even though I'm using a while statement that prevents the "lookback" if a "top" has been detected. Then it's no longer necessary to look back anymore.
Related to (2). I'm suspecting there is a much smarter way to do this in pinescript but I haven't figured it out yet without the use of arrays.
Here's the code.
Is it possible to do this without arrays to solve all my 3 issues above?
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sincereStork12718
//#version=5
indicator("Pivot lines", overlay=true, max_boxes_count=500, max_lines_count=500)
//---------------------------------------
//Variable declaration
//---------------------------------------
nbrBars = bar_index
plot (nbrBars, title = "nbrBars", display = display.data_window)
plot(last_bar_index, title = "last_bar_index", display = display.data_window)
//---------------------------------------
// Pivot to downside with ARRAYS working 99.9%
//---------------------------------------
//Declare arrays
var index_array_bear = array.new<int>(last_bar_index)
var high_array = array.new<float>(last_bar_index)
var upwick_array = array.new<float>(last_bar_index)
var shadow_high = array.new<float>(last_bar_index+1)
isAttFUBear = ta.pivothigh(1,1) // For this example ta.pivothigh(1,1) will produce the same result
isFUBear = ta.pivothigh(1,1) //For this example ta.pivothigh(1,1) will produce the same result
//Get bar_index, the high and the start of the upper wick for all AttFU/FU to the downside in the series from first to last bar
var i_dn = 0
if (isAttFUBear or isFUBear) == 1
array.set(index_array_bear, i_dn, bar_index[1])
array.set(high_array, i_dn, high[1])
array.set(upwick_array, i_dn, math.max(open[1], close[1]))
i_dn += 1
var j_dn = 0
array.set(shadow_high, j_dn, high)
j_dn += 1
//Draw line from index n to all index n++ that satisfies the condition below
//1. n++ index must be lower than the n:th index, i.e. high[n++] < high[n]
//2. n++ index must have it's high above upwick of the n:th, i.e. high[n++] > upwick[n]
//3. There must be a filter that removes any high higher than high[n] and high[n++] to not draw any unneccesary lines, i.e. if a "top" is found is not longer neccecary to look backwards
cnt1 = 1
top_found = false
while i_dn >= 2 and i_dn-1-cnt1 >= 0 and top_found == false
if array.get(high_array, i_dn-1-cnt1) > array.get(high_array, i_dn-1) //Checking if previous FU is higher than the current, if NOT, check the next previous etc.
if array.get(high_array, i_dn-1) >= array.get(upwick_array, i_dn-1-cnt1) //Check if current high is retesting the wick of the previous
//testing filter high in between
high_in_range = array.new<float>(nbrBars) //Create temporary array that stores all high values between the first and last endpoints of the line
for cnt2 = array.get(index_array_bear, i_dn-1-cnt1) to array.get(index_array_bear, i_dn-1)
array.set(high_in_range, cnt2, array.get(shadow_high, cnt2))
max_high_in_range = array.max(high_in_range, 2) //Return the second highest high within the range (excluding the i-1-cnt1 which is the higest)
if array.get(high_array, i_dn-1) > max_high_in_range
line.new(array.get(index_array_bear, i_dn-1-cnt1), array.get(high_array, i_dn-1-cnt1), array.get(index_array_bear, i_dn-1), array.get(high_array, i_dn-1), color = color.white, style = line.style_dashed, width = 1)
if i_dn-1-cnt1-1 > 0 //Checking if current lookback FU is higher than previous and next. If true then stop the search
if array.get(high_array, i_dn-1-cnt1) > array.get(high_array, i_dn-1-cnt1+1) and array.get(high_array, i_dn-1-cnt1) > array.get(high_array, i_dn-1-cnt1-1)
top_found := true
cnt1 := cnt1 +1
I'm quite new to programming and GDScript and wondering how to do something that I did think would be quite simple, but I'm struggling a bit!
I've loaded an array of images and I want it to go through each of these images one after the other each time a button is clicked and replace a sprite texture with that particular image.
Right now I can successfully get the sprite to change to any of the images if I put its array number in e.g. new_texture[0] or new_texture[3] etc., but I would like it to go through each of the images one after the other every time the user clicks the button. (And once it's got to the last image, go back to the first image again)
What am I missing?
Here is the code:
extends Node
onready var my_sprite = get_node("/root/Game/Picture/Sprite")
var new_texture = [
load("res://Art/Picture1.png"),
load("res://Art/Picture2.png"),
load("res://Art/Picture3.png"),
load("res://Art/Picture4.png"),
load("res://Art/Picture5.png"),
load("res://Art/Picture6.png")
]
func _on_Arrow_pressed() -> void:
my_sprite.texture = new_texture[0]
As you know, you can get the first image of the array like this:
new_texture[0]
Where new_texture is a variable that holds the array, and 0 is a constant (a literal, to be more precise) that indicates the position on the array.
So you can change the constant to get a different image, for example the fourth element is:
new_texture[3]
Now, that is fine if you want to change which element you get form one version to another of the game, since it requires to modify the code…
But you want which element from the array you get to change during the execution of the program. In other words, you don't want it to be constant, but variable.
So declare a variable that will hold which element you get:
var texture_index := 0
Use it instead of the constant:
func _on_Arrow_pressed() -> void:
my_sprite.texture = new_texture[texture_index]
And now you can change which element from the array you get by changing the value of the variable, for example:
func _on_Arrow_pressed() -> void:
my_sprite.texture = new_texture[texture_index]
texture_index += 1
Here we are changing the value of texture_index so the next time this code executes it gets the next position.
But be careful to keep the value of the variable inside the bounds of the array to avoid errors. So, what do you want to do after it reaches the last element? Do you want it to loop? Well, we can check if we reached the last element with an if, and set a different value to the variable, like this:
func _on_Arrow_pressed() -> void:
my_sprite.texture = new_texture[texture_index]
texture_index += 1
if texture_index == new_texture.size():
texture_index = 0
I remind you that elements of the array go from the position 0 to the position array.size() - 1. For example, if the array has 10 elements, they are in the positions 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 (from 1 to 9, there are 9 numbers, plus the 0 you have 10 numbers). So an array with 10 elements does not have an element in the position 10. So there is no element in the position array.size(), thus, when the index reaches that value it is out of bounds of the array, and we reset it to the first element (position 0).
There are, of course, variation of this approach.
Which element is seen the most in the array, if there is a tie, return the first element that was encountered by my user?
I have the user picking the wind for his golf shots. So at each distance the user shoots from, a wind is picked and put into an array. The array is as follows. 0 = no wind, 1 = north, 2 = west, 3 = south, 4 = east (I used Ints for some reasons but it likely can only be attributed to my semi-beginner knowledge).
The goal is to return the most common wind that they encountered during the round/level. If there is a tie, the first wind condition they encountered should be returned.
The current problem in my solution is that when I iterate through the initial array and place them in a dictionary to increment the value for each key as it is seen, I have no way of knowing which order the dictionary is in, therefore I cannot return the first key-value pair in the dictionary like I was hoping for initially. My current setup can only sort ties by taking the greatest or least key between the tieing key-value pairs. In no way does that reflect the order in the myWindArray itself.
// myWindArray basically means they took 8 shots, 0(no wind) was first shot, 1(north wind was second), ...
let myWindArray = [0,1,0,3,1]
func mostCommonWind (array: [Int]) -> Int {
var dictionary: [Int: Int] = [:]
// Iterate over the dictionary
for b in array {
// Every time there is a repeat value add one to that key
dictionary[b] = (dictionary[b] ?? 0) + 1
}
let decending = dictionary.sorted(by: {$0.1 > $1.1})
return (decending[0].key)
}
mostCommonWind returns a value of 0 like I am hoping for but it only return 0 because that key is sorted as the lower of the two between 0 and 1 keys. My intent is to return the value of 0 because it was the first value in the input array placed into the mostCommonWindfunction was a 0. Any ideas?
Having found your value you could just look it up using index(of:):
return array.index(of: decending[0].key)!
(The ! is required as an optional is returned, it cannot fault as you obtained your values from the array.)
HTH
I want to generate random coordinates from 0-5 (Row,Col) without any duplicates using for loops. Basically if the numbers (3,2), (3,4), (1,4), (3,4) are generated then (3,4) will not be utilized and a new random set will be looked for.
These are the methods that generate two random numbers and loads them into the array shootP[ ]
int[] shootP = new int[2];
public static int shootRowP(int[] shootP)
{
Random rn = new Random();
shootP[0] = rn.nextInt(5)+1;//loads into ROW
System.out.print("Row:"+(shootP[0]));
return shootP[0]--;
}
public static int shootColP(int[] shootP,int[][]boardP)
{
Random rn = new Random();
shootP[1] = rn.nextInt(5)+1;//loads into Col
System.out.print(" Column:"+(shootP[1])+"/n");
return shootP[1]--;
}
I then want to have another method read in the values of shootP[0] and shootP[1] and check to see if those values were already used. Any suggestions?
Since the number of possible coordinates (X,Y) is not too large, first build a list of all possible coordinates. Then, at each random pick, choose a random element in this list (i.e. rn.nextInt(current_list_length)), remove the element from the list and return it.
Notes:
You must do something (exception?) when the list is empty
You can also shuffle your list at initialization and at each draw, you pick and delete the first (or last) element. See here how to shuffle an array, for example.
Instead of a list, a stack (an array + an element counter) can do the trick.
I thinking about this problem since two days and didn't find a practicable resolution:
I have a two dimensional array and want to find the biggest number of items that are connected (horizontal and vertical, not diagonal) but no item of this group should be duplicate.
Examples for possible groups:
--FG- or -F--- or -----
--E-- -E--- ---AF
-BC-- CBD-- ----B
-AD-- -A--- --CDE
This is a simplified view of my problem because in "reality" the array is 6x9 and there are three different type of "elements" (lets say numbers, letters and symbols) with each 30 distinct possible items and a blank (-) element. In the first pass I check each position and find all connected items of the same elements. This was relatively easy to achieve with a recursive function, the field 0,0 is at the bottom left (another simplified view):
12AB-1- The check for -AB----
23CD23- position 2:0 -CD----
2*CE55- ("C") would --CE---
#2E2*AA result in --E----
#$A23BC this: --A----
$$F1+*E --F----
21C31*2 --C----
The check for position 2:0 "C" would result in an array with 10 connected "letter" items. Now I search for the the biggest number of connected items in this new array that are distinct, so that are not two duplicate items in the new group. For position 2:0 this would result in max 4 connected distinct items, because you can not reach another item without touching an item that is already in the group (here another C).
For my problem it is enough to detect max. 6 different connected items in the 10 items group.
A possible group for the above example would be (when I check position 2:1 "F"):
--B----
--D----
--C----
--E----
--A----
--F----
-------
I don't find an algorithm that would do that, like the simple recursive function I use to find all the items of the same element in the array. It seems to be far more complex.
For example the algorithm must also recognize that it don't add the E at position 3:4 to the group but the E at position 2:3.
I think the above described intermediate step to first find alle connected items of an element is unneccessary, but at the moment I do this here and in my code to make things more clear :)
This is a DFS problem. The algorithm should be;
For each connected component, start a dfs with a map. Here is a pseudocode:
void dfs(position pos, map<char, bool> m, int number) {
//if the element is seen before, quit
if(m[array2d[pos] == true)
return;
//it is seen now
m[array2d[pos]] = true;
//update max value
maxValue = max(maxValue, number);
//go to each neighbor
foreach(neighbor of pos) {
dfs(neighbor, m, number+1);
}
//unlock the position
m[array2d[pos]] = false;
}
I believe that you should start dfs from each location in the array.
Because all algorithm I tried don't work or would use a big recursive stacks I have done it another way:
For my purpose it is enough to check for max. 5 connected different items in a group of elements. I made masks (around 60) for all possible combinations for 5 items. Here five examples.
----- ----- ----- ----- *----
----- ----- ----- ----- *----
----- ----- ----- ***-- ***--
----- ---*- --*-- *---- -----
***** ****- ****- *---- -----
Now I check each connected component with these masks. If all five items on this positions are different the check is true. The actual start position for the check in the mask is always in one of the four corners.
This way it takes less memory and less calculations than every algorithm I tried, but this resolution would be not acceptable for more than six or seven items because there would be to many masks.