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?
Related
So I need help figuring out what code I am missing here. I have checked all over the place, but I need specifics on wether its the formulas used or a typo that i haven't noticed yet.
Here is the polygon class. I am trying to create random polygons with 8 vertices and then of course fill with a plain color. But I want them to continue to generate random position but leave them fixed. In a better way the poly's are my terrain.Ok revise: the polygons are there and my character interacts with them, but I cannot see them, and yes they are on the same layer. Oh but they don't keep generating at the bottom, which i am guessing i just need to delete the old ones once they go off the screen and it should make a new poly.
-(void) genBody:(b2World *)world pos:(CGPoint *)pos {
//Here we generate a somewhat random convex polygon by sampling
//the "eccentric anomaly" of an ellipse with randomly generated
//x and y scaling constants (a,b). The algorithm is limited by
//the parameter max_verts, and has a number of tunable minimal
//and scaling values.
// I need to change this to randomly choosing teh number of vertices between 3-8,
// then choosing random offsets from equally distributed t values.
// This will eliminate teh while loop.
screen_pos = ccp(pos->x, pos->y);
float cur_t;
float new_t;
float delta_t;
float min_delta_t = 0.5;
float t_scale = 1.5;
b2Vec2 *verts= new b2Vec2[m_maxVerts]; // this should be replaced by a private verts ... maybe ... hmm that will consume more ram though
float t_vec[m_maxVerts];
// Generate random vertices
int vec_len;
while (true) {
cur_t = 0.0;
for (vec_len=0; vec_len<m_maxVerts; vec_len++) {
//delta_t = t_scale*(float)rand()/(float)RAND_MAX; // wish they just had a randf method :/
delta_t = t_scale*floorf((double)arc4random()/ARC4RANDOM_MAX);
#ifdef POLY_DEBUG
CCLOG(#"delta_t %0.2f", delta_t);
#endif
if (delta_t < min_delta_t) {
delta_t = min_delta_t;
}
new_t = cur_t + delta_t;
if (new_t > 2*PI) {
break;
}
t_vec[vec_len] = new_t;
cur_t = new_t;
}
// We need at least three points for a triangle
if ( vec_len > 3 ) {
break;
}
}
At least where the body is being generated.
then...
float num_verts = vec_len;
b2BodyDef BodyDef;
BodyDef.type = b2_staticBody;
BodyDef.position.Set(pos->x/PTM_RATIO, pos->y/PTM_RATIO);
BodyDef.userData = self; // hope this is correct
m_polyBody = world->CreateBody(&BodyDef);
b2PolygonShape polyShape;
int32 polyVert = num_verts;
polyShape.Set(verts, polyVert);
b2FixtureDef FixtureDef;
FixtureDef.shape = &polyShape;
FixtureDef.userData = self; // hope this is correct
FixtureDef.density = 1.6f;
FixtureDef.friction = 0.4f;
FixtureDef.restitution = 0.5f;
m_polyBody->CreateFixture(&FixtureDef);
for (int i=0; i < num_verts; i++) {
// Convert from b2Vec2 to CCPoint and from physics units to pixels
m_verts[i] = ccp(verts[i].x*PTM_RATIO, verts[i].y*PTM_RATIO);
}
m_numVerts = num_verts;
delete verts;
}
-(void) setColor:(ccColor4F) color {
m_color = color;
}
-(BOOL) dirty {
return true;
}
-(void) draw {
//[super draw];
ccDrawPoly(m_verts, m_numVerts, YES);
CCLOG(#"Drawing?");
}
-(CGAffineTransform) nodeToParentTransform {
b2Vec2 pos = m_polyBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
/*if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPixels_.x;
y += anchorPointInPixels_.y;
}*/
// Make matrix
float radians = m_polyBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ){
x += c*-anchorPointInPixels_.x + -s*-anchorPointInPixels_.y;
y += s*-anchorPointInPixels_.x + c*-anchorPointInPixels_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
there is some stuff in between but its less important. I can post it if asked.
Then the update function, which is based in my game scene class.
-(void)updateObstacles
{
//CCLOG(#"updating obstacles");
int xpos;
int ypos;
CGPoint pos;
for (int i=0; i<MAX_OBSTACLES; i++ ) {
// If there is no obstacle generate a new one
if ( obstacles[i] == NULL ) {
polyObstacleSprite *sprite = [[polyObstacleSprite alloc] init];
ypos = int(_winSize.width/2*(double)arc4random()/ARC4RANDOM_MAX) - _winSize.width/2;
xpos = int(_winSize.height/2*(double)arc4random()/ARC4RANDOM_MAX) - _winSize.height/2;
//CCLOG(#"generating obstacle at %d,%d", xpos, ypos);
pos = ccp(xpos, ypos);
[sprite genBody:_world pos:&pos];
[self addChild:sprite z:1];
obstacles[i] = sprite;
}
//CCLOG(#"position: %d, %d", obstacles[i]->screen, obstacles[i]->position.y); FINISH
}
}
Sorry if its sort of a mess I set this up quick, but pretty much what I want to do is have randomly generated polygons appear at the bottom of my iphone screen as my character moves down with gravity. I got everything else but the polygons working.
Thanks in advance for spending the time to read this.
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.
I have the following calculation to make:
float value = MY_VALUE;
float percentage = (value - MIN)/(MAX - MIN);
if (percentage < 0)
percentage = 0;
if (percentage > 1)
percentage = 1
I was just wondering if there is some method that exists in Objective-C or C to help accomplish this, like for example a method that will round the "value" float to be between the MIN and MAX bounds.
There is no such method in standard C libraries (I don't know about Objective C).
No.
On another note, you should probably update your code so you are preventing a possible divide-by-zero:
float den = (value - MIN);
float num = (MAX - MIN);
float percentage;
if(den) {
percentage = num / den;
if (percentage < 0.0f ) {
percentage = 0.0f;
} else if (percentage > 1.0f) {
percentage = 1.0f;
} else {
printf("Percentage: %3.2f",percentage);
}
} else {
percentage = 0.0f;
printf("DIVIDE BY ZERO ERROR!");
}
Also, careful. Oracle is suing Google over a very similar piece of code. You may want to reconsider posting it publicly:
http://developers.slashdot.org/story/12/05/16/1612228/judge-to-oracle-a-high-schooler-could-write-rangecheck
If you want to calculate percentage in objective-c syntax then this is the way to go.
(I think Dogbert got it wrong (opposite) on line 6).
Here's what I did and it works just as wanted..
- (CGFloat)calculateProgressForValue:(CGFloat)value start:(CGFloat)start end:(CGFloat)end
{
CGFloat diff = (value - start);
CGFloat scope = (end - start);
CGFloat progress;
if(diff != 0.0) {
progress = diff / scope;
} else {
progress = 0.0f;
}
return progress;
}
And you can always constrain this to not go over 0.0 or 1.0 like so:
- (CGFloat)progressConstrained {
// example values
CGFloat start = 80;
CGFloat end = 160;
CGFloat a = 100;
// the constrain
return MAX(0.0, MIN(1.0, [self calculateProgressForValue:a start:start end:end]))
}
If you just want to make sure a value is not higher or lower than other values you should use MAX(a, b) and MIN(a, b)...
float maxValue = 1.0;
float minValue = 0.0;
float myValue = 2.8;
float myValueConstrained = MAX(minValue, MIN(maxValue, myValue)); // 1.0
MIN(a,b) takes the smallest of a and b
MAX(a,b) takes the greatest of a and b
I typically just do like this:
float myValueConstrained = MAX(0.0, MIN(1.0, myValue));
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
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.