13

I am trying to write my own Game of Life, with my own set of rules. First 'concept', which I would like to apply, is socialization (which basicaly means if the cell wants to be alone or in a group with other cells). Data structure is 2-dimensional array (for now).

In order to be able to move a cell to/away from a group of another cells, I need to determine where to move it. The idea is, that I evaluate all the cells in the area (neighbours) and get a vector, which tells me where to move the cell. Size of the vector is 0 or 1 (don't move or move) and the angle is array of directions (up, down, right, left).

This is a image with representation of forces to a cell, like I imagined it (but reach could be more than 5):

ForceAppliedToACell

Let's for example take this picture:

Example

Forces from lower left neighbour: down (0), up (2), right (2), left (0)
Forces from right neighbour     : down (0), up (0), right (0), left (2)
sum                             : down (0), up (2), right (0), left (0)

So the cell should go up.

I could write an algorithm with a lot of if statements and check all cells in the neighbourhood. Of course this algorithm would be easiest if the 'reach' parameter is set to 1 (first column on picture 1). But what if I change reach parameter to 10 for example? I would need to write an algorithm for each 'reach' parameter in advance... How can I avoid this (notice, that the force is growing potentialy (1, 2, 4, 8, 16, 32,...))? Can I use specific design pattern for this problem?

Also: the most important thing is not speed, but to be able to extend initial logic.

Things to take into consideration:

  • reach should be passed as a parameter
  • i would like to change function, which calculates force (potential, fibonacci)
  • a cell can go to a new place only if this new place is not populated
  • watch for corners (you can't evaluate right and top neighbours in top-right corner for example)
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
sventevit
  • 4,766
  • 10
  • 57
  • 89
  • First, it's an interesting question. I don't understand your example. Why do you take only the red cells into account in the example? Why do right(2) and right(0) sum to right(0) and up(2) and up(0) sum to up(2)? – Ikaso Apr 18 '10 at 15:19
  • @Ikaso: right two plus left two equals zero. – Eric Lippert Apr 18 '10 at 15:22
  • 2
    It's an interesting question and problem to solve but your question is not even close to what you are looking for. You need code in order to refactor. What you are really asking for is help in writing the algorithm. – John Apr 18 '10 at 15:23
  • @Eric Lippert: thanks got it. It's nice to get a reply from a legend. What about the first question regarding the red cells? – Ikaso Apr 18 '10 at 15:26
  • @lkaso: The red cells are places where somebody else is living. Each lifeform is trying to distance themselves from other lifeforms. – Matthew T. Staebler Apr 18 '10 at 15:29
  • @John - I have code, but it is 'spaghetti code' with a bunch of if statements. Also it works for only one reach parameter - if I want to change reach, then I have to write new algorithm. But yeah, maybe this is to broad question... :( – sventevit Apr 18 '10 at 15:40
  • Incidentally, the function you wish to compute is based on the Manhattan Distance from the point. That is, if you can only move on rectangular city streets, how far is it from point A to point B. – Eric Lippert Apr 18 '10 at 23:17
  • Have you read up on cellular automata, the class of 'games' to which the game of life belongs? It seems unlikely that what you have in mind can be implemented as a CA, but it may provide useful inspiration. – Nick Johnson Sep 23 '11 at 01:15

2 Answers2

4

It should not be difficult to write your algorithm to search all of the cells within the reach distance of a particular cell C. Each cell that has an inhabitant would have a particular force of repulsion on cell C. This force of repulsion is based on the distance from the cell to cell C. In the example that you have given, that force of repulsion is based upon the L-1 distance and is 2^(reach-distance). Each repulsion force is then added together to create a cumulative force that dictates the direction in which to move the inhabitant in cell C.

You do not need to write an algorithm for each different reach. The magnitude of the force can be determined via a simple formula. If you change that formula to something else such as a Fibonacci number, you should still be able to calculate the magnitude as needed based upon the distance and the reach.


Here is some rough code written in pseudo-Java showing the basic ideas: http://codepad.org/K6zxnOAx

enum Direction {Left, Right, Up, Down, None};

Direction push(boolean board[][], int testX, int testY, int reach)
{
    int xWeight = 0;
    int yWeight = 0;
    for (int xDist=-reach; xDist<=+reach; ++xDist)
    {
        for (int yDist=-reach; yDist<=+reach; ++yDist)
        {
            int normDist = abs(xDist) + abs(yDist);
            if (0<normDist && normDist<reach)
            {
                int x = testX + xDist;
                int y = testY + yDist;
                if (0<=x && x<board.length && 0<=y && y<board[0].length)
                {
                   if (board[x][y])
                   {
                       int force = getForceMagnitude(reach, normDist);
                       xWeight += sign(xDist) * force;
                       yWeight += sign(yDist) * force;
                   }
                }
            }
        }
    }
    if (xWeight==0 && yWeight==0) return Direction.None;
    if (abs(xWeight) > abs(yWeight))
    {
        return xWeight<0 ? Direction.Left : Direction.Right;
    }
    else
    {
        return yWeight<0 ? Direction.Up : Direction.Down;
    }
}

int getForceMagnitude(int reach, int distance)
{
    return 1<<(reach-distance);
}
Matthew T. Staebler
  • 4,756
  • 19
  • 21
  • That looks great! I think the only thing to change is the return type, so that you can return up to 2 directions (up and right for instance), but basically that's what I was looking for. – sventevit Apr 18 '10 at 21:19
  • 1
    You should consider returning the actual cumulative force vector then. The force vector *f* = <*f_x*, *f_y*>, where *f_x* and *f_y* are the amounts of force being exerted on the cell in the x direction and y direction, respectively. With that vector, the callee can figure out which direction is preferred. If that direction is either occupied or off the board, then the perpendicular preferred direction could be attempted. – Matthew T. Staebler Apr 19 '10 at 05:15
0

Write a function to loop over the neighbors:

  • Use min/max to clamp the bounds of the matrix.
  • Use a for loop to loop over all neighbors.
  • Modify the for loop bounds to represent reach.

:

def CalculateForceOnCell(x, y):
    force_on_x_y = [0,0,0,0]
    for i in range(max(0, x-reach), min(WIDTH, x+reach)+1):
        limited_reach = reach - abs(x-i)
        for j in range(max(0, y - limited_reach), min(HEIGHT, y + limited_reach + 1)):
            force_coefficient = limited_reach + 1
            AddNeighborForce(force_on_x_y, (x, y), (i, j), force_coefficient)
    return force_on_x_y
Stephen
  • 47,994
  • 7
  • 61
  • 70