Finding the shortest path between two points in 2D Array - search

I have a simple game, I'm trying to get the shortest route between 2 points
The map consists of 2d array matrix: Node[][],
class Node{
index: {
x: number,
y: number
},
isAvailable: boolean
}
The algorithm should return the shortest path with respect to node availability.
e.g. Trees are marked as unavailable node.isAvailable = false
I'm stuck on implementing the algorithm for this matrix
I tried to use Dijkstras algorithm from here, but I couldn't figure out how to apply it, I did
const graph = new Dijkstra();
//convert the matrix (2d array) to graph
matrix.map((row) => {
row.map((node: Node) => {
let x = node.index.x;
let y = node.index.y;
graph.addVertex(x + ":" + y, {x: x, y: y});
});
});
console.log(graph.shortestPath('0:0', '5:5'));
//the output was ['0:0'] (definitly not the answer)
How can I apply the algorithm on this matrix?
P.S here is my full code

I used a method best described as spilling paint at the target:
You mark the target square with 0, then traverse the neighbours and mark them as 1, which represents distance to target, then traverse neighbours of neighbours, etc. Repeat the process until the paint reaches your troll. All that is left for troll to do is just start moving to the squares with lowest potential.
It becomes more fun once you have multiple characters that need to path around each other while everyone is moving.

I had to implement the A* algorithm
export class PathFinder {
grid: Tile[][];
gridHeight: number;
gridWidth: number;
startTile: Tile;
endTile: Tile;
/** Array of the already checked tiles. */
closedList: List<Tile> = new List<Tile>();
openList: List<Tile> = new List<Tile>();
constructor(grid: Tile[][], gridHeight: number, gridWidth: number) {
this.grid = grid;
this.gridHeight = gridHeight;
this.gridWidth = gridWidth;
}
searchPath(start: Tile, end: Tile): Tile[] {
this.startTile = start;
this.endTile = end;
/** Path validation */
if (!start.walkable) {
console.log('The start tile in not walkable, choose different tile than', start.index);
return [];
}
if (!end.walkable) {
console.log('The end tile in not walkable, choose different tile than', end.index);
return [];
}
/** Start A* Algorithm */
/** Add the starting tile to the openList */
this.openList.push(start);
let currentTile;
/** While openList is not empty */
while (this.openList.length) {
//current node = node for open list with the lowest cost.
currentTile = this.getTileWithLowestTotal(this.openList);
//if the currentTile is the endTile, then we can stop searching
if(JSON.stringify(currentTile.index) === JSON.stringify(end.index)){
this.startTile.setBackgroundColor("rgba(255, 45, 45, .8)");
this.endTile.setBackgroundColor("rgba(255, 45, 45, .8)");
return this.shortestPath();
}
else {
//move the current tile to the closed list and remove it from the open list.
this.openList.remove(currentTile);
this.closedList.push(currentTile);
// //Get all adjacent Tiles
let adjacentTiles = this.getAdjacentTiles(currentTile);
for (let adjacentTile of adjacentTiles) {
//Get tile is not in the open list
if (!this.openList.contains(adjacentTile)) {
//Get tile is not in the closed list
if (!this.closedList.contains(adjacentTile)) {
//move it to the open list and calculate cost
this.openList.push(adjacentTile);
//calculate the cost
adjacentTile.cost = currentTile.cost + 1;
//calculate the manhattan distance
adjacentTile.heuristic = this.manhattanDistance(adjacentTile);
// calculate the total amount
adjacentTile.total = adjacentTile.cost + adjacentTile.heuristic;
currentTile.setBackgroundColor('rgba(0, 181, 93, 0.8)');
}
}
}
}
}
}
getTileWithLowestTotal(openList: Tile[]): Tile {
let tileWithLowestTotal = new Tile();
let lowestTotal: number = 999999999;
/** Search open tiles and get the tile with the lowest total cost */
for (let openTile of openList) {
if (openTile.total <= lowestTotal) {
//clone lowestTotal
lowestTotal = openTile.total;
tileWithLowestTotal = openTile;
}
}
return tileWithLowestTotal;
}
getAdjacentTiles(current: Tile): Tile[] {
let adjacentTiles: Tile[] = [];
let adjacentTile: Tile;
//Tile to left
if (current.index.x - 1 >= 0) {
adjacentTile = this.grid[current.index.x - 1][current.index.y];
if (adjacentTile && adjacentTile.walkable) {
adjacentTiles.push(adjacentTile);
}
}
//Tile to right
if (current.index.x + 1 < this.gridWidth) {
adjacentTile = this.grid[current.index.x + 1][current.index.y];
if (adjacentTile && adjacentTile.walkable) {
adjacentTiles.push(adjacentTile);
}
}
//Tile to Under
if (current.index.y + 1 < this.gridHeight) {
adjacentTile = this.grid[current.index.x][current.index.y + 1];
if (adjacentTile && adjacentTile.walkable) {
adjacentTiles.push(adjacentTile);
}
}
//Tile to Above
if (current.index.y - 1 >= 0) {
adjacentTile = this.grid[current.index.x][current.index.y - 1];
if (adjacentTile && adjacentTile.walkable) {
adjacentTiles.push(adjacentTile);
}
}
/** TODO: Diagonal moves */
return adjacentTiles;
}
/** Calculate the manhattan distance */
manhattanDistance(adjacentTile: Tile): number {
return Math.abs((this.endTile.index.x - adjacentTile.index.x) +
(this.endTile.index.y - adjacentTile.index.y));
}
shortestPath() {
let startFound: boolean = false;
let currentTile = this.endTile;
let pathTiles = [];
//includes the end tile in the path
pathTiles.push(this.endTile);
this.endTile.ball = true;
while (!startFound) {
let adjacentTiles = this.getAdjacentTiles(currentTile);
//check to see what newest current tile.
for (let adjacentTile of adjacentTiles) {
//check if it is the start tile
if (JSON.stringify(adjacentTile.index) === JSON.stringify(this.startTile.index)){
return pathTiles;
}
//it has to be inside the closedList or openList
if (this.closedList.contains(adjacentTile) || this.openList.contains(adjacentTile)) {
if (adjacentTile.cost <= currentTile.cost && adjacentTile.cost > 0) {
//change the current tile.
currentTile = adjacentTile;
//Add this adjacentTile to the path list
pathTiles.push(adjacentTile);
//highlight way with yellow balls
adjacentTile.ball = true;
break;
}
}
}
}
}
}

Related

How to find all words that "contain" or "unscramble" an input string using a Trie?

Here is a trie made to work on alphabets/scripts from any language.
class TrieNode {
constructor(key) {
// the "key" value will be the character in sequence
this.key = key;
// we keep a reference to parent
this.parent = null;
// we have hash of children
this.children = {};
// check to see if the node is at the end
this.end = false;
}
getWord() {
let output = [];
let node = this;
while (node !== null) {
output.unshift(node.key)
node = node.parent
}
return output.join('')
}
}
class Trie {
constructor() {
this.base = new TrieNode(null)
}
insert(word) {
let node = this.base
const points = Array.from(word)
for (const i in points) {
const point = points[i]
if (!node.children[point]) {
const child = node.children[point] = new TrieNode(point)
child.parent = node
}
node = node.children[point]
if (i == word.length - 1) {
node.end = true
}
}
}
contains(word) {
let node = this.base
const points = Array.from(word)
for (const i in points) {
const point = points[i]
if (node.children[point]) {
node = node.children[point]
} else {
return false
}
}
return node.end;
}
find(prefix) {
let node = this.base
let output = []
const points = Array.from(prefix)
for (const i in points) {
const point = points[i]
// make sure prefix actually has words
if (node.children[point]) {
node = node.children[point]
} else {
// there's none. just return it.
return output
}
}
const stack = [node]
while (stack.length) {
node = stack.shift()
// base case, if node is at a word, push to output
if (node.end) {
output.unshift(node.getWord())
}
// iterate through each children, call recursive findAllWords
for (var child in node.children) {
stack.push(node.children[child])
}
}
return output
}
}
After asking How to efficiently store 1 million words and query them by starts_with, contains, or ends_with? and getting some answers, I am wondering how the "contains" and "unscrambles" parts can be implemented as a trie. The prefix ("starts with") search is easily handled by the trie.
const fs = require('fs')
const Trie = require('./Trie')
const words = fs.readFileSync('tmp/scrabble.csv', 'utf-8')
.trim()
.split(/\n+/)
.map(x => x.trim())
const trie = new Trie()
words.forEach(word => trie.insert(word))
console.log(trie.find('zy'))
[
'zygodactylous', 'zygomorphies', 'zygapophysis',
'zygapophyses', 'zygomorphic', 'zymologies',
'zygospores', 'zygosities', 'zygomorphy',
'zygodactyl', 'zymurgies', 'zymograms',
'zymogenes', 'zygotenes', 'zygospore',
'zygomatic', 'zyzzyvas', 'zymosans',
'zymology', 'zymogram', 'zymogens',
'zymogene', 'zygotene', 'zygosity',
'zygomata', 'zyzzyva', 'zymurgy',
'zymotic', 'zymosis', 'zymoses',
'zymosan', 'zymogen', 'zymases',
'zygotic', 'zygotes', 'zygosis',
'zygoses', 'zygomas', 'zydecos',
'zymase', 'zygote', 'zygose',
'zygoma', 'zygoid', 'zydeco',
'zymes', 'zyme'
]
Here is the tmp/scrabble.csv I used.
The question is, can a trie be used to find all the words which "contain" some input string, or find all the words which "unscramble" the input string? I am curious how to accomplish this with a Trie. If a trie cannot do this efficiently, then knowing why not, and potentially what I should be looking at instead, will be a great answer as well.
From my initial thought-attempts at solving "contains", it seems I would have to create a trie which maps all possible combinations of substrings to the final word, but that seems like it will be an explosion of memory, so not sure how to better reason about this.
For "unscrambles", where you put in "caldku" and it finds "duck", amongst other possible words, I think possibly, similar to the linked answer to the other question, maybe sorting "duck" into "cdku", and then storing that in the trie, and then sorting the input to "acdklu", and then searching for "contains" using the previous algorithm, but hmm, no that will break in a few cases. So maybe a trie is not the right approach for these two problems? If it is, roughly how would you do it (you don't need to provide a full implementation, unless you'd like and it's straightforward enough).
Here is an answer to unscrambles that ALSO handles pagination. It actually returns [words, startOfNextPage]. So to find the next page you call it starting at startOfNextPage.
The idea is to normalize words by sorting the letters, and then storing the word in the trie once. When searching we can dig into the tree both for the next letter we want, and any letter before it (because the letter we want may come after). So we also store a lookup to know what letters might be found, so that we can break out early without having to look at a lot of bad options. And, of course, we include counts so that we can calculate how much of the trie we have explored and/or skipped over.
class Trie {
constructor(depth=0) {
this.depth = depth;
this.children = {};
this.words = [];
this.count = 0;
this.isSorted = false;
this.hasChildWith = {};
}
insert (word) {
if (word != undefined) {
this._insert(Array.from(word).sort(), word);
}
}
_insert (key, word) {
this.count++;
this.isSorted = false;
if (key.length == this.depth) {
this.words.push(word);
}
else {
for (let i = this.depth+1; i < key.length; i++) {
this.hasChildWith[key[i]] = true;
}
if (! this.children[key[this.depth]] ) {
this.children[key[this.depth]] = new Trie(this.depth+1);
}
this.children[key[this.depth]]._insert(key, word);
}
}
sort () {
if (! this.isSorted) {
this.words.sort();
let letters = Object.keys(this.children);
letters.sort();
// Keys come out of a hash in insertion order.
let orderedChildren = {};
for (let i = 0; i < letters.length; i++) {
orderedChildren[letters[i]] = this.children[letters[i]];
}
this.children = orderedChildren;
}
}
find (letters, startAt=0, maxCount=null) {
return this._find(Array.from(letters).sort(), 0, startAt, maxCount || this.count, 0);
}
_find (key, keyPos, startAt, maxCount, curPos) {
if (curPos + this.count < startAt) {
return [[], curPos + this.count];
}
this.sort(); // Make sure we are sorted.
let answer = [];
if (keyPos < key.length) {
// We have not yet found all the letters.
// tmpPos will track how much we looked at in detail
let tmpPos = curPos;
tmpPos += this.words.length;
for (const [k, v] of Object.entries(this.children)) {
if (k < key[keyPos]) {
// The next letter we want can be deeper in the trie?
if (v.hasChildWith[key[keyPos]]) {
// It is! Let's find it.
let result = v._find(key, keyPos, startAt, maxCount - answer.length, tmpPos);
answer = answer.concat(result[0]);
if (maxCount <= answer.length) {
// We finished our answer, return it and what we found.
return [answer, result[1]];
}
}
tmpPos += v.count; // Didn't find it, but track that we've seen this.
}
else if (k == key[keyPos]) {
// We found our next letter! Search deeper.
let result = v._find(key, keyPos+1, startAt, maxCount - answer.length, tmpPos);
answer = answer.concat(result[0]);
if (maxCount <= answer.length) {
// We finished the search.
return [answer, result[1]];
}
else {
// No other letter can match.
break;
}
}
else {
// Neither this or any later letter can match.
break;
}
}
// Return our partial answer and mark that we went
// through this whole node.
return [answer, curPos + this.count];
}
else {
// We have our letters and are recursively finding words.
if (startAt <= curPos + this.words.length) {
answer = this.words.slice(startAt - curPos);
if (maxCount <= answer.length) {
// No need to search deeper, we're done.
answer = answer.slice(0, maxCount);
return [answer, curPos + answer.length];
}
curPos += answer.length;
}
for (const child of Object.values(this.children)) {
let result = child._find(key, keyPos, startAt, maxCount - answer.length, curPos);
answer = answer.concat(result[0])
if (maxCount <= answer.length) {
return [answer, result[1]];
}
else {
curPos += child.count;
}
}
return [answer, curPos];
}
}
}
exports.Trie = Trie
And here is some code to test it with.
const fs = require('fs')
const AnagramTrie = require('./AnagramTrie')
const words = fs.readFileSync('tmp/scrabble.csv', 'utf-8')
.trim()
.split(/\n+/)
.map(x => x.trim())
const trie = new AnagramTrie.Trie()
words.forEach(word => trie.insert(word))
console.log(trie.find('b', 0, 12))
console.log(trie.find('b', 0, 6))
console.log(trie.find('b', 11, 6))
console.log(trie.find('zx', 0, 12))
console.log(trie.find('zx', 0, 6))
console.log(trie.find('zx', 60875, 6))
It will return words in the order of their sorted anagram, followed by the word itself. So if you search for admired you'll find unadmired first because the n in un comes before the r in admired. And you'll find that disembarrassed comes before either because it has 2 a's in it.
And here is an answer to contains. Note how, despite using a Trie both times, it needs a lot of customization to the problem we are solving.
Sample code to use it is like the other one. You can see the deduplication at work if you search for a. The answer aa (correctly) only comes up once. Also it is a little slower to start up because you have to put words into the data structure multiple times.
class Trie {
constructor(depth=0, key=[]) {
this.depth = depth;
this.children = {};
this.words = [];
this.count = 0;
this.isSorted = false;
this.path = key.slice(0, depth).join("");
this.char = key[this.depth-1];
}
insert (word) {
if (word != undefined) {
const key = Array.from(word);
for (let i = 0; i < key.length; i++) {
this._insert(key.slice(i), word);
}
}
}
_insert (key, word) {
this.count++;
if (this.depth == key.length) {
this.words.push(word);
}
else {
if (! this.children[key[this.depth]] ) {
this.children[key[this.depth]] = new Trie(this.depth+1, key);
}
this.children[key[this.depth]]._insert(key, word);
}
}
sort () {
if (! this.isSorted) {
this.words.sort();
let letters = Object.keys(this.children);
letters.sort();
// Keys come out of a hash in insertion order.
let orderedChildren = {};
for (let i = 0; i < letters.length; i++) {
orderedChildren[letters[i]] = this.children[letters[i]];
}
this.children = orderedChildren;
this.isSorted
}
}
find (letters, startAt=0, maxCount=null) {
// Defaults, special cases, etc.
if (this.count <= startAt) {
return [[], startAt];
}
if (maxCount == null) {
maxCount = this.count;
}
if (letters == "") {
// Special case.
this.sort();
answer = this.words.slice(startAt, startAt + maxCount);
return [answer, startAt + answer.length];
}
// We will do the recursive search.
const key = Array.from(letters);
// The challenge is that each word is stored multiple times.
// We want to only find them once. So we will stop searching as soon
// as we match our search string twice.
//
// This requires keeping track of where we are in trying to match our
// search string again. And if we fail, backtracking in our attempt
// to match.
//
// Here is the complication. Partial matches can overlap.
//
// Consider the following search string:
//
// search: a b a a b a c a b
// 0 1 2 3 4 5 6 7 8
//
// Now suppose that I'm looking for "c" at 6 next and it isn't an
// "c". Well I know I matched "aba" and should next check if I got
// character 3, "a". We can encode this logic in an array.
//
// a b a a b a c a b
// backtrackTo: [-1, 0,-1, 1, 0,-1, 3,-1, 0]
//
// So if I've matched through 5, I check "c", then "a", then "b"
// before deciding that I'm not still partway through a match.
//
// Let's calculate that backtrackTo. We will also calculate a
// variable, matchAtAtEnd. Which is how long our longest
// running match is at a full match.
// We start by assuming that we go back to the beginning.
let backtrackTo = [-1];
let rematches = [];
let matchAtEnd = 0;
for (let i = 1; i < key.length; i++) {
if (key[i] == key[0]) {
backtrackTo[i] = -1;
rematches.push(i);
}
else {
backtrackTo[i] = 0;
}
}
// In our example we have:
//
// backtrackTo = [-1, 0, -1, -1, 0, -1, 0, -1, 0]
// rematches = [2, 3, 5, 7]
//
// Now let `k` be the length of the current rematch.
let k = 1;
while (0 < rematches.length) {
let nextRematches = [];
for (let i = 0; i < rematches.length; i++) {
if (i + k == key.length) {
matchAtEnd = k-1;
}
else {
if (key[k] == key[i+k]) {
nextRematches.push(i);
}
else {
backtrackTo[i+k] = k;
}
}
}
rematches = nextRematches;
k++;
// In our example we get:
//
// k = 1
// backtrackTo = [-1, 0, -1, -1, 0, -1, 0, -1, 0]
// rematches = [2, 3, 5, 7]
// matchAtEnd = -1
//
// k = 2
// backtrackTo = [-1, 0, -1, 1, 0, -1, 0, -1, 0]
// rematches = [3, 7]
// matchAtEnd = -1
//
// k = 3
// backtrackTo = [-1, 0, -1, 1, 0, -1, 3, -1, 0]
// rematches = []
// matchAtEnd = 2
//
// and we see that we got the expected backtrackTo AND
// we have recorded the fact that at matching
// abaabacab we are currently also matching the ab at the
// start.
//
// Now let's find the first match.
let node = this;
for (let i = 0; i < key.length; i++) {
node = node.children[key[i]];
if (!node) {
return [[], startAt];
}
}
return node._find(key, startAt, maxCount, 0, backtrackTo, matchAtEnd)
}
_find (key, startAt, maxCount, curPos, backtrackTo, nextMatch) {
// console.log([key, startAt, maxCount, curPos, backtrackTo, nextMatch, [this.path, this.count]]);
// Skip me?
if ((curPos + this.count <= startAt) || (key.length <= nextMatch)) {
return [[], curPos + this.count];
}
this.sort();
let answer = [];
if (curPos < startAt) {
if (startAt < curPos + this.words.length) {
answer = this.words.slice(startAt-curPos, startAt-curPos+maxCount);
}
else {
curPos += this.words.length; // Count the words we are skipping.
}
}
else {
answer = this.words.slice(0, maxCount);
}
curPos += answer.length;
if (maxCount <= answer.length) {
return [answer, curPos];
}
for (const [k, v] of Object.entries(this.children)) {
let thisMatch = nextMatch;
while ((-1 < thisMatch) && (key[thisMatch] != k)) {
thisMatch = backtrackTo[thisMatch];
}
thisMatch++;
let partialAnswer = null;
[partialAnswer, curPos] = v._find(key, startAt, maxCount - answer.length, curPos, backtrackTo, thisMatch);
answer = answer.concat(partialAnswer);
if (maxCount <= answer.length) {
break; // We are done.
}
}
return [answer, curPos];
}
}
exports.Trie = Trie

Collision of frames P2

I'm trying to determine the points of the players based on the different frame that a bullet hits, is this possible? So basically I want to for example, give 100 points if figurapega matches figura, and 50 points if it does not, but I have not been please my code below.
///////Here I load the atlas
this.load.atlas('Monsters', 'asset/game1/Monstruos/monstruos.png', 'asset/game1/Monstruos/monstruos.json');
///this is the one I want to use as reference to compare
createFiguraCompare: function(){
//Figura para comparar
this.figuritaspega = this.game.add.sprite(800, 140, 'Monsters', this.rnd.integerInRange(0,4));
this.figuritaspega.scale.set(0.5, 0.5 );
},
/////////and this is the one generating sprites that need to be shot at
makeOneFigura: function() {
this.figura = this.game.add.group();
this.figura.enableBody = true;
this.figura.physicsBodyType = Phaser.Physics.P2JS;
// for (var i = 0; i < 5; i++){
this.figura.createMultiple(100, 'Monsters', 0, false);
// }
this.figura.setAll('anchor.x', 0.5);
this.figura.setAll('anchor.y', 0.5);
this.figura.setAll('outOfBoundsKill', true);
this.figura.setAll('checkWorldBounds', true);
},
makeFiguras: function(x, y){
if (this.timerFiguras) {
this.figuras = this.figura.getFirstExists(false);
if (this.figuras) {
this.figuras.reset(0, 350);
this.figuras.frame = this.game.rnd.integerInRange(0,4);
this.figuras.body.setCollisionGroup(this.figuraCG);
this.figuras.body.collides(this.bulletCG);
this.figuras.body.velocity.x = 1000;
}
};
},
/////and last but no least the collision handler which is where Im trying to compare but with no luck
collisionBulletFigura: function(bullet, figuras, score, scoreText, figuritaspega) {
if (this.figuras.frame === this.figuritaspega.frame){
figuras.sprite.kill();
bullet.sprite.kill();
this.score += 100;
this.scoreText.text = this.score;
}else {
figuras.sprite.kill();
bullet.sprite.kill()
this.score += 50;
this.scoreText.text = this.score;
}
},

NSCollectionViewFlowLayout - left alignment

NSCollectionViewFlowLayout produces a layout with items justified on the right margin or, if the container is only wide enough for one item, centres items. I was expecting an alignment option, e.g. on the delegate, but am not finding anything in the docs. Does it require subclassing NSCollectionViewFlowLayout to achieve this?
Here is a subclass that produces a left justified flow layout:
class LeftFlowLayout: NSCollectionViewFlowLayout {
override func layoutAttributesForElementsInRect(rect: CGRect) -> [NSCollectionViewLayoutAttributes] {
let defaultAttributes = super.layoutAttributesForElementsInRect(rect)
if defaultAttributes.isEmpty {
// we rely on 0th element being present,
// bail if missing (when there's no work to do anyway)
return defaultAttributes
}
var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()
var xCursor = self.sectionInset.left // left margin
// if/when there is a new row, we want to start at left margin
// the default FlowLayout will sometimes centre items,
// i.e. new rows do not always start at the left edge
var lastYPosition = defaultAttributes[0].frame.origin.y
for attributes in defaultAttributes {
if attributes.frame.origin.y > lastYPosition {
// we have changed line
xCursor = self.sectionInset.left
lastYPosition = attributes.frame.origin.y
}
attributes.frame.origin.x = xCursor
// by using the minimumInterimitemSpacing we no we'll never go
// beyond the right margin, so no further checks are required
xCursor += attributes.frame.size.width + minimumInteritemSpacing
leftAlignedAttributes.append(attributes)
}
return leftAlignedAttributes
}
}
#Obliquely's answer fails when the collectionViewItems are not uniform in height. Here is their code modified to handle non-uniformly-sized items in Swift 4.2:
class CollectionViewLeftFlowLayout: NSCollectionViewFlowLayout
{
override func layoutAttributesForElements(in rect: CGRect) -> [NSCollectionViewLayoutAttributes]
{
let defaultAttributes = super.layoutAttributesForElements(in: rect)
if defaultAttributes.isEmpty {
return defaultAttributes
}
var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()
var xCursor = self.sectionInset.left // left margin
var lastYPosition = defaultAttributes[0].frame.origin.y // if/when there is a new row, we want to start at left margin
var lastItemHeight = defaultAttributes[0].frame.size.height
for attributes in defaultAttributes
{
// copy() Needed to avoid warning from CollectionView that cached values are mismatched
guard let newAttributes = attributes.copy() as? NSCollectionViewLayoutAttributes else {
continue;
}
if newAttributes.frame.origin.y > (lastYPosition + lastItemHeight)
{
// We have started a new row
xCursor = self.sectionInset.left
lastYPosition = newAttributes.frame.origin.y
}
newAttributes.frame.origin.x = xCursor
xCursor += newAttributes.frame.size.width + minimumInteritemSpacing
lastItemHeight = newAttributes.frame.size.height
leftAlignedAttributes.append(newAttributes)
}
return leftAlignedAttributes
}
}
A shorter solution for swift 4.2:
class CollectionViewLeftFlowLayout: NSCollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: NSRect) -> [NSCollectionViewLayoutAttributes] {
let attributes = super.layoutAttributesForElements(in: rect)
if attributes.isEmpty { return attributes }
var leftMargin = sectionInset.left
var lastYPosition = attributes[0].frame.maxY
for itemAttributes in attributes {
if itemAttributes.frame.origin.y > lastYPosition { // NewLine
leftMargin = sectionInset.left
}
itemAttributes.frame.origin.x = leftMargin
leftMargin += itemAttributes.frame.width + minimumInteritemSpacing
lastYPosition = itemAttributes.frame.maxY
}
return attributes
}
}
In case your items have the same width...
In the other delegate method, you should change the frame of the NSCollectionViewLayoutAttributes
- (NSCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSCollectionViewLayoutAttributes *attributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
NSRect modifiedFrame = [attributes frame];
modifiedFrame.origin.x = floor(modifiedFrame.origin.x / (modifiedFrame.size.width + [self minimumInteritemSpacing])) * (modifiedFrame.size.width + [self minimumInteritemSpacing]);
[attributes setFrame:modifiedFrame];
return [attributes autorelease];
}

Move the sprite but don't collide with other sprites in the scene in Phaser

I'm referring to the official example on Phaser.io site, but have copied it here for reference below. What I want, and repeatedly fail to achieve is that the moving (with keyboard keys) starfield sprite would not collide with other vegies sprites.
I did go through the docs and looked here on SO and their forum, and it seems that the solutions should be easy enough; to just put the following code in the update() function:
game.world.bringToTop(sprite);
But, for some reason this is not working for me, so please tell me what I'm doing wrong.
var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update });
function preload() {
game.load.image('sky', 'assets/skies/sky4.png');
game.load.image('starfield', 'assets/misc/starfield.jpg');
game.load.spritesheet('veggies', 'assets/sprites/fruitnveg64wh37.png', 64, 64);
}
var sprite;
var cursors;
var veggies;
function create() {
game.add.image(0, 0, 'sky');
// Enable p2 physics
game.physics.startSystem(Phaser.Physics.P2JS);
// Make things a bit more bouncey
game.physics.p2.defaultRestitution = 0.8;
// Add a sprite
sprite = game.add.tileSprite(300, 450, 200, 50, 'starfield');
// Enable if for physics. This creates a default rectangular body.
game.physics.p2.enable(sprite);
veggies = game.add.group();
veggies.enableBody = true;
veggies.physicsBodyType = Phaser.Physics.P2JS;
var vegFrames = [ 1, 3, 4, 8 ];
for (var i = 0; i < 10; i++)
{
var veg = veggies.create(game.world.randomX, game.world.randomY, 'veggies', game.rnd.pick(vegFrames));
veg.body.setCircle(26);
}
text = game.add.text(20, 20, 'move with arrow keys', { fill: '#ffffff' });
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
sprite.body.setZeroVelocity();
game.world.bringToTop(veggies);
if (cursors.left.isDown)
{
sprite.body.moveLeft(400);
sprite.tilePosition.x -= 8;
}
else if (cursors.right.isDown)
{
sprite.body.moveRight(400);
sprite.tilePosition.x += 8;
}
if (cursors.up.isDown)
{
sprite.body.moveUp(400);
sprite.tilePosition.y -= 8;
}
else if (cursors.down.isDown)
{
sprite.body.moveDown(400);
sprite.tilePosition.y += 8;
}
}
edit: Solution which worked in the end thanks to SirSandman's answer:
var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });
function preload() {
game.load.image('stars', 'assets/misc/starfield.jpg');
game.load.spritesheet('ship', 'assets/sprites/humstar.png', 32, 32);
game.load.image('panda', 'assets/sprites/spinObj_01.png');
game.load.image('sweet', 'assets/sprites/spinObj_06.png');
}
var ship;
var starfield;
var cursors;
function create() {
// Enable P2
game.physics.startSystem(Phaser.Physics.P2JS);
// Turn on impact events for the world, without this we get no collision callbacks
game.physics.p2.setImpactEvents(true);
game.physics.p2.restitution = 0.8;
// Create our collision groups. One for the player, one for the pandas
var playerCollisionGroup = game.physics.p2.createCollisionGroup();
var pandaCollisionGroup = game.physics.p2.createCollisionGroup();
// This part is vital if you want the objects with their own collision groups to still collide with the world bounds
// (which we do) - what this does is adjust the bounds to use its own collision group.
game.physics.p2.updateBoundsCollisionGroup();
starfield = game.add.tileSprite(0, 0, 800, 600, 'stars');
starfield.fixedToCamera = true;
var pandas = game.add.group();
pandas.enableBody = true;
pandas.physicsBodyType = Phaser.Physics.P2JS;
for (var i = 0; i < 4; i++)
{
var panda = pandas.create(game.world.randomX, game.world.randomY, 'panda');
panda.body.setRectangle(40, 40);
// Tell the panda to use the pandaCollisionGroup
panda.body.setCollisionGroup(pandaCollisionGroup);
// Pandas will collide against themselves and the player
// If you don't set this they'll not collide with anything.
// The first parameter is either an array or a single collision group.
panda.body.collides(pandaCollisionGroup);
panda.body.velocity.x = 500;
panda.body.velocity.y = 500;
}
// Create our ship sprite
ship = game.add.sprite(200, 200, 'ship');
ship.scale.set(2);
ship.smoothed = false;
ship.animations.add('fly', [0,1,2,3,4,5], 10, true);
ship.play('fly');
game.physics.p2.enable(ship, false);
ship.body.setCircle(28);
ship.body.fixedRotation = true;
// Set the ships collision group
ship.body.setCollisionGroup(playerCollisionGroup);
// The ship will collide with the pandas, and when it strikes one the hitPanda callback will fire, causing it to alpha out a bit
// When pandas collide with each other, nothing happens to them.
game.camera.follow(ship);
cursors = game.input.keyboard.createCursorKeys();
}
function hitPanda(body1, body2) {
// body1 is the space ship (as it's the body that owns the callback)
// body2 is the body it impacted with, in this case our panda
// As body2 is a Phaser.Physics.P2.Body object, you access its own (the sprite) via the sprite property:
body2.sprite.alpha -= 0.1;
}
function update() {
ship.body.setZeroVelocity();
if (cursors.left.isDown)
{
ship.body.moveLeft(200);
}
else if (cursors.right.isDown)
{
ship.body.moveRight(200);
}
if (cursors.up.isDown)
{
ship.body.moveUp(200);
}
else if (cursors.down.isDown)
{
ship.body.moveDown(200);
}
if (!game.camera.atLimit.x)
{
starfield.tilePosition.x += (ship.body.velocity.x * 16) * game.time.physicsElapsed;
}
if (!game.camera.atLimit.y)
{
starfield.tilePosition.y += (ship.body.velocity.y * 16) * game.time.physicsElapsed;
}
}
function render() {
game.debug.text('Collide with the Pandas!', 32, 32);
}
I P2 you have to set the Collisiongroups in contrast to arcarde.
I think you have to set a collisiongroup for the sprite like that:
var veggCollisionGroup = game.physics.p2.createCollisionGroup();
and then define with which other groups this group shell collide like that in the Loop:
veggies.body.setCollisionGroup(veggCollisionGroup);
veggies.body.collides(veggCollisionGroup);
And then the your tilesprite should collide with your other tilesprites.
Source:
http://phaser.io/examples/v2/p2-physics/collision-groups
if i should be wrong you will find your answer in the examples. :)

Function failing to return a value

So I'm making a simple steganography tool (encrypting messages within images) and exposing it as a web service via Node.js. I am very new to Javascript and Node.js in particular. The app first converts a text string into a binary string by changing each character into an 8-bit ASCII encoding, resulting in one large binary string. I then encrypt the message within the pixels. Even values of pixels represent 0s from the binary, and odd values represent 1s. The end of the string is marked as 3 pixels of value 100 in a row (this is temporary, until I figure out a better way to mark the end). I'm using a node.js library called 'pngjs' that gives me pixel-level access to png images.
So I have a problem with the decodeMessage function. It builds up the string message, and is then meant to return it, however the return call at the end results in undefined.
How can I fix it?
Thanks in advance for the help!
function encodeMessage(image, mes) {
var message = mes;
var fs = require('fs'),
PNG = require('pngjs').PNG;
fs.createReadStream(image)
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x);// << 2;
//console.log(idx);
if (idx < message.length) {
var item = message.charAt(idx);
/* if the character in the encoded string is 0 */
if (item == 0) {
/* if the pixel is odd, we want it to be even */
if (this.data[idx] % 2 == 1) {
/* if the pixel is 0, add 1 to it */
if (this.data[idx] == 0) {
this.data[idx] = this.data[idx] + 1;
} else {
/* else substract 1 */
this.data[idx] = this.data[idx] - 1;
}
}
} else {
/* if the character in the encoded string is 1 */
if (this.data[idx] % 2 == 0) {
if (this.data[idx] == 0) {
this.data[idx] = this.data[idx] + 1;
} else {
this.data[idx] = this.data[idx] - 1;
}
}
}
//console.log(this.data[idx]);
} else if (idx === message.length) {
/* do something to the first pixel following the end of the string */
this.data[idx] = 100;
this.data[idx+1] = 100;
this.data[idx+2] = 100;
//console.log(this.data[idx]);
} else {
/* do something to the remaining pixels */
}
}
}
this.pack().pipe(fs.createWriteStream('encoded_' + image));
});
}
function decodeMessage(image) {
var message = "";
var fs = require('fs'),
PNG = require('pngjs').PNG;
fs.createReadStream(image)
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
dance:
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x);// << 2;
if (this.data[idx] == 100 && this.data[idx+1] == 100 && this.data[idx+2] == 100) {
break dance;
} else {
if (this.data[idx] % 2 == 0) {
message += "0";
} else {
message += "1";
}
}
}
}
/* the message outputs correctly over here */
console.log(message);
//return message;
});
/* but the return of the variable here doesn't work */
return message;
}
exports.encodeMessage = encodeMessage;
exports.decodeMessage = decodeMessage;
The parsed event is fired asynchronously, so you cannot return a value from decodeMessage.
function decodeMessage(image, cb) {
// Code
.on('parsed', function() {
// Code
console.log(message);
cb(message);
});
}
Then you must pass a callback to your decodeMessage function.
decodeMessage(image, function(decoded){
// Here is the decoded data.
});
The same is true for your encodeMessage function. The function will return before encoding has finished. If you want to know when it is done, you need to pass a callback the same way.

Resources