Can't get Jacobi algorithm to work in Objective-C - objective-c

For some reason, I can't get this program to work. I've had other CS majors look at it and they can't figure it out either.
This program performs the Jacobi algorithm (you can see step-by-step instructions and a MATLAB implementation here). BTW, it's different from the Wikipedia article of the same name.
Since NSArray is one-dimensional, I added a method that makes it act like a two-dimensional C array. After running the Jacobi algorithm many times, the diagonal entries in the NSArray (i[0][0], i[1][1], etc.) are supposed to get bigger and the others approach 0. For some reason though, they all increase exponentially. For instance, i[2][4] should equal 0.0000009, not 9999999, while i[2][2] should be big.
Thanks,
Chris
NSArray+Matrix.m
#implementation NSArray (Matrix)
#dynamic offValue, transposed;
- (double)offValue {
double sum = 0.0;
for ( MatrixItem *item in self )
if ( item.nonDiagonal )
sum += pow( item.value, 2.0 );
return sum;
}
- (NSMutableArray *)transposed {
NSMutableArray *transpose = [[[NSMutableArray alloc] init] autorelease];
int i, j;
for ( i = 0; i < 5; i++ ) {
for ( j = 0; j < 5; j++ ) {
[transpose addObject:[self objectAtRow:j andColumn:i]];
}
}
return transpose;
}
- (id)objectAtRow:(NSUInteger)row andColumn:(NSUInteger)column {
NSUInteger index = 5 * row + column;
return [self objectAtIndex:index];
}
- (NSMutableArray *)multiplyWithMatrix:(NSArray *)array {
NSMutableArray *result = [[NSMutableArray alloc] init];
int i = 0, j = 0, k = 0;
double value;
for ( i = 0; i < 5; i++ ) {
for ( j = 0; j < 5; j++ ) {
value = 0.0; // (JeremyP's answer)
for ( k = 0; k < 5; k++ ) {
MatrixItem *firstItem = [self objectAtRow:i andColumn:k];
MatrixItem *secondItem = [array objectAtRow:k andColumn:j];
value += firstItem.value * secondItem.value;
}
MatrixItem *item = [[MatrixItem alloc] initWithValue:value];
item.row = i;
item.column = j;
[result addObject:item];
}
}
return result;
}
#end
Jacobi_AlgorithmAppDelegate.m
// ...
- (void)jacobiAlgorithmWithEntry:(MatrixItem *)entry {
MatrixItem *b11 = [matrix objectAtRow:entry.row andColumn:entry.row];
MatrixItem *b22 = [matrix objectAtRow:entry.column andColumn:entry.column];
double muPlus = ( b22.value + b11.value ) / 2.0;
muPlus += sqrt( pow((b22.value - b11.value), 2.0) + 4.0 * pow(entry.value, 2.0) );
Vector *u1 = [[[Vector alloc] initWithX:(-1.0 * entry.value) andY:(b11.value - muPlus)] autorelease];
[u1 normalize];
Vector *u2 = [[[Vector alloc] initWithX:-u1.y andY:u1.x] autorelease];
NSMutableArray *g = [[[NSMutableArray alloc] init] autorelease];
for ( int i = 0; i <= 24; i++ ) {
MatrixItem *item = [[[MatrixItem alloc] init] autorelease];
if ( i == 6*entry.row )
item.value = u1.x;
else if ( i == 6*entry.column )
item.value = u2.y;
else if ( i == ( 5*entry.row + entry.column ) || i == ( 5*entry.column + entry.row ) )
item.value = u1.y;
else if ( i % 6 == 0 )
item.value = 1.0;
else
item.value = 0.0;
[g addObject:item];
}
NSMutableArray *firstResult = [[g.transposed multiplyWithMatrix:matrix] autorelease];
matrix = [firstResult multiplyWithMatrix:g];
}
// ...

Have you got any unit tests for your matrix category? I mean, are you certain that the multiplication algorithm works? I would say that initialising value to 0 happens in the wrong loop. I think you need to do it inside the j loop.
A couple of other observations:
You don't need the #dynamic property declaration because you are defining the implementation of the properties yourself.
Consider creating your own Matrix class that wraps a normal C array of doubles. You might find the implementation a bit simpler.

When you add the square root term to muPlus, you don't divide by two. The calculation should be either:
double muPlus = ( b22.value + b11.value ) / 2.0;
muPlus += sqrt( pow((b22.value - b11.value), 2.0)
+ 4.0 * pow(entry.value, 2.0)
) / 2.0;
or:
double muPlus = ( b22.value + b11.value );
muPlus += sqrt( pow((b22.value - b11.value), 2.0)
+ 4.0 * pow(entry.value, 2.0) );
muPlus /= 2.0;
Also, you assign u1.y to both Gr,c and Gc,r. From the algorithm description, you want Gr,c=U1,2 (or u1.y) and Gc,r=U2,1 (or u2.x). Note that you don't actually need u2; you can substitute -u1.y for u2.x and u1.x for u2.y.
Off-Topic
According to the Fundamental Rule of Cocoa Memory Management, -[NSArray multiplyWithMatrix:] should return an autoreleased array, since the multiplicand should relinquish ownership. Also, you should use accessors to assign GT * A * G to matrix rather than doing it directly so that it can be properly managed.
Since most of the tests in the loop to fill out g will be false during each iteration, it's most likely more efficient to fill g with some default values and then update g. You could create a zero matrix, then set the diagonal to ones, then fill in the values from U, or you could create an identity matrix (leave the i%6 == 0 test in the loop) then fill in the values from U. Profile each of the three approaches.

Related

Why is my collision prediction not detecting any collisions?

Bullet objects are created with .l for location and .vel for velocity. I'm trying with a large radius of 30 and still they never collide.
- (void)bulletsFired:(NSArray *)bullets li:(int)li {
[self playBulletSound:li];
for (Bullet *b in bullets) {
[self addChild:b];
b.tag = -1;
b.shotNumber = shotsFired;
}
for (Bullet *b in bullets) {
for (Bullet *bb in self.bullets) {
float timeOfApproach = TimeOfClosestApproach(b.l, b.vel, bb.l, bb.vel, 30, 30);
if (timeOfApproach > 0) {
NSLog(#"time of : %f", timeOfApproach);
NSString *keyName = [self.collisions objectForKey:[self keyNameForTime:(int)timeOfApproach]];
NSMutableArray *timedCollisions = [self.collisions objectForKey:keyName];
if (timedCollisions == nil) {
NSMutableArray *newCollisions = [NSMutableArray array];
[self.collisions setObject:newCollisions forKey:keyName];
}
NSDictionary *collision = #{#"b1" : [NSString stringWithFormat:#"%d", bb.shotNumber], #"b2" : [NSString stringWithFormat:#"%d", b.shotNumber]};
[timedCollisions addObject:collision];
}
}
}
[self.bullets addObjectsFromArray:bullets];
[self.scoreCycler score:1];
}
I check for timeOfClosestApproach with this function:
float TimeOfClosestApproach(CGPoint Pa, CGPoint Pb, CGPoint Va, CGPoint Vb, float Ra, float Rb) {
CGPoint Pab = ccpSub(Pa, Pb);
CGPoint Vab = ccpSub(Va, Vb);
float a = ccpDot(Vab, Vab);
float b = 2 * ccpDot(Pab, Vab);
float c = ccpDot(Pab, Pab) - (Ra + Rb) * (Ra + Rb);
// The quadratic discriminant.
float discriminant = b * b - 4 * a * c;
// Case 1:
// If the discriminant is negative, then there are no real roots, so there is no collision. The time of
// closest approach is then given by the average of the imaginary roots, which is: t = -b / 2a
float t;
if (discriminant < 0) {
t = -b / (2 * a);
return -1;
} else {
// Case 2 and 3:
// If the discriminant is zero, then there is exactly one real root, meaning that the circles just grazed each other. If the
// discriminant is positive, then there are two real roots, meaning that the circles penetrate each other. In that case, the
// smallest of the two roots is the initial time of impact. We handle these two cases identically.
float t0 = (-b + (float)sqrt(discriminant)) / (2 * a);
float t1 = (-b - (float)sqrt(discriminant)) / (2 * a);
t = min(t0, t1);
// We also have to check if the time to impact is negative. If it is negative, then that means that the collision
// occured in the past. Since we're only concerned about future events, we say that no collision occurs if t < 0.
if (t < 0) {
return -1;
} else {
}
}
// Finally, if the time is negative, then set it to zero, because, again, we want this function to respond only to future events.
if (t < 0) {
t = 0;
}
return t;
}
I keep getting -1 returned, and the bullets are never predicted to collide, even when they should based on my eyesight.
if (t < 0) {
return -1;
} else {
}
Keeps getting triggered.
What's wrong with my timeOfClosestApproach function?

CLLocation becomes nan value for coordinate.latitude

I have a view controller which is modally presented that displays a map view. So the map view works correctly with the following code, BUT there are 2 unused variables (long doubles x1, x2) and when I remove them the CLLocation always returns a nan value for the coordinate.latitude the third time the view controller is presented. temp1 value will be nan the third time and from then on.
WHY do I need these 2 unused variables?????, this is my question.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
navigationItemTitle.prompt = name;
if ([mapDict count] > 0)
{
id val = nil;
NSArray *values = [mapDict allValues];
long double x1, x2, temp1, temp2; //x1, x2 unused but necessary
for (int i = 0; i < [mapDict count]; i++)
{
val = [values objectAtIndex:i];
temp1 += ((CLLocation *)val).coordinate.latitude;
temp2 += ((CLLocation *)val).coordinate.longitude;
}
temp1 /= [values count];
temp2 /= [values count];
//NSLog(#"%Lf", temp1);
//NSLog(#"%Lf", temp2);
CLLocationCoordinate2D centerCooordinate = CLLocationCoordinate2DMake(temp1, temp2);
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(centerCooordinate, 10000000, 10000000);
[mapView setRegion:[mapView regionThatFits:region]];
for(id key in mapDict)
{
id value = [mapDict objectForKey:key];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = ((CLLocation *)value).coordinate;
point.title = key;
point.subtitle = [NSString stringWithFormat:#"%f\t%f", point.coordinate.latitude, point.coordinate.longitude];
[mapView addAnnotation:point];
}
}
}
The fact that the temp1 value is NaN only from the "third time" the view controller is presented is a coincidence.
Unlike instance variables, local variables are not initialized to any default values.
If you don't initialize them, they will have "random" values based on whatever is in their memory location at the time.
By declaring two "unused" variables, you're just changing the memory location of temp1 and temp2 which results in them taking slightly different default values.
To avoid this unpredictability, you should always initialize local variables.
Add these two lines before the for loop that calculates temp1 and temp2:
temp1 = 0.0;
temp2 = 0.0;
By the way, since iOS 7, you can avoid manually calculating the center coordinate by just calling showAnnotations after adding all the annotations.

extracting variable from one method to another method in the same .m file in objective c, xcode

Hello I would like to know how to use a variable from this method
+ (NSString *) yourCalculation:(NSString *)height:(NSString *)weight{
double bmiVal = 0;
if (weight > 0 && height > 0) {
CGFloat wInPounds = [weight floatValue];
CGFloat hInInches = [height floatValue];
CGFloat hInCms = hInInches *0.393700787;
}
}
in this method
+(NSString *) actualCalculation{
float val = wInPounds/(hInCms*hInCms);
float bmiVal = val *703;
}
This is only a small segment of the code, but it gets across what I want to do with it.
If anyone can tell me how to do this, I would appreciate it.
Thanking You
Create a custom class that has properties for the various values you want to share and return an instance of that. For example, assuming a MyNumerics class with the obvious properties:
+ (MyNumerics *) yourCalculation:(NSString *)height weight:(NSString *)weight {
MyNumerics *result = nil;
double bmiVal = 0;
if (weight > 0 && height > 0) {
result = [[MyNumerics alloc] init];
result.wInPounds = [weight floatValue];
result.hInInches = [height floatValue];
result.hInCms = hInInches *0.393700787;
}
return result;
}
Have the calling routine use the result's properties in its calculations.

Using malloc to create a 2d C style array of my class

(Edit: put possible solution at end)
I'm a C/C++ programmer who is learning Objective C to develop iPhone apps. The programs that I will be writing will deal with large 2d arrays of objects. I've read about using NSArray's of NSArray's and have some working code, but I'm trying to understand how to use C style arrays to save overhead and to learn what you can and can't do.
In this fragment MapClass only contains two properties, int x and int y. I have the following code fragment working with a statically defined array of 10x10.
MapClass *arr[10][10];
arr[2][3] = [[MapClass alloc] init];
arr[2][3].x = 2;
arr[2][3].y = 3;
NSLog(#"The location is %i %i", arr[2][3].x, arr[2][3].y);
// Output: "The location is 2 3"
This is an example of doing it with a one dimensional array and calculating where the cell is based on the X and Y:
MapClass **arr = (MapClass**) malloc(10 * 10 * sizeof(MapClass *));
arr[3 * 10 + 2] = [[MapClass alloc] init];
arr[3*10 + 2].x = 2;
arr[3*10 + 2].y = 3;
NSLog(#"The location is %i %i", arr[3*10 + 2].x, arr[3*10 + 2].y);
// Output: "The location is 2 3"
My question is this: How can I malloc my array as a two dimensional array so that I can use arr[2][3] style notation to access it?
Everything I'm trying is generating various errors such as "Subscript requires the size of [your class], which is not constant in non-fragile ABI".
Can anyone give me a snippit on how to do this? I've been reading and experimenting and can't figure it out. Does my one dimensional array example do anything wrong?
Answer?
After fooling around with xzgyb's answer, I have the following block working. Anything wrong with it? Thanks!
int dimX = 20;
int dimY = 35;
MapClass ***arr = (MapClass***) malloc( dimX * sizeof(MapClass **));
for (int x = 0; x < dimX; ++x)
{
arr[x] = (MapClass **) malloc( dimY * sizeof(MapClass*));
}
for (int x = 0; x < dimX; ++x)
{
for (int y = 0; y < dimY; ++y)
{
arr[x][y] = [[MapClass alloc] init];
arr[x][y].x = x;
arr[x][y].y = y;
}
}
for (int x = 0; x < dimX; ++x)
{
for (int y = 0; y < dimY; ++y)
{
NSLog(#"%i %i is %i %i", x, y, arr[x][y].x, arr[x][y].y);
}
}
// Cleanup
for (int x = 0; x < dimX; ++x) {
for (int y = 0; y < dimY; ++y) {
[arr[x][y] release];
}
}
for (int x = 0; x < dimX; ++x)
{
free(arr[x]);
}
free(arr);
Try the followed code:
MapClass ***arr = (MapClass***) malloc(10 * 10 * sizeof(MapClass *));
for ( int row = 0; row < 10; ++row ) {
arr[ row ] = (MapClass **)&arr[ row * 10 ];
}
arr[0][1] = [[MapClass alloc] init];
arr[1][2] = [[MapClass alloc] init];
Tested and it works fine using NSMutableString class and a variety of string methods.
I'd probably recommend using the standard message sending brackets than using the newer dot operator syntax just to simplify to the compiler what you are actually trying to accomplish.
The sizeof(ClassName ) should be the same as sizeof([ClassName class]) (and int or id for that matter) if I understand your meaning. The code you posted should not give an error like that as all pointers will be the same size. Now if you tried something like sizeof(*someInstanceOfAClass) then you're running into some issues because you're attempting to malloc enough memory to fit 10*10*(the actual size of your object) which is not what you're intending to do. (And sounds like what your warning is intended for.)

Game tile movement in 2d map

What's the best way to do movement in a 2D square grid system? I have this something that works but it seems wrong/ugly (see below).
x x x x x x x
x x x x x x x
x x x O x x x
x x x U x x x
x x x x x x x
x x x x x x x
x x x x x x x
For example, U is the unit I want to move, and O is an impassable object like another unit or a mountain. If U can move 3 tiles, I want the moveable area (M) to look like this.
x x x x x x x
x x M x M x x
x M M O M M x
M M M U M M M
x x M M M M x
x x M M M x x
x x x M x x x
Here's my code:
public function possibleMoves(range:uint, cords:Array):void {
var X:uint = cords[0];
var Y:uint = cords[1];
if (range > 0) {
try {
theGrid[X + 1][Y].moveable = true;
if (theGrid[X + 1][Y].getOccupied == false) {
possibleMoves(range - 1, [X + 1, Y], flag, mtype);
}
} catch (err:Error) { }
try {
theGrid[X - 1][Y].moveable = true;
if (theGrid[X - 1][Y].getOccupied == false) {
possibleMoves(range - 1, [X - 1, Y], flag, mtype);
}
} catch (err:Error) { }
try {
theGrid[X][Y + 1].moveable = true;
if (theGrid[X][Y + 1].getOccupied == false) {
possibleMoves(range - 1, [X, Y + 1], flag, mtype);
}
} catch (err:Error) { }
try {
theGrid[X][Y - 1].moveable = true;
if (theGrid[X][Y - 1].getOccupied == false) {
possibleMoves(range - 1, [X, Y - 1], flag, mtype);
}
} catch (err:Error) { }
}
the data structure of your tileset seems strongly coupled to a "Tile" class that does too many things ; theGrid[X][Y].moveable, theGrid[X][Y].getOccupied... + probably some other methods.
maybe the tileset data structure should only store Boolean values (walkable?true/false) and have a single method to tell wether a tile is walkable or not. in this case, a Vector of Boolean values is enough. testing the 4 ( or 8 with diagonals ) naerby values is pretty fast and spreading the test to the newly found values can be done with a recursive loop.
if you have different types of tiles (walls, objects, characters etc.), you could use a Vector.< int > rather than Booleans ; 0 would be a walkable tile and anything else would be forbidden areas.
this allows a Boolean check : as 0 = false and any other value = true.
I've done a sample here http://wonderfl.net/c/bRV8 ; it might be clearer than pasting the code. move the mouse around, you should see a pinky shape the gives you the valid cells.
l.53 is the "connexity" possible valeus are 4 and 8
four connected gives
eight connected
l.54 is the max recursion depth
as such, the recursion is performed regardless of the starting point. it will spill in a sometimes unexpected way.
if you need to give a specific amount of moves this won't be enough, you'll have to set up some kind of pathfinder.
Edit:
It appears that the code provided works, but contains a recursion termination bug that is attempted to be avoided by the following line. This works only in some cases and behaves really weird if you put your character at the edge of the map or give him number of moves other than 5:
var max:int = ( maxDepth * maxDepth );
if( maxDepth % 2 == 0 )max--;
recursiveCheck( valid, tilesetClone, 0, max, connexity );
I checked with different recursion depth, and the bug quickly becomes apparent. Lack of grid and complex map design of this example obscures the bug, but here's a screenshot below - note that if mouse is positioned in the corner like shown, the field extends 6 squares up and 7 squares left, while it should've only been 5.
Your code will work, but is far from elegant. A lot of tiles will be calculated multiple times. You could fix this by caching the results for each gridTile.
Have a look at the Memoization technique.
Here's the correct solution to the recursion to the avoiding obstacles on a 2D tile map problem in objective-c. Took me good 4.5 hours translate action script to objective-c and debug it... almost 3AM now :) To use this, just create a map of X by Y squares, put your model on the map and call
-(NSMutableArray*)possibleMovesFromIndex:(int)tileIndex movesCount:(int)moves allowDiagonalMoves:(BOOL)allowDiagonal
The resulting array will give you locations that your character can reach with the given number of moves. You can then use A* pathfinding algorithm to animate movement from the current position to any one of highlighted tiles.
I have attempted to be super-verbose in my names and descriptions, as it was quite difficult to trace these points through all these method calls without it.
MapOfTiles.h:
#import <Foundation/Foundation.h>
#define tileCountWide 14
#define tileCountTall 8
#interface MapOfTiles : NSObject
#property (nonatomic,strong)NSMutableArray* tilesetWalkable;
#property (nonatomic)int width;
#property (nonatomic)int height;
#property (nonatomic,readonly)int tileCount;
-(id)initWithXWidth:(int)xWidth yHeight:(int)yHeight;
-(CGPoint)pointFromIndex:(int)index;
-(NSMutableArray*)possibleMovesFromIndex:(int)tileIndex movesCount:(int)moves allowDiagonalMoves:(BOOL)allowDiagonal;
#end
MapOfTiles.m
#import "MapOfTiles.h"
#implementation MapOfTiles
-(id)initWithXWidth:(int)xWidth yHeight:(int)yHeight
{
self = [super init];
if (self) {
self.width = xWidth;
self.height = yHeight;
int count = xWidth*yHeight;
self.tilesetWalkable = [[NSMutableArray alloc] initWithCapacity:count];
for(int i = 0 ; i<count; i++)
{
//initial map is blank and has no obstacles
[self.tilesetWalkable addObject:[NSNumber numberWithBool:YES]];
}
}
return self;
}
-(int)tileCount
{
return self.width*self.height;
}
-(NSMutableArray*)possibleMovesFromIndex:(int)tileIndex movesCount:(int)moves allowDiagonalMoves:(BOOL)allowDiagonal
{
int connexity = 4;
if(allowDiagonal)
{
connexity = 8;
}
//check if there is an obstacle at the origin
NSNumber* movementOrigin = self.tilesetWalkable[tileIndex];
//if the first tile is walkable, proceed with seeking recursive solutions using 4 or 8 connected tiles
if(movementOrigin.boolValue == YES)
{
//create a copy to avoid messing up the real map
NSMutableArray* tilesetClone = [NSMutableArray arrayWithArray:self.tilesetWalkable];
//will contain tileset indices where you can reach in the given number of moves if you can only move in a straight line or straight line and diagonally
NSMutableArray* validMoves = [NSMutableArray arrayWithCapacity:10];
//we start building our array of walkable tiles with the origin, because we just tested it
NSNumber* originIsWalkable = [NSNumber numberWithInt:tileIndex];
NSMutableArray* initialWalkableTilesArray = [NSMutableArray arrayWithObject:originIsWalkable];
//for the first recursion, we manually set the origin to be not walkable, so recursion cannot return to it
[tilesetClone replaceObjectAtIndex:tileIndex withObject:[NSNumber numberWithBool:NO]];
[validMoves addObject:initialWalkableTilesArray];
[self recursiveCheckWithValidMovesArray:validMoves
tileset:tilesetClone
currentMove:0
maxMoves:moves
connexity:connexity];
return validMoves;
}
return nil;
}
-(void)recursiveCheckWithValidMovesArray:(NSMutableArray*)validMovesToPopulate tileset:(NSMutableArray*)tileset currentMove:(int)currentDepth maxMoves:(int)maxDepth connexity:(int)connexity
{
if(currentDepth == maxDepth)
{
return;
}else
{
NSArray* movesToCheck = [validMovesToPopulate objectAtIndex:currentDepth];
DLog(#"checking moves: %#",movesToCheck);
for (NSNumber* walkableMapIndex in movesToCheck)
{
//check array for valid moves
NSMutableArray* validMovesFromPoint = [self getValidMovesFromPoint:[self pointFromIndex:walkableMapIndex.intValue]
lockMovesInTileset:tileset
usingConnexity:connexity];
//remember valid moves, so the next iteration will check them
if(validMovesToPopulate.count == currentDepth+1)
{
//this is the first time we are looking at moves at this depth, so add an array that will hold these moves
[validMovesToPopulate addObject:validMovesFromPoint];
}else
{
//there is already an array at this depth, just add more values to it
NSMutableArray* validTilesForThisMove = validMovesToPopulate[currentDepth+1];
[validTilesForThisMove addObjectsFromArray:validMovesFromPoint];
}
}
if(movesToCheck.count>0)
{
[self recursiveCheckWithValidMovesArray:validMovesToPopulate
tileset:tileset
currentMove:++currentDepth
maxMoves:maxDepth
connexity:connexity];
}else
{
return;
}
}
}
-(CGPoint)pointFromIndex:(int)index
{
//for a field that is 8 tall by 12 wide with 0,0 in bottom left
//tileCountTall is also number of rows
//x is column
int x = index / tileCountTall;
//y is row
int y = index % tileCountTall;
CGPoint xyPointInTileset = CGPointMake(x, y);
DLog(#"Examing index: %i assigned:x%.0f, y:%.0f",index, xyPointInTileset.x,xyPointInTileset.y);
return xyPointInTileset;
}
-(int)indexFromPoint:(CGPoint)point
{
return [self indexFromX:point.x y:point.y];
}
-(int)indexFromX:(int)x y:(int)y
{
//in my case the map is rectangular
if ( x < 0 ) x = 0;
int tileWidth = tileCountWide -2 ;//in my case, 2 rows of grid are hidden off screen for recycling of map segments
if ( x > tileWidth - 1 ) x = tileWidth - 1;
if ( y < 0 ) y = 0;
if ( y > tileCountTall - 1 ) y = tileCountTall - 1;
#warning this might screw up the algorithm, because for me x and y values are mapped differently?
return x * tileCountTall + y;
return 0;
}
-(void)lockTileAtIndex:(int)index forTileset:(NSMutableArray*)tileset rememberValidMovesInThisArray:(NSMutableArray*)tiles
{
DLog(#"Locking tile: %i",index);
//we lock this tile, so it is not checked by future recursions
NSNumber* tileIsNotWalkableAtIndex = [NSNumber numberWithBool:NO];
[tileset replaceObjectAtIndex:index withObject:tileIsNotWalkableAtIndex];
//remember that this index is a valid move
[tiles addObject:[NSNumber numberWithInt:index]];
}
-(NSMutableArray*)getValidMovesFromPoint:(CGPoint)p lockMovesInTileset:(NSMutableArray*)tileset usingConnexity:(int)connexity
{
int i = 0;
NSMutableArray* validMovesFromThisPoint = [NSMutableArray array];//these tiles are valid moves from point
NSNumber* tileIsWalkable = nil;
//using (x,y) (0,0) as bottom left corner, Y axis pointing up, X axis pointing right
i = [self indexFromPoint:CGPointMake(p.x-1, p.y)];//left
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES)
{
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x+1, p.y)];//right
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES)
{
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x, p.y-1)];//bottom
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES)
{
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x, p.y+1)];//top
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES)
{
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
if(connexity == 4){
return validMovesFromThisPoint;//if we want a connexity 4, no need to go further
}
i = [self indexFromPoint:CGPointMake(p.x-1, p.y-1)];//bottom left
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES){
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x+1, p.y-1)];//bottom right
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES){
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x-1, p.y+1)];//top left
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES){
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
i = [self indexFromPoint:CGPointMake(p.x+1, p.y+1)];///top right
tileIsWalkable = tileset[i];
if(tileIsWalkable.boolValue == YES){
[self lockTileAtIndex:i forTileset:tileset rememberValidMovesInThisArray:validMovesFromThisPoint];
};
return validMovesFromThisPoint;
}
#end