I tried to get back into Java by implementing a simplified version of the 2048 game. What I am mainly looking for is ways to reduce code duplication, all of my move functions have a very similar but distinct pattern.
Suggestions on better patterns, practices or optimizations are welcome, keeping in mind that I am trying to implement my functionality without relying on any Libraries (built-in or otherwise).
public class Game { int[][] board; int COLNUM = 4; int ROWNUM = 4; int globalPointer = 0; // demonstration code to illustrate usage public static void main (String[] args) { Game game = new Game(); game.printBoard(); game.moveSouth(); game.printBoard(); game.moveWest(); game.printBoard(); game.moveWest(); game.printBoard(); game.moveEast(); game.printBoard(); game.moveNorth(); game.printBoard(); } // simplified constructor, could add option to provide 2D array to instantiate the board public Game() { board = new int[ROWNUM][COLNUM]; board[0][0] = 2; board[1][0] = 8; board[2][0] = 4; board[3][0] = 8; board[0][1] = 1; board[0][2] = 2; board[0][3] = 1; board[2][1] = 4; board[2][2] = 4; board[2][3] = 4; } public void printBoard() { String output = ""; for (int i = 0; i < ROWNUM; i++) { String row = ""; for (int j = 0; j < COLNUM; j++) { row += board[i][j] + ","; } row += "\n"; output += row; } System.out.println(output); } public void moveNorth() { for (int col = 0; col < COLNUM; col++) { globalPointer = 0; for (int row = 0; row < ROWNUM; row++) { // if not zero we try to suck in the next tile if (board[row][col] != 0) { // if we had a prior zero tile then shift the column if (globalPointer <= row) { shiftRowTiles(row, col, false); } } } } } public void moveSouth() { for (int col = 0; col < COLNUM; col++) { globalPointer = ROWNUM - 1; for (int row = ROWNUM -1; row >= 0; row--) { // if not zero we try to suck in the next tile if (board[row][col] != 0) { // if we had a prior zero tile then shift the column if (globalPointer >= row) { shiftRowTiles(row, col, true); } } } } } public void moveWest() { for (int row = 0; row < ROWNUM; row++) { globalPointer = 0; for (int col = 0; col < COLNUM; col++) { // if not zero we try to suck in the next tile if (board[row][col] != 0) { // if we had a prior zero tile then shift the row if (globalPointer <= col) { shiftColTiles(row, col, false); } } } } } public void moveEast() { for (int row = 0; row < ROWNUM; row++) { globalPointer = COLNUM - 1; for (int col = COLNUM - 1; col >= 0; col--) { // if not zero we try to suck in the next tile if (board[row][col] != 0) { // if we had a prior zero tile then shift the row if (globalPointer >= col) { shiftColTiles(row, col, true); } } } } } private void shiftRowTiles(int currentRow, int currentCol, boolean reverse) { // if we have same tiles we suck them in if (board[globalPointer][currentCol] == 0 || board[globalPointer][currentCol] == board[currentRow][currentCol]) { if (currentRow > globalPointer || (reverse && (globalPointer > currentRow))) { board[globalPointer][currentCol] += board[currentRow][currentCol]; board[currentRow][currentCol] = 0; } } else { if (reverse) { globalPointer--; } else { globalPointer++; } // look around to shift away zeroes in sequences shiftRowTiles(currentRow, currentCol, reverse); } } private void shiftColTiles(int currentRow, int currentCol, boolean reverse) { // if we have same tiles we suck them in if (board[currentRow][globalPointer] == 0 || board[currentRow][globalPointer] == board[currentRow][currentCol]) { if (currentCol > globalPointer || (reverse && (globalPointer > currentCol))) { board[currentRow][globalPointer] += board[currentRow][currentCol]; board[currentRow][currentCol] = 0; } } else { if (reverse) { globalPointer--; } else { globalPointer++; } // look around to shift away zeroes in sequences shiftColTiles(currentRow, currentCol, reverse); } } }