0

I have a big square matrix of structs which represents creatures. Each creature can move left,up,right or down. I'm checking the neighbouring matrix positions for empty cells and do the following calculation for the new coordinates:

function res = Move2(worldDimension,row,col,left,up,right,down)

%In the following matrices, good moves are > 0.

%Directions pattern:
patternMatrix = [0 2 0;
                 1 5 3;
                 0 4 0];
%Possible border moves:             
borderMatrix = [0 (row>1) 0;
                (col>1) 1 (col<worldDimension);
                 0 (row<worldDimension) 0;];
%Possible neighbor moves:              
neighborsMatrix = [0 (up==0) 0 ;
                (left==0) 1 (right==0);
                 0 (down==0) 0;];             

%Matrix of possible directions including neighbors and borders
possibleMovesMatrix = ((borderMatrix).*(neighborsMatrix)).*(patternMatrix);

%Vector of possible directions:
possibleMovesVector = sort(possibleMovesMatrix(possibleMovesMatrix(:) > 0));

%Random direction:
randomDirection = possibleMovesVector(randi(length(possibleMovesVector)));

directionCoordsVector = [[row (col-1)];[(row-1) col];[row (col+1)];[(row+1) col];[row col]];                         
res = [directionCoordsVector(randomDirection,1) directionCoordsVector(randomDirection,2)];
end

This function is kind of slow, when I run the profiler it tells me that:

borderMatrix = [0 (row>1) 0;
(col>1) 1 (col<worldDimension);
0 (row<worldDimension) 0;];

takes 36% of time and that: randomDirection = possibleMove... takes 15% of time. Is there a way I can accelerate the process?

Maybe I can take a different approach by taking from the main game board the free spots arround the coordinate of the creature immediately? If so, how do I take a submatrix if a creature is near the border of the board with out having to deal with off-boundary indexes?

Thanks, Guy.

Rachel Gallen
  • 27,943
  • 21
  • 72
  • 81
Guy Wald
  • 599
  • 1
  • 10
  • 25
  • I don't see why you need a matrix to handle your data. I know it's less intuitive, but you really only have 4 possible moves, so you only really need a vector of length 4. That should speed things up. – Squazic Jan 22 '13 at 15:07

2 Answers2

1

So you have an array of structs, and move around the structs inside the array? That seems extremely inefficient to me.

Also, the reason the borderMatrix-line takes so long is because you are constructing a possibly large array.

Here's a suggestion for handling moving creatures:

Store your creatures as a nCreatures-by-mProperties numeric array. It's much easier to apply functions on a column of an array that to crawl individual fields. For example creatures = [x,y,prop1,prop2];

Move your creatures one-by-one:

for iCreature = 1:nCreatures
    currentPos = creatures(iCreature,1:2);

    %# initialize array of allowed moves with border conditions
    goodMoves = [currentPos(1) > 1, currentPos(2) > 1, currentPos(1) < maxX, currentPos(2) < maxY, true];

    %# check for neighbors
    if any(creatures(:,1) == currentPos(1) - 1 & creatures(:,2) == currentPos(2))
       goodMoves(1) = false;
    end
    %# etc

    %# identify remaining good moves
    if any(goodMoves(1:4))
       goodMoveIdx = find(goodMoves);
       move = goodMoveIdx(randi(length(goodMoveIdx)));
    else
       move = 5; %# remain stationary
    end
end
Jonas
  • 74,690
  • 10
  • 137
  • 177
  • When I check for neighbors, might the conditions in the example falsify the move to a certain direction even if the the square to that direction is free? I mean, the if above checks the x and falsifies the left movement even if the the y will put the creature far from the current creature. Can I both x and y together? – Guy Wald Jan 22 '13 at 18:02
  • @Guy: I assumed that you can only move left/right/up/down, not left+up, and that the step will only be of size one. If there can be multi-step movements, I suggest that you just loop the single step a bunch of times. If there can be diagonal movements (one-step only), then you need to have more "goodMove"-options, and you need to check both coordinates at the same time, e.g. "left" is checked as `creatures(:,1) == currentPos(1)-1 & creatures(:,2) == currentPos(2)` – Jonas Jan 22 '13 at 19:27
  • You assumed correct. Maybe I do not understand the logic. The creatures have a 2d position. If I understood, checking 'any(creatures(:,1) == currentPos(1) - 1)' checks if another creature exists exists in the row above me. That creature might be to the row above but to the left or to the right somewhere. Yet the square exactly above me might be free. – Guy Wald Jan 22 '13 at 19:52
  • My solution is: goodMoves(left) = sum((creatures(:,row)==currentPos(1)).*(creatures(:,col)==currentPos(2)-1)) == 0; – Guy Wald Jan 22 '13 at 19:53
  • It seems that the use of n-creatures by m-properties, with the for iCreatire=1:nCreatures will lead me to an O(n^2) solution, while using a matrix is O(n) because with a matrix I check exactly the cells arround, while with this solution i check for every move all the other creatures. No? – Guy Wald Jan 22 '13 at 20:12
  • 1
    @Guy: You're right - you'd need to do the check that I suggest in my comment above. I'll edit my answer. The logical checks are in general fast compared to most other solutions, even if you end up checking all vs all. Thus, I also suggest using `any` and `&` instead of `sum` and `.*`. Anyway, for a possibly faster solution you can calculate the distance using a fast mex-algorithm that allows you to skip distance calculations for far away neighbors - up/down/left/right is only ever one step away. – Jonas Jan 22 '13 at 20:39
  • 1
    You may also skip the neighbor-check if there isn't any goodMove for that check, to save some additional time. Anyway, you should profile again to make sure that it is this part that really hurts you performance-wise. – Jonas Jan 22 '13 at 20:41
  • Jonas: Thank you. This improved the code speed a lot and I have learned a lot about Matlab from you!! – Guy Wald Jan 22 '13 at 23:03
0

It is not really clear whether there are multiple highly dependant creatures, but otherwise this would be an efficient workflow:

  1. Generate 1 random number per creature
  2. Determine per creature how many possible moves it has
  3. Use the corresponding random number to make a choice

If they are dependant, but not too much you can do this several times untill you find a feasible value or include the dependance in step 2.

Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122