2048 Game | Machine Coding | Feedback Required | OOD

Hi leetcode community, I am new to machine coding and system design world. Please review the below code for 2048 game. Feedback is welcome. Thanks.

State.java

package com.game.g2048;

public enum State {
    VICTORY, OVER, ONGOING
}

Direction.java

package com.game.g2048;

public enum Direction {
    UP, RIGHT, BOTTOM, LEFT
}

Board.java

package com.game.g2048;

import java.util.ArrayList;
import java.util.List;

public class Board {

    public static class Cell {
        private final int row;
        private final int column;

        public Cell(int row, int column) {
            this.row = row;
            this.column = column;
        }

        public int getRow() {
            return row;
        }

        public int getColumn() {
            return column;
        }
    }

    public int getSize() {
        return size;
    }

    private final int size;
    private List<List<Integer>> grid;

    public Board(int size) {
        this.size = size;
        grid = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            grid.add(new ArrayList<>(size));
            for (int j = 0; j < size; j++) {
                grid.get(i).add(0);
            }
        }
    }

    public void setCell(Cell cell, int num) {
        int row = cell.getRow();
        int column = cell.getColumn();
        if (row < 0 || column < 0 || row >= size || column >= size) {
            throw new RuntimeException("Invalid cell passed");
        }
        this.grid.get(cell.getRow()).set(cell.getColumn(), num);
    }

    public boolean isCellVacant(Cell cell) {
        return grid.get(cell.row).get(cell.column) == 0;
    }

    public boolean contains(int num) {
        for (List<Integer> row : grid) {
            for (Integer cellValue : row) {
                if (cellValue == num) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isFull() {
        for (List<Integer> row : grid) {
            for (Integer cellValue : row) {
                if (cellValue == 0) {
                    return false;
                }
            }
        }
        return true;
    }

    public void slideUp() {
        transpose();
        slideLeft();
        transpose();
    }

    public void slideDown() {
        transpose();
        slideRight();
        transpose();
    }

    public void slideRight() {
        reverse();
        slideLeft();
        reverse();
    }

    public void slideLeft() {
        for (int i = 0; i < size; i++) {
            grid.set(i, slideRowLeft(grid.get(i)));
        }
    }

    private List<Integer> slideRowLeft(List<Integer> row) {
        List<Integer> updatedRow = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            updatedRow.add(0);
        }
        int i = 0;
        int j = 0;
        while (i < size) {
            if (row.get(i) == 0){
                i++;
            } else if (row.get(i) != 0 && updatedRow.get(j) == 0) {
                updatedRow.set(j, row.get(i));
                i++;
            } else if (row.get(i) == updatedRow.get(j)) {
                updatedRow.set(j, updatedRow.get(j) + row.get(i));
                i++;
                j++;
            } else {
                j++;
            }
        }
        return updatedRow;
    }

    private void transpose() {
        List<List<Integer>> transpose = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            List<Integer> col = new ArrayList<>();
            for (List<Integer> row : grid) {
                col.add(row.get(i));
            }
            transpose.add(col);
        }
        grid = transpose;
    }

    private void reverse() {
        for (int i = 0; i < size; i++) {
            reverseRow(grid.get(i));
        }
    }

    private void reverseRow(List<Integer> row) {
        int i = 0, j = size - 1;
        while (i < j) {
            int temp = row.get(i);
            row.set(i, row.get(j));
            row.set(j, temp);
            i++;
            j--;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (List<Integer> row : grid) {
            for (Integer cellValue : row) {
                sb.append(cellValue == 0 ? "-" : cellValue.toString());
                sb.append(" ");
            }
            sb.append("\n");
        }
        return sb.toString();
    }

}

GameService.java

package com.game.g2048.service;

import com.game.g2048.Board;
import com.game.g2048.Direction;
import com.game.g2048.State;

import java.util.Random;

public class GameService {
    private final Board board;

    private static final int DEFAULT_GRID_SIZE = 4;

    public GameService(int gridSize) {
        this.board = new Board(gridSize);
    }

    public GameService() {
        this(GameService.DEFAULT_GRID_SIZE);
    }

    public void initialise() {

        Board.Cell randomCell1 = getRandomCell();
        Board.Cell randomCell2 = getRandomCell();

        while (randomCell2.getRow() == randomCell1.getRow() && randomCell2.getColumn() == randomCell1.getColumn()) {
            randomCell2 = getRandomCell();
        }

        board.setCell(randomCell1, 2);
        board.setCell(randomCell2, 2);

        System.out.println(board);
    }

    public State getCurrentState() {
        if (board.contains(2048)) {
            return State.VICTORY;
        } else if (board.isFull()) {
            return State.OVER;
        } else {
            return State.ONGOING;
        }
    }

    public void move(Direction directionToMove) {
        switch (directionToMove) {
            case UP:
                board.slideUp();
                break;
            case LEFT:
                board.slideLeft();
                break;
            case RIGHT:
                board.slideRight();
                break;
            case BOTTOM:
                board.slideDown();
                break;
        }
        Board.Cell randomCell = getRandomCell();
        while (!board.isCellVacant(randomCell)) {
            randomCell = getRandomCell();
        }
        board.setCell(randomCell, 2);
        System.out.println(board);
    }

    private Board.Cell getRandomCell() {
        Random random = new Random();
        return new Board.Cell(random.nextInt(board.getSize()), random.nextInt(board.getSize()));
    }
}

Main.java

package com.game;

import com.game.g2048.Direction;
import com.game.g2048.State;
import com.game.g2048.service.GameService;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        GameService gameService = new GameService();
        gameService.initialise();
        Scanner sc = new Scanner(System.in);
        System.out.println("\n0 - UP");
        System.out.println("1 - RIGHT");
        System.out.println("2 - BOTTOM");
        System.out.println("3 - LEFT");
        while (gameService.getCurrentState() == State.ONGOING) {
            int move = sc.nextInt();
            if (move < 0 || move > 3) {
                System.out.println("Please enter a valid move");
                continue;
            }
            Direction directionToMove = Direction.values()[move];
            gameService.move(directionToMove);
        }
        System.out.println(gameService.getCurrentState());
    }
}
Comments (2)