tags:

views:

1830

answers:

6

I'm trying to write a model containing digital organisms. Within the model i'd liek the environment to be a fixed 2-d array, but each cell needs to contain a list of the organisms in it. I tried using a jagged array, but as the number of occupied elements varies quite a bit throughout the programm run, i need to use something more flexible than an array. I've tried making a 2-D array of the type list, but im getting errors with it.

    List<Creature>[,] theWorld;


    public Environment()
    { 
        List<Creature>[,] theWorld = new List<Creature>[100,100];
    }

    public void addCreature(Creature c)
    {
       for (int x = 0; x < 100; x++)
        {
            for (int y = 0; y < 100; y++)
            {
                theWorld[x, y].Add (c);

            } } }

this is the segment where i'm trying to declare the array at the beginning, as a type that holds lists (of the organisms), and later i try to add a creature (c) to each of the lists in each element of the array.

when i run it i get the following error message-

"An unhandled exception of type 'System.NullReferenceException' occurred in HGT_sim_2.exe

Additional information: Object reference not set to an instance of an object."

and the line "World[x, y].Add (c);" is highlighted.

If anyone can tell me what i'm doing wrong, and even better, a way around the problem, it'd be amazing. thank you ain advance!

+1  A: 

Here's the fix:

List<Creature>[,] theWorld;


public Environment()
{ 
    theWorld = new List<Creature>[100,100]; // Remove the type, you were creating a new list and throwing it away...

    for(int x = 0 ; x < 100 ; x++)
       for(int y = 0 ; y < 100 ; y++)
          theWorld[x,y] = new List<Creature>();    
}

public void addCreature(Creature c)
{
   for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            theWorld[x, y].Add (c);

        } } }
consultutah
Where's the fix?
Vilx-
Added a comment to tell you, in the constructor.
consultutah
Fair point.
Vilx-
I did miss that it needs to new each list up though...
consultutah
Btw - I didn't downvote you.
Vilx-
A: 

You have created the array object to hold your lists, but you haven't created the list itself. You will need to do the following in your constructor:

for (int x = 0; x < 100; x++) 
    for (int y = 0; y < 100; y++)
        theWorld[x,y] = new List<Creature>();

Another problem: you were also defining theWorld as a local variable in your constructor, which means your theWorld field on Environment was never initialized, either.

However, 10,000 Lists may be overkill for what you really need. If your Environment really needs a Creature at every point, and some Creatures may move to other points (where there is more than one at a point, then it may make more sense to use a Dictionary<Point, IList<Creature>> as your model versus 10,000 lists.

public void Add(Creature c, Point at)
{
    IList<Creature> list;
    if (!theWorld.TryGetValue(at)) {
        list = theWorld[at] = new List<Creature>();
    }
    list.Add(c);
}

You can then implement Move and Remove methods similarly. Also, note that you are adding the same Creature to every point, which (may) mean that there's one Creature at all points in your Environment. You will probably want to create a new Creature() for every point, if that's what you are actually modeling.

Jason
Partially true, but it wouldn't work without fixing the code in the constructor
consultutah
Edited to include that and another suggestion.
Jason
+5  A: 

All your array contains initially is a lot of nulls. You need to actually create the lists...

for(int x = 0 ; x < 100 ; x++)
    for(int y = 0 ; y < 100 ; y++)
        theWorld[x,y] = new List<Creature>();

Personally, though, I expect this will be a costly way to do things...

It depends in part on whether the data is "sparse" - i.e. are most of the cells usually taken? A simple (but possibly more efficient) approach, for example, would be to use something like multi-map; i.e.

Point pt = new Point(x,y);
theWorld.Add(pt, someCreature);

where theWorld could be something like EditableLookup<Point, Creature> (using EditableLookup<,> from "MiscUtil"). That way, you can still query it by co-ordinate, and have multiple creatures on a coordinate, but you don't have to allocate space for every cell. And because it functions as a dictionary it is still fast. Not as fast as a flat array, but it will scale to bigger (sparse) grids... of course, if the grid has creatures on every cell it could be more expensive! Hence the need to understand your data.

Marc Gravell
was going to say something similar so you got my upvote
instanceofTom
PowerCollections contains a MultiDictionary<K,T> which will help
ShuggyCoUk
Indeed. If you haven't written your own multi-map at some point you haven't been trying ;-p
Marc Gravell
+1  A: 

You need to initialize each member of your array, e.g.

for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            theWorld[x, y] = new List<Creature>();

        } }
Eric J.
PS ... do that BEFORE the code you posted that adds to the list ;-)
Eric J.
A: 

When you do this:

List<Creature>[,] theWorld = new List<Creature>[100,100];

You're creating an array of List<Creature> references, but they are all empty (pointing to null, not a valid List). You need to initialize each individual element:

for (int x = 0; x < 100; x++) {
    for (int y = 0; y < 100; y++) {
       theWorld[i,j] = new List<Creature>();
    }
}

Once you've done that, you'll be able to call .Add on the individual members.

Reed Copsey
A: 

You're doing it almost correct. Your variable is a 2D array of List<Creature>. Now, List<Creature> is a reference type, so the array is initialized to contain null's in all its members. Thus you get the NullReferenceException. The line

theWorld[x, y].Add (c);

is basically equivalent to

null.Add (c);

All you need to do is to initialize all the members to contain instances of List<Creature>. The best way to do this would be in the constructor. Just rewrite it like this:

public Environment()
{ 
    theWorld = new List<Creature>[100,100];
    for(int x = 0 ; x < 100 ; x++)
        for(int y = 0 ; y < 100 ; y++)
            theWorld[x,y] = new List<Creature>();
}

Now all the operations will work as expected.

Also note, that in your example you are creating a local variable with the same name as the class member. This way you don't initialize the class member at all - it stays null.

Vilx-
A downvote? What for?
Vilx-
No, idea. You said the same thing I did. But more understandable...
consultutah