Home > Mobile >  How can I check if all panels of a playing board were visited by the player?
How can I check if all panels of a playing board were visited by the player?

Time:06-28

I am working on an exercise from Mooc.fi's java part 2, week 10. It is called Dungeon. Basically you have a playing board(created with a String[][] array) and a player that moves on it. The size of the board(lenght and height) can be set when starting the game.

If you pass a string containing the letters "wasd" the player will change position accordingly and reappear on the board as long as the move is within the bounds. If not, the command that would set the player out of bounds is ignored but the rest is still executed. So if the user enters "wasd" and s would carry the player out of bounds, only "wad" is actually executed.

The player will only reappear on the new position if the entire command entered has been processed.

Now, you either win if you hunted down all the enemies, or if the player has been to all the pannels of the board. But I'm not sure how I can check that the player has been to all possible positions on the board?

I figured I could use a List containing HashMaps with all the coordinates of the board and compare that with a similar list that contains all the moves the player made, but that does not seem to work.

Here is the method that sets up the board. This is also where I figured I could get all possible coordinates of the board from, I stored them in a list called boardPlaces:

for(int i = 0; i < length; i  )
    {
        for(int j = 0; j < height; j  )
        {
            board[i][j] = ".";
            HashMap<Integer,Integer> values = new HashMap<Integer,Integer>();
            values.put(i,j);
            this.boardPlaces.add(values);
        }
    }

And this is the player method where the string entered for the movement of the player is processed and the player moves are recorded:

public void updatePosition(String m)
{
    
    char[] c = m.toCharArray();
    playerMoves = new String[c.length];
    for(int i = 0; i < c.length; i  )
    {
        playerMoves[i] = String.valueOf(c[i]);
    }
    
    for(String move : playerMoves)
    {
        if(move.equals("w") && y-1 >= 0)
        {
            this.y--;
        }
        if(move.equals("s") && y 1 <= height-1)
        {
            this.y  ;
        }
        if(move.equals("a") && x-1 >= 0)
        {
            this.x--;
        }
        if(move.equals("d") && x 1 <= length-1)
        {
            this.x  ;
        }
        HashMap<Integer,Integer> values = new HashMap<Integer,Integer>();
        values.put(x,y);
        movesRecorded.add(values);
    }
    moves--;
}

And this here is where I check if all pannels of the board have actually been visited by the player.

int counter1 = 0;
    int counter2 = 0;
    this.playerMovesRecorded = player.getMovesRecorded();
    for(HashMap map : boardPlaces)
    {
        counter1  ;
    }
    for(HashMap map : playerMovesRecorded)
    {
        counter2  ;
    }
    return (counter1 == counter2) /*and contents of lists are the same*/|| vamps.isEmpty();

I figured if the number of elements is correct and the content matches it should return true -> player has visited all boards. But I'm not sure how I should go about comparing the content of the two lists?

CodePudding user response:

You can either create a custom class or create another array to capture the information, like

public class BoardTile {
  String info;
  boolean visited;
}

And then use a BoardTile[][] instead of a String[][]

Alternatively, you could create a separate 2D array of booleans

private boolean[][] visited = new boolean[widthOfBoard, heightOfBoard];

CodePudding user response:

Why your Map doesn't work:

You are using a Map<Integer, Integer>. For each distinct Integer, this map can store another Integer (it "maps" Integer to Integer)

Example dataset of your current Map:

{
    1: 3
    2: 5
    4: -2
}

This is obviously not what you want. You likely want something like this (Mapping 2 Integers to a String:

{
    "1/3": "WALL"
    "2/5": "WALL"
    "4/-2": "GROUND"
}

Because having 2 keys isn't possible, you need a wrapper class. (Ignore Vector for now):

public interface Vector {
    int getX();

    int getY();

    default Position add(Vector other) {
        return new Position(getX()   other.getX(), getY()   other.getY());
    }

    default Position subtract(Vector other) {
        return new Position(getX() - other.getX(), getY() - other.getY());
    }
}

public record Position(int x, int y) implements Vector {
}

record is just an abbreviation for a class that only contains final attributes and implements hashCode and equals.

Now you can create your Map like this:

Map<Position, String> map = new HashMap<>();

Or to save a number of Positions the player already entered, do this:

Set<Position> list = new HashSet<>();

A Set can only contain each value once, so you can add the same Position over and over again, but it will only the first one will be saved.

Why I would not do that:

Java is an Object-oriented programming language. You can bundle information and simplify problems by using an object-oriented approach.

Let's create a few classes here to make things object-oriented:

  1. Not using String to store non-textual data

Because the amount of different Tiles you have is limited anyway, you can create an enum. I assume you have 2 Tiles (WALL and GROUND).

public enum Pattern {
    GROUND,
    WALL
}
  1. Bundling all information about a Tile:
public class Tile {
    private final Pattern pattern; // The kind of Tile doesn't change.
    private final Position pos; // The pos doesn't change.
    private boolean visited; // Whether you entered the Tile can change.

    public Tile(Pattern pattern, Position pos) {
        this.pattern = pattern;
        this.pos = pos;
    }
    
    public Position getPos() {
        return pos;
    }

    public void markVisited() {
        visited = true;
    }

    public boolean isVisited() {
        return visited;
    }
}
  1. Bundling all information about a board.
  • You can either apply strict boundaries to your Board (see getTile1),
  • or make it wrap (way easier than you probably think) (see getTile2).
public class Board {
    private final int width, heigth;
    private final Tile[][] tiles;

    public Board(int width, int height) {
        this.width = width;
        this.height = height;
        this.tiles = new Tile[width][height];
        for (int y = 0; y < height; y  ) {
            for (int x = 0; x < width; x  ) {
                tiles[x][y] = new Tile(Pattern.GROUND);
            }
        }
    }

    public Tile getTile1(Position pos) {
        int x = Math.max(0, Math.min(width - 1, pos.getX()));
        int y = Math.max(0, Math.min(height - 1, pos.getY()));
        return tiles[x][y];
    }

    public Tile getTile2(Position pos) {
        int x = Math.floorMod(x, width);
        int y = Math.floorMod(y, height);
        return tiles[x][y];
    }
}
  1. Instead of using Strings, make an enum Direction:
public enum Direction implements Vector {
    UP(0, -1, 'w'), DOWN(0, 1, 's'), LEFT(-1, 0, 'a'), RIGHT(1, 0, 'd');

    private final int x, y;
    private final char keyChar;

    Direction(int x, int y, char keyChar) {
        this.x = x;
        this.y = y;
        this.keyChar = keyChar;
    }

    public static Direction forKeyChar(char keyChar) {
        for (Direction dir : values()) {
            if (dir.keyChar == keyChar) return dir;
        }
        throw new IllegalArgumentException("No Direction for keyChar "   keyChar);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

How powerful this is:

  1. Updating the player position (I assume you chose either getTile1 or getTile2 already and called it simply getTile)
private Board board = new Board();
private Tile playerTile = board.getTile(new Position(x, y));

Code to update:

For each key pressed, you can now add all of the Directions together to obtain a Position, which acts as a Vector:

String m = ...;
Position change = new Position(0, 0);
for (char c : m.toCharArray()) pos = pos.add(Direction.forKeyChar(c));
Tile newPlayerPos = board.getTile(playerTile.getPos().add(change));
  1. Solve your problem

Add this method into Board:

public boolean allVisited() {
    for (int y = 0; y <= height; y  ) {
        for (int x = 0; x <= height; x  ) {
            if (!tiles[x][y].isVisited()) return false;
        }
    }
    return true;
}

Note: I didn't check any of the code, hope it works!

  •  Tags:  
  • java
  • Related