tags:

views:

266

answers:

3

I'm still working on my Cell class for my maze game I'm attempting to make. After help in a different thread it was suggested that I use an EnumMap for my Walls/Neighbors and this is working great so far.

Here is what I have thus far:

enum Dir {
    NORTH, SOUTH, EAST, WEST
}

class Cell {
    public Map<Dir, Cell> neighbors = Collections
            .synchronizedMap(new EnumMap<Dir, Cell>(Dir.class));
    public Map<Dir, Boolean> walls = Collections
            .synchronizedMap(new EnumMap<Dir, Boolean>(Dir.class));

    public boolean Visited;

    public Cell() {
        Visited = false;
        for (Dir direction : Dir.values()) {
            walls.put(direction, true);
        }
    }

    // Randomly select an unvisited neighbor and tear down the walls
    // between this cell and that neighbor.
    public Cell removeRandomWall() {
        List<Dir> unvisitedDirections = new ArrayList<Dir>();
        for (Dir direction : neighbors.keySet()) {
            if (!neighbors.get(direction).Visited)
                unvisitedDirections.add(direction);
        }

        Random randGen = new Random();
        Dir randDir = unvisitedDirections.get(randGen
                .nextInt(unvisitedDirections.size()));
        Cell randomNeighbor = neighbors.get(randDir);

        // Tear down wall in this cell
        walls.put(randDir, false);
        // Tear down opposite wall in neighbor cell
        randomNeighbor.walls.put(randDir, false); // <--- instead of randDir, it needs to be it's opposite.

        return randomNeighbor;
    }
}

If you look at that last comment there, I first tear down say the NORTH wall in my current cell. I then take my North neighbor, and now I must tear down my SOUTH wall, so the walls between the two cells have been removed.

What would be a simple way to extend my enum so I can give it a direction and it return to me it's opposite?

+5  A: 

Simplest, I think, is just to add a method to it. Note that this only works well if the number of enum constants won't change over time.

enum Dir {
    NORTH,
    SOUTH,
    EAST,
    WEST;

    public Dir opposite() {
        switch(this) {
            case NORTH: return Dir.SOUTH;
            case SOUTH: return Dir.NORTH;
            case EAST: return Dir.WEST;
            case WEST: return Dir.EAST;
            default: throw new IllegalStateException("This should never happen: " + this + " has no opposite.");
        }
    }
}

Then, in your code, you could do this:

randomNeighbor.walls.put(randDir.opposite(), false);
jqno
+1. You're not supposed to `switch` on enums, but because of the circular dependencies, this is the cleanest, most readable way to do it... assuming you add `default: throw new AssertionError(this)` :)
gustafc
@gustafc Agreed, I updated my answer.
jqno
@gustafc, what is the reason behind not normally wanting to switch on enums?
Scorcher84
@Scorcher84 If you decide later on to add an entry to your enum (say, NORTHWEST), you have to update all your switch statements. If they're in the enum itself, that's annoying but do-able. If you have switches elsewhere in your code, it will become nearly impossible. Generally, it's better to add an abstract method to your enum. In this case, that's not possible, because you cannot reference an enum constant before its declaration.
jqno
+9  A: 

yet another way without switch/case, or having to store state:

public enum Dir {
   NORTH { @Override public Dir opposite() { return SOUTH; }},
   EAST  { @Override public Dir opposite() { return WEST;  }},
   SOUTH { @Override public Dir opposite() { return NORTH; }},
   WEST  { @Override public Dir opposite() { return EAST;  }},
   ;

   abstract public Dir opposite();
}
Jason S
Better than my approach. Mine just looks like over-engineering ;-)
toolkit
Note this create five class files instead of one.
Tom Hawtin - tackline
yup. tradeoff between code space and data space. This does not scale well to larger data structures, many methods, or to lots of enums... but for little tweaks it's a quick fix. Also works well when most of the enums need to return one value, with a few exceptions.
Jason S
If only Java facilitated compile-time automatic code generation, you could define functions to be executed at compile time to generate tedious coding patterns :-(
Jason S
A: 
public enum Direction
{
    NORTH,EAST,SOUTH,WEST;

    public Direction opposite()
    {
        return Direction.values()[ (this.ordinal() + 2) & 3 ];
    }
}
fforw
or even this.ordinal()^2 ?
araqnid
this is a good point.
fforw