proj0. 2048 of CS61B of UCB

Intro

In this project, we need to solve four tasks I talked about how I dealt with these four problems

Task 1. public static boolean emptySpaceExists(Board b)

This task requires us to check whether there is an empty grid in 2048 The solution is easy to think of: traverse all the grids on one side to see if there is any space Look at other files to know what API to use to complete this function For example, we can use board Tile (col, row) to obtain the corresponding lattice You can know whether it is empty by the returned status

/** Returns true if at least one space on the Board is empty.
 *  Empty spaces are stored as null.
 * */
public static boolean emptySpaceExists(Board b) {
    int size = b.size();
    for (int col = 0; col < size; col++) {
        for (int row = 0; row < size; row++) {
            if (b.tile(col, row) == null) {
                return true;
            }
        }
    }
    return false;
}

Task 2. public static boolean maxTileExists(Board b)

This is actually very similar to task 1, but at this time, we don't want to judge whether the grid is empty, we want to judge whether the value of the grid is a specific value, which is actually easy to think of Because we still solve this problem through traversal, but the judgment condition becomes: t.values() == MAX_PIECE.

/**
 * Returns true if any tile is equal to the maximum valid value.
 * Maximum valid value is given by MAX_PIECE. Note that
 * given a Tile object t, we get its value with t.value().
 */
public static boolean maxTileExists(Board b) {
    int size = b.size();
    for (int col = 0; col < size; col++) {
        for (int row = 0; row < size; row++) {
            Tile t = b.tile(col, row);
            // only when t != null should we check t.value()
            if (t != null && t.value() == MAX_PIECE) {
                return true;
            }
        }
    }
    return false;
}

Task 3. public static boolean atLeastOneMoveExists(Board b)

Indeed, this problem will be more challenging for novices The key to the problem is how to judge whether the square of 2048 can continue to play. There are two main situations

  1. At least one of all squares in 2048 is empty This can be done with emptySpaceExists() implemented earlier
  2. If there are adjacent grids with the same value in the moving direction, we can merge them at this time Obviously, we need to check four directions for each grid This is achieved by dx and dy, which represent increments in different directions
/**
 * Returns true if there are any valid moves on the board.
 * There are two ways that there can be valid moves:
 * 1. There is at least one empty space on the board.
 * 2. There are two adjacent tiles with the same value.
 */
public static boolean atLeastOneMoveExists(Board b) {
    if (emptySpaceExists(b)) {
        return true;
    }
    // 4 directions, LEFT/UP/RIGHT/DOWN
    int[] dx = {0, -1, 0, 1};
    int[] dy = {-1, 0, 1, 0};

    int size = b.size();
    for (int col = 0; col < size; col++) {
        for (int row = 0; row < size; row++) {
            // Because we have checked emptySpace, t.values() must exist
            int curTileValue = b.tile(col, row).value();
            for (int move = 0; move < 4; move++) {
                int colNew = col + dx[move];
                int rowNew = row + dy[move];
                // make sure the tile is within the boundary
                if (colNew > 0 && colNew < size && rowNew > 0 && rowNew < size) {
                    Tile newTile = b.tile(colNew, rowNew);
                    if (newTile.value() == curTileValue) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

Task 4. Building the Game Logic

It took me a lot of time to solve this task 😦

: happy: CS61B this course provides us with some functions intimately, so that we can only care about how to deal with this problem when going up We just need to add the corresponding function before and after our code ⬇️

board.setViewingPerspective(side);
// put your code here
board.setViewingPerspective(Side.NORTH);

The final solution to the problem is (one by one) ⬇️

  1. First move each empty grid to the "back" (merging is not considered at this time) Ensure that the front is a continuous effective grid The front and back here are relative to the direction we move You can take a look at the Gif diagram below to understand

  2. Then we will consider merging. We need to check whether the values of adjacent grids are the same. Start from the front and check whether it can be merged with the next adjacent grid. If so, move the back to the front for merging, and then deal with the next one For example [2, 2, 2, x] - [4, x, 2, x] - [4, 2, x, x] Then we'll move to the next position, 2, to see if 2 can merge with X

You can see more details in the code ⬇️

/** Tilt the board toward SIDE. Return true iff this changes the board.
 *
 * 1. If two Tile objects are adjacent in the direction of motion and have
 *    the same value, they are merged into one Tile of twice the original
 *    value and that new value is added to the score instance variable
 * 2. A tile that is the result of a merge will not merge again on that
 *    tilt. So each move, every tile will only ever be part of at most one
 *    merge (perhaps zero).
 * 3. When three adjacent tiles in the direction of motion have the same
 *    value, then the leading two tiles in the direction of motion merge,
 *    and the trailing tile does not.
 * */
public boolean tilt(Side side) {
    boolean changed;
    changed = false;

    /** The coordinates are:
     *  3
     *  2
     *  1
     *  0 1 2 3
     */
    board.setViewingPerspective(side);
    int size = board.size();
    for (int col = 0; col < size; col++) {
        // Step1. move every non-empty tile in order
        // [x, 2, 2, x] -> [2, 2, x, x]
        // skip merging this step.
        for (int row = size - 1; row >= 0; row--) {
            Tile t = board.tile(col, row);
            if (t != null) {
                // find nextPos which is null
                int nextPos = 3;
                while (nextPos >= row) {
                    if (board.tile(col, nextPos) == null) {
                        break;
                    }
                    nextPos--;
                }
                // check if nextPos is a legal position
                if (nextPos >= row) {
                    board.move(col, nextPos, t);
                    changed = true;
                }
            }
        }
        // Step2. try to merge
        // [2, 2, x, x] -> [4, x, x, x]
        for (int row = 3; row >= 0; row--) {
            Tile curTile = board.tile(col, row);
            int nextLine = row - 1;
            if (nextLine < 0) {
                break;
            }
            Tile nextTile = board.tile(col, nextLine);
            if (curTile == null || nextTile == null) {
                break;
            }
            int nextValue = nextTile.value();
            if (nextValue == curTile.value()) {
                board.move(col, row, nextTile);
                score += curTile.value() * 2;
                for (int p = nextLine - 1; p >= 0; p--) {
                    Tile tt = board.tile(col, p);
                    if (tt == null) {
                        break;
                    }
                    if (p < size) {
                        board.move(col, p + 1, tt);
                    }
                }
                changed = true;
            }
        }
    }
    board.setViewingPerspective(Side.NORTH);

    checkGameOver();
    if (changed) {
        setChanged();
    }
    return changed;
}

Play !

2048 games still have a sense of accomplishment when they run

The complete project file is in here👋👋👋

Added by CodeToad on Tue, 01 Feb 2022 15:58:56 +0200