I am building a turn-based strategy game using Swift and Apple's SpriteKit and GameplayKit frameworks. I am attempting to use the GameplayKit-provided GKGridGraph data structure to represent the map grid and am having problems figuring out how to construct the GKGridGraph. If you build an array of GKGridGraphNodes, then add them to an empty GKGridGraph using the add() method, the grid graph is not aware of the grid, even though the nodes are "in" the graph. See sandbox code below:
override func sceneDidLoad() {
// Dimensions of our grid
let width: Int32 = 4
let height: Int32 = 4
// Create an empty graph
let graph = GKGridGraph()
// Create a mutable array to hold the nodes
var nodes: [GKGridGraphNode] = []
// Build up the array of empty graph nodes
for row in 0..<height {
for col in 0..<width {
let position = SIMD2<Int32>(Int32(col), Int32(row))
nodes.append(GKGridGraphNode(gridPosition: position))
}
}
// Add all the nodes
graph.add(nodes)
// Try to find each node in the grid.
// No nodes are found using node() method.
for row in 0..<height {
for col in 0..<width {
let pos = SIMD2<Int32>(Int32(col), Int32(row))
if let _ = graph.node(atGridPosition: pos) {
print("Found node at [\(row),\(col)]")
}
}
}
}
The GKGridGraph method node(atGridPosition) will not find any of the nodes in the grid. The only way I have found to construct a GKGridGraph in such a way that makes it aware of its own grid is to use the constructor:
init(fromGridStartingAt: vector_int2,
width: Int32,
height: Int32,
diagonalsAllowed: Bool,
nodeClass: AnyClass)
which does construct a grid-aware graph. However, the only way I have figured out (based on reading books, websites, and API documentation) how to remove the nodes that I don't want to be traversable is to use the GKGraph remove() method. However, I have found the remove method's performance to be atrocious, so it is not going to work for my purposes to build a "complete" grid graph, then go back and remove the nodes I don't want to be in the graph. I do not know why the performance of remove() is so bad (possibly having to sever all of the adjacent connections), but for a large grid it seems almost unusable.
I have also tried manually adding all the node-to-node connections after I added the array of nodes, and that has no effect on being able to use the node() method to retrieve a node at a specific grid position.
Does anyone have any idea how to do what I am trying to accomplish: fully construct a GKGridGraph using only the nodes initially that I want to be in the graph?
The remove method of GKGridGraph has terrible performance as you say, after few hours I found a fix that works
The main idea is to never use the remove method and instead only disconnect and reconnect manually the nodes.
Here is an example of a wallNode that you want to remove from the gridGraph
let wallNode = gridGraph.node(gridPosition: vector(Int32(column), Int32(row))
wallNode.removeConnections(to: wallNode.connectedNodes, bidirectionnal: true)
For your specific problem, you should just do this with all the nodes in the graph and then reconnect those you want
Related
I have the following formula: =ArrayFormula(INDEX(Items!F2:F,MATCH(C2,Items!E2:E,0)))
I would like to extend it such that the entire C column runs the same formula for values. Please help. If a script is necessary to achieve this, I'd like to explore that option too.
Use Apps Script!
Sheet functions (formulae) work great (especially if you are a master like player0), but I find it much easier to work within Apps Script for anything much more complicated than a simple INDEX MATCH. If you are willing to learn some JavaScript, I highly recommend learning some.
Custom Functions
You can write custom sheet functions in Apps Script that you can call with the traditional =FUNCTION() from a cell.
The way it works is that you write a function in Apps Script that returns a two dimensional array corresponding to the area that it needs to fill.
For example, if wanted a function to fill a 2 x 2 block with 1, you would need to make your function return:
[[1,1],[1,1]]
Or you can write it like this:
[
[1, 1],
[1, 1]
]
Implementing Index Match
There are many ways you can implement it, here is an example.
The example spreadsheet has 2 tabs, "Ledger" and "Items".
The goal of the function that follows is to get the costs of the items from the "Items" tab.
function ledgerIndexMatch(){
// Initializing the location of data
let ss = SpreadsheetApp.getActive();
let ledger = ss.getSheetByName("Ledger");
let source = ss.getSheetByName("Items");
let ledgerRange = ledger.getDataRange();
let sourceRange = source.getDataRange();
// Getting the values into a 2D array
let ledgerValues = ledgerRange.getValues();
let sourceValues = sourceRange.getValues();
// Discarding the first row (headers)
ledgerValues.shift();
sourceValues.shift();
// Initializing the output array
let output = [];
// This is where the INDEX MATCH happens
// For each row in ledger
ledgerValues.forEach(ledgerRow => {
// Get the second column (index 1)
let item = ledgerRow[1];
// Initialize the column
let value = [];
// For each row in the source
sourceValues.some(sourceRow => {
// Check if the item is there
if (item == sourceRow[0]) {
// if so, add to value
value.push(sourceRow[1]);
// stop looking for values
return true
// if not matched, keep looking
} else return false
})
// Add the found value (or blank if not found)
// to the output array.
output.push(value);
})
return output;
}
Which can be used like this:
Whats nice about Apps Script is that you can customize it to your heart's content. In this example, the function automatically detects the height of the respective tables, so you don't need to fiddle around with ranges.
You might want to extend this function with arguments so that its more flexible. Or you could just have a few versions of it for different operations, if you don't have too many. Or refactor it... its up to you.
References
Apps Script
Custom Functions
Tutorials
SpreadsheetApp
use:
=ARRAYFORMULA(IFNA(VLOOKUP(C2:C, Items!E2:F, 2, 0)))
I'm wanting to represent a directed network graph that is closed, e.g. where the edges would look like:
N1->N2
N2->N3
N3->N1
Where each node has a predefined number of edges allowed to connect to it. In this case that would be 2, no more, no less.
I could define a list of nodes and then a list of edges to create the network but I'm wondering if it is possible with F# to define my types in such a way that the compiler would throw an error if the graph was open or one of the nodes had edge connection <> 2?
I'm not sure this is possible to represent due to the restriction on cyclic dependencies.
I think this is close to what you want, although it doesn't enforce all the restrictions you mentioned:
type Node =
{
Name : string
RefersTo : Node
}
let rec graph =
{
Name = "N1"
RefersTo =
{
Name = "N2"
RefersTo =
{
Name = "N3"
RefersTo = graph
}
}
}
I am very new in Neo4j data base .
I am taking above site as a reference and try to create nodes of data storing and retrieving the nodes and there respective properties.
For Retrieving the nodes i am using following method :
ExecutionResult result = engine.execute(query,map);
Iterator<Object> columnAs = result.columnAs("n");
while(columnAs.hasNext())
{
Node n = (Node)columnAs.next();
for (String key : n.getPropertyKeys()) {
Sysout(key);
Sysout(n.getProperty(key));
}
}
For Executing above while loop it takes lots of time it takes almost 10 - 12 sec to traverse 28k nodes.
I am not sure whether I am following proper method or is there any other alternative for this.
Thanks in advance.
Essentially, I'm storing a directed graph of entities in CouchDB, and need to be able to find edges going IN and OUT of the graph.
SETUP:
The way the data is being stored right now is as follows. Each document represents a RELATION between two entities:
doc: {
entity1: { name: '' ... },
entity2: { name: '' ... }
...
}
I have a view which does a bunch of emits, two of which emit documents keyed on their entity1 component and on their entity2 component, so something like:
function() {
emit(['entity1', doc.entity1.name]);
emit(['entity2', doc.entity2.name]);
}
Edges are directed, and go from entity1 and entity2. So if I want to find edges going out of an entity, I just query the first emit; if I want edges going into an entity, I query the second emit.
PROBLEM:
The problem here lies in the fact that I also have the need to capture edges both going INTO and OUT OF entities. Is there a way I can group or reduce these two emits into a single bi-directional set of [x] UNIQUE pairs?
Is there a better way of organizing my view to promote this action?
It might be preferable to just create a second view. But there's nothing stopping you from cramming all sorts of different data into the same view like so:
function() {
if (doc.entity1.name == doc.entity2.name) {
emit(['self-ref', doc.entity1.name], 1);
}
emit(['both' [doc.entity1.name, doc.entity2.name]], 1);
emit(['either' [doc.entity1.name, "out"]], 1);
emit(['either' [doc.entity2.name, "in"]], 1);
emit(['out', doc.entity1.name], 1);
emit(['in', doc.entity2.name], 1);
}
Then you could easily do the following:
find all the self-ref's:
startkey=["self-ref"]&endkey=["self-ref", {}].
find all of the edges (incoming or outgoing) for a particular node:
startkey=["either", [nodeName]]&endkey=["either", [nodeName, {}]]
if you don't reduce this, then you'll still be preserving "in" vs "out" in the key. If you never need to query for all nodes with incoming or outgoing edges, then you can replace the last two emits with the "either" emits.
find all of the edges from node1 -> node2:
key=["both", [node1, node2]
as well as your original queries for incoming or outgoing for a particular node.
I'd recommend benchmarking your application's typical use cases before choosing between this combined view approach or a multi-view approach.
i'm using riak to store json documents right now, and i want to sort them based on some attribute, let's say there's a key, i.e
{
"someAttribute": "whatever",
"order": 1
}
so i want to sort the documents based on the "order".
I am currently retrieving the documents in riak with the erlang interface. i can retrieve the document back as a string, but i dont' really know what to do after that. i'm thinking the map function just reduces the json document itself, and in the reduce function, i'd make a check to see whether the item i'm looking at has a higher "order" than the head of the rest of the list, and if so append to beginning, and then return a lists:reverse.
despite my ideas above i've had zero results after almost an entire day, i'm so confused with the erlang interface in riak. can someone provide insight on how to write this map/reduce function, or just how to parse the json document?
As far as I know, You do not have access to Input list in Map. You emit from Map a document as 1 element list.
Inputs (all the docs to handle as {Bucket, Key}) -> Map (handle single doc) -> Reduce (whole list emitted from Map).
Maps are executed per each doc on many nodes whereas Reduce is done once on so called coordinator node (the one where query was called).
Solution:
Define Inputs (as a list or bucket)
Retrieve Value in Map and emit whole doc or {Id, Val_to_sort_by)
Sort in Reduce (using regular list:keysort)
This is not a map reduce solution but you should check out Riak Search.
so i "solved" the problem using javascript, still can't do it using erlang.
here is my query
{"inputs":"test",
"query":[{"map":{"language":"javascript",
"source":"function(value, keyData, arg){ var data = Riak.mapValuesJson(value)[0]; var obj = {}; obj[data.order] = data; return [ obj ];}"}},
{"reduce":{"language":"javascript",
"source":"function(values, arg){ return [ values.reduce(function(acc, item){ for(var order in item){ acc[order] = item[order]; } return acc; }) ];}",
"keep":true}}
]
}
so in the map phase, all i do is create a new array, obj, with the key as the order, and the value as the data itself. so visually, the obj is like this
{"1":{"firstName":"John","order":1}
in the reduce phase, i'm just putting it in the accumulator, so basically that's the sort if you think about it, because when you're done, everything will be put in order for you. so i put 2 json documents for testing, one is above, the ohter is just firstName: Billie, order 2. and here is my result for the query above
[{"1":{"firstName":"John","order":1},"2":{"firstName":"Billie","order":2}}]
so it works! . but i still need to do this in ERLANG, any insights?