Suppose I have a sparse domain and I want to check if an element has been added.
var D = {1..5, 1..5},
SD: sparse subdomain(D);
SD += (1,3);
Now I want to see if (1,3) and (2,3) are in SD. This is not correct and this page doesn't .contain() an example. HA! See what I did there?
//Don't do this
SD.contains(1,3) // want true;
SD.contains(2,3) // want false;
Domains in Chapel all support a method with the signature member(i:idxType ...rank) that returns true if the specified index is a member of the domain and false otherwise. This includes associative, opaque, rectangular, and sparse domains. Examples for each of these types follow:
Associative:
var D: domain(string);
D += "hello";
writeln("Associative");
writeln(D.member("world")); // false
writeln(D.member("hello")); // true
Opaque:
var D: domain(opaque);
var i1 = D.create();
var i2: i1.type;
writeln("Opaque");
writeln(D.member(i2)); // false
writeln(D.member(i1)); // true
Rectangular:
var D = {1..4, 3..5};
writeln("Rectangular");
writeln(D.member(2,6)); // false
writeln(D.member(3,3)); // true
Sparse:
var D = {1..10, 1..10};
var SD: sparse subdomain(D);
SD += (2,3);
writeln("Sparse");
writeln(SD.member(2,7)); // false
writeln(SD.member(2,3)); // true
Related
I want to be able to call specific phrases by their assigned numbers in this JSON file, so when i call go(1) for example, it displays only the text that has 'Num' set as 1.
my JSON file:
[
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
{"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
The Textbox code:
extends ColorRect
export var dialogPath = ""
export(float) var textSpeed = 0.005
var dialog
var phraseNum = 0
var finished = false
func go(phraseNum):
$Timer.wait_time = textSpeed
dialog = getDialog()
assert(dialog, "Dialog not found")
nextPhrase()
var f = File.new()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.scancode == KEY_Q:
if finished:
$NEXT.play()
nextPhrase()
else:
$Text.visible_characters = len($Text.text)
func getDialog() -> Array:
var f = File.new()
assert(f.file_exists(dialogPath), "File path does not exist")
f.open(dialogPath, File.READ)
var json = f.get_as_text()
var output = parse_json(json)
if typeof(output) == TYPE_ARRAY:
return output
else:
return []
func nextPhrase() -> void:
if phraseNum >= len(dialog):
queue_free()
return
finished = false
$Name.bbcode_text = dialog[phraseNum]["Name"]
$Text.bbcode_text = dialog[phraseNum]["Text"]
$Text.visible_characters = 0
while $Text.visible_characters < len($Text.text):
$Text.visible_characters += 1
$TEXT_AUDIO.play()
$Timer.start()
yield($Timer, "timeout")
finished = true
phraseNum += 1
return
how I call it:
$TextBox.show()
$TextBox.go(1)
and lastly, the tutorial I followed for it:
https://www.youtube.com/watch?v=GzPvN5wsp7Y
How would I approach to do this?
What you asked
What you are asking requires quite some extra work. Let us start with the JSON file:
[
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
{"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
This will parse as an Array (everything between [ and ]), where each element is a Dictionary (which are the ones between { and }). This means that you are going to need to iterate over the Array, check every Num.
Before we do that, we need to acknowledge that we would be using the name phraseNum to representing two things:
The index in the dialog Array
The desired value of Num
We are in this situation because of the source material you are using. They have phraseNum as a parameter (here: func go(phraseNum)) that hides a phraseNum field (here var phraseNum = 0 ).
This hinders communication. If I tell you do this with phraseNum, which one is it? That is bound to give us trouble.
I'll be rewriting go so it takes a Num instead of the index in the dialog Array, so I'll keep phraseNum for the index in the dialog Array, and have a different name for the parameter of go.
Let us begin rewriting go:
func go(num):
pass
Now, let us get all the dialogues:
func go(num):
dialog = getDialog()
We are going to iterate over them. Since I will want an index for phraseNum, I will iterate using index:
func go(num):
dialog = getDialog()
for index in dialog.size():
pass
And we need to check if Num matches. If it does, we got our index:
func go(num):
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
break
We need to handle the case where we didn't find it. Now, hmm… The source material only has an assert, I'll keep that approach. So we need a way to know the code didn't find it…
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
Next the you call nextPhrase(), sure:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
nextPhrase()
And then a not used var f = File.new(), I'll not add that.
And you set the portrait texture. Sure:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
And I had skipped over the timer thing, I'll insert it now:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
$Timer.wait_time = textSpeed
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
Something else
Now, you said you want only the phrases with the given Num. This is open to interpretation.
To be clear, the code above will have the dialog start at the first instance of the Num you ask for. But it will not end - nor skip - when it finds a different Num. I don't know if you want that or not.
And we have a couple ways to do this. We could remember what was the num and check against it in nextPhrase. I really don't want to do that. So, I will give you an alternative approach: let us make a dialog array that only contains the elements we want.
It looks like this:
func go(num):
var every_dialog = getDialog()
dialog = []
for candidate in every_dialog:
if num == candidate["Num"]:
dialog.append(candidate)
assert(dialog, "Dialog not found")
phraseNum = 0
$Timer.wait_time = textSpeed
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
Please notice that in this example we are not reading getDialog() to dialog. Instead we are building a dialog array that only contains the entries we want. And we do that by iterating the result of getDialog() (we add to the array with append).
This is a subtle change in the meaning of dialog, because it would no longer represent every entry form the JSON file. Instead it only represent the entries that will be displayed. Before those two things were the same, but they aren't anymore with this change.
What you didn't ask
The function getDialog reads from the JSON file. And you would be doing that every time you call go. You could instead do it once in _ready.
It is important that you understand what each variable represents, and also where you read and write them. Above I mention that the there is a subtle meaning of dialog do to the alternative change. You need to consider that to make this change.
I strongly believe that nextPhrase should handle the timer, and the portrait. There should be no need to set those from go.
I want you to consider this alternative JSON file structure:
[
[
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
],
[
{"Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
]
Then you would get an Array of Arrays, where each element of the nested Array is a Dictionary. Then you can "simply" get the nested array by index, instead of having to iterate over every element.
The structure you get resembles the structure of the JSON file. Which also means you will have to change how you use it. For example, instead of dialog[phraseNum]["Text"] it could be dialog[num][phraseNum]["Text"].
Now consider the source material. In this case we have a node that has both the responsibility of parsing JSON and the responsibility of displaying character dialogues. It would be easier to tinker with this code if these were separated from each other. Presumably the intention of the author is that you would have different JSON files for the different dialogues, so you would switch JSON file when necessary (which also explain why they read the JSON file each time).
But that is not what you asked.
Invalid get'index '16' (on base:Array).
Hi Im getting the above message when the player attempts to moves outside of the
array size (tile size).
The error occurs on line 54:
grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER
Im stuck on how to fix it.
regards
Jason
extends TileMap
var tile_size = get_cell_size()
var half_tile_size = tile_size / 2
enum ENTITY_TYPES {PLAYER, OBSTACLE, COLLECTIBLE}
var grid_size = Vector2(16,16)
var grid = []
onready var Obstacle = preload("res://scenes/Obstacle.tscn")
func _ready():
# Creates grid array of grid_size
for x in range(grid_size.x):
grid.append([])
for y in range(grid_size.y):
grid[x].append(null)
print(ENTITY_TYPES)
# Create obstacle positions array
var positions = []
# create 5 obstacles
for n in range(5):
# random positions constrained to grid_size
var grid_pos = Vector2(randi() % int(grid_size.x),randi() % int(grid_size.y))
#check random posisitions not already in array before adding new one
if not grid_pos in positions:
positions.append(grid_pos)
for pos in positions:
var new_obstacle = Obstacle.instance()
new_obstacle.position = (map_to_world(pos) + half_tile_size)
grid[pos.x][pos.y] = ENTITY_TYPES.OBSTACLE
add_child(new_obstacle)
func is_cell_vacant(pos, direction):
# Return true if cell is vaccant, else false
var grid_pos = world_to_map(pos) + direction
# world Boundaries
if grid.pos.x < grid_size.x and grid_pos.x >= 0:
if grid.pos.y < grid_size.y and grid_pos.y >= 0:
#return true if grid[grid_pos.x][grid_pos.y] == null else false
return grid[grid_pos.x][grid_pos.y] == null
return false
func update_child_pos(child_node):
# Move a child to a new position in the grid array
# Returns the new target world position of the child
var grid_pos = world_to_map(child_node.position)
grid[grid_pos.x][grid_pos.y] = null
var new_grid_pos = grid_pos + child_node.direction
grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER
var target_pos = (map_to_world(new_grid_pos) + half_tile_size)
return target_pos
pass
You are receiving the error because you are exceeding the arrays elements.
To avoid it, either make the grid bigger or make it so that the player cannot leave the grid.
If you want the player to be able to leave the grid, then you will probably have to create an if condition:
if player_in_grid: grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER.
I have this structure: [String: [String: Double]]()
Specifically, something like that: var dictionaries = ["GF": ["ET": 4.62, "EO": 21.0],"FD": ["EE": 80.95, "DE": 0.4]]
How can I easily access and modify nested dictionaries?
EXAMPLE UPDATED: I want to append "TT": 6 at FD and later I want to append another dictionary inside the array. At the end I'll print the results.
for (key,value) in dictionaries {
// if array contains FD, add the record to FD
if key.contains("FD") {
dictionaries["FD"]!["TT"] = 6
}
else {
// if array doesn't contain FD, add FD and add the record to it
dictionaries = dictionaries+["FD"]["TT"] = 6 // <-- I know that it's wrong but I want to achieve this result in this case.
}
}
Result of print will be:
GF -> ET - 4.62, EO - 21.0
FD -> EE - 80.95, DE - 0.4, TT - 6
MISSION: I need to append new dictionary records like in the example above, update existing ones in a simple and straightforward way, loop easily through records to read the values and print them out.
Can anyone help me? Never had the chance to manage dictionaries in Swift since now.
It is not clear what exactly you need but the exercise amused me, so I came up with this solution: we extend Dictionary so that it provides convenience methods if it is a nested dictionary.
First, because of Swift idiosyncrasies, we have to create a dummy protocol to "mark" Dictionary¹:
protocol DictionaryProtocol {
associatedtype Key: Hashable
associatedtype Value
subscript(key: Key) -> Value? { get set }
var keys: LazyMapCollection<[Key : Value], Key> { get }
}
extension Dictionary: DictionaryProtocol {}
Basically, just copy-paste² all declarations you need later from Dictionary to DictionaryProtocol.
Then, you can happily extend away. For instance, add a two-parameter subscript:
extension Dictionary where Value: DictionaryProtocol {
typealias K1 = Key
typealias K2 = Value.Key
typealias V = Value.Value
subscript(k1: K1, k2: K2) -> V? {
get {
return self[k1]?[k2]
}
set {
if self[k1] == nil {
self.updateValue([K2: V]() as! Value, forKey: k1)
}
self[k1]![k2] = newValue
}
}
}
Or an alternative pretty-print³:
extension Dictionary where Value: DictionaryProtocol {
func pretty() -> String {
return self.keys.map { k1 in
let row = self[k1]!.keys.map { k2 in
return "\(k2) - \(self[k1]![k2]!)"
}.joined(separator: ", ")
return "\(k1) -> \(row)"
}.joined(separator: "\n")
}
}
You can also create a type alias for this special dictionary:
typealias D2Dictionary<K: Hashable, V> = Dictionary<K, Dictionary<K, V>>
Going back to the example in your question:
var dictionary = D2Dictionary<String, Double>()
dictionary["GF", "ET"] = 4.62
dictionary["GF", "EO"] = 21.0
dictionary["FD", "EE"] = 80.95
dictionary["FD", "DE"] = 0.4
dictionary["FD", "TT"] = 6
print(dictionary.pretty())
// > GF -> ET - 4.62, EO - 21.0
// > FD -> EE - 80.95, DE - 0.4, TT - 6.0
Background: Only protocols can be used in type bounds on extension conditions.
Make sure to get the types right. If we write var keys: [Key] { get }, for instance, the compiler dies with a seg fault.
Unfortunately, extension Dictionary: CustomStringConvertible where Value: DictionaryProtocol { ... } is not allowed, for whatever reason.
It'll be much simpler if you have the key "FD" you can use
dictionaries["FD"]!["TT"] = 6
to add a new value ["TT":6] or modify existing value of TT to 6. Note that the ! between ["FD"]!["TT"] assumes that ["FD"] exists regardless. You need to check if ["FD"] exists otherwise. Like:
if dictionaries["FD"] != nil {
dictionaries["FD"]!["TT"] = 6
} else {
dictionaries["FD"] = ["TT" : 6]
}
If you need to look for the key you will have to run the entire dictionary like you already tried, but dictionaries support fast enumeration for key and values like
for (key,value) in dictionaries {
if key.contains("FD") { //or any other checks you need to identify your key
value["TT"] = 6
}
}
I want to compare two arrays with each other and append the value that is not in the other array to a new array. The problem now is that all of the values that does not equal the other array already get appended, but I want only the values that are new in the other array getting appended.
I hope that the problem is clear. Sorry if it's a very vague question. I try to be clear haha.
The code and output is printed below:
// Iterate through all possible values
for i in 0...messages.count-1{
var match = false
for r in 0...self.messages.count-1{
println("NIEUWE" + messages[i].getID() + "OUDE" + self.messages[r].getID())
if(messages[i].getID().toInt() == self.messages[r].getID().toInt()){
var match = true
println(match)
break
}
}
if (!match) {
newArray.append(messages[i])
println(newArray)
}
}
Output:
NIEUWE170OUDE170
NIEUWE170OUDE171
true
[PostDuif.Message]
NIEUWE171OUDE170
true
[PostDuif.Message, PostDuif.Message]
NIEUWE172OUDE170
true
This "I want to compare two arrays with each other and append the value that is not in the other array to a new array" is just 'set difference'
var s1 = Set(["a", "b", "c"]) // this to be similar to your need
var s2 = Set(["b", "c", "d"])
var s3 = s2.subtract (s1)
As such:
9> var s3 = s2.subtract(s1)
s3: Set<String> = {
[0] = "d"
}
Note that you have subtract, intersect, and union with inPlace options as methods on the Set type. New to Swift 1.2.
I have an ActionScript 3 array that lists pairs of items like this:
pairs[0] = Array('ItemA', 'ItemB');
pairs[1] = Array('ItemA', 'ItemC');
pairs[2] = Array('ItemC', 'ItemD');
pairs[3] = Array('ItemC', 'ItemE');
pairs[4] = Array('ItemF', 'ItemG');
pairs[5] = Array('ItemF', 'ItemH');
And I need to loop over the array in some way to find all overlapping pairs (any pairs that share common pairs).
For example, ItemA is paired with ItemB and ItemC, so they belong in a group together. ItemC is also paired with ItemD and ItemE so they also need to be a part of the first group.
ItemF, ItemG and ItemH do not overlap with any of the items fromt he first group, so they need to be put into their own group.
The resulting array would need to be something like:
groups[0] = Array('ItemA', 'ItemB', 'ItemC', 'ItemD', 'ItemE');
groups[1] = Array('ItemF', 'ItemG', 'ItemH');
Thanks for any help and suggestions!
Edit:
A little bit of a back story; I'm trying to group together movie clips that overlap each other in 2D to create groups or clusters (might be a better word).
So if I have 3 movieclips on the stage and ClipA overlaps with ClipB and ClipB overlaps ClipC (but ClipA doesn't directly overlap ClipC) they should all be grouped together as they are all a part of the same cluster. This way should a new clip overlap any single item in a cluster, it will be added to that group's array.
I've already got the code worked out to find overlapping elements which is producing this pairs list, now I need to condense it into tidy groups.
An algorithm like the example below should work.
NOTE: This is not the most efficient or concise way to write this code (it's certainly more repetitive than it needs to be), but I wanted to keep it clear and simple for this example. [Also, I haven't tested this code--it's presented as pseudo-code only--so if you find an error, please just let me know, and I'll fix it]
var idx:Object = new Object;
var groups:Array = new Array();
for( var i:int = 0; i<pairs.length; ++i ) {
var onePair:Array = pairs[i];
// which groups do the two items belong to?
var g1:Array = idx[onePair[0]];
var g2:Array = idx[onePair[1]];
if( !g1 ) {
// if item #1 is not yet in a group, then add it to item #2's
// existing group, or if neither group exists yet, just create a new one
g1 = g2;
if( !g1 ) {
g1 = [];
groups.push(g1);
}
g1.push( onePair[0] );
// ensure that the idx properly reflects the location of the new item
idx[onePair[0]] = g1;
}
// now do the same for the second item... but g1 will never be null, so
// this case is a little simpler.
if( !g2 ) {
g2 = g1;
g2.push( onePair[1] );
idx[onePair[1]] = g2;
}
if( g1 != g2 ) {
// now, if they're not already the same group, then merge the two
// groups, and update the idx to reflect the merge.
for( var z:int=0; z<g2.length; ++z ) {
idx[g2[z]] = g1;
g1.push( g2[z] );
g2.splice(0);
}
}
}
groups will end up being an array of arrays, just like you asked for -- but there will be a few empty arrays that can be discarded. Just prune (or ignore) the empty ones, and you'll have your groups.
the basic idea here, is that idx provides a lookup table that indicates, throughout the indexing process, for any given item, which group it's in (if any). This allows us to determine whether an item has been encountered previously or not, and if so, to utilize it's existing group.
You can use an Object to keep the track of the association of a pair iten and a group, the key will be each item of your pair.
Here a litle snippet that make the works :
var pairs:Array=[];
pairs[0] = ['ItemA', 'ItemB'];
pairs[1] = ['ItemA', 'ItemC'];
pairs[2] = ['ItemC', 'ItemD'];
pairs[3] = ['ItemC', 'ItemE'];
pairs[4] = ['ItemF', 'ItemG'];
pairs[5] = ['ItemF', 'ItemH'];
// will contain group created
var groups:Array=[];
// will contain association between a pair item and a group
var pair2group:Object={};
// function that turn pairs into groups
function makeGroups(pairs:Array):void{
var pairLen:int = pairs.length;
for (var i:int=0;i<pairLen;i++){
var pair:Array = pairs[i];
var item1:String = pair[0];
var item2:String = pair[1];
var group:Array = pair2group[item1];
// is first pair item already in a group
if (group == null) {
// no so create a new group
group=[];
// create the association
pair2group[item1] = group;
// add the item to the group we have created
group.push(item1);
// add it to all the groups
groups.push(group);
}
// is the second pair item into a grouo
if (pair2group[item2] == null) {
// no so add it to the group where the first item belong
group.push(item2);
// create the association for the second item
pair2group[item2] = group;
}
}
}
// ---- test
makeGroups(pairs);
trace(groups.length);
trace(groups[0]);
trace(groups[1]);
After lots of playing around here's the solution I came up with.
This will take an 2D overlapArray that has pairs and produce a group list with unique values.
I used a in_array() function to duplicate PHP's handy function for finding if an item is already in an array.
for each(var pair:Array in overlapArray) {
var pairInGroup = false;
for each(var group:Array in overlapArrayGroups) {
if(in_array(pair[0],group) || in_array(pair[1],group)) {
if(!in_array(pair[0],group)) {
group.push(pair[0]);
}
if(!in_array(pair[1],group)) {
group.push(pair[1]);
}
pairInGroup = true;
}
}
if(!pairInGroup) {
overlapArrayGroups.push(pair);
}
}
The in_array() function:
public static function in_array( needle:String, haystack:Array ):Boolean {
for( var a = 0; a < haystack.length; a++ ) {
if( haystack[a] == needle ) {
return true;
} else if( haystack[a] is Array ) {
return in_array(needle, haystack[a]);
}
}
return false;
}