Java Project – 2048 Game
FREE Online Courses: Dive into Knowledge for Free. Learn More!
In the world of addictive puzzle games, 2048 stands as an outstanding gem. This innovative and mind-boggling game challenges players to combine numbers, striving to reach the challenging 2048 tile. With a simple yet elegant design, 2048 offers a delightful experience for players of all ages.
Prepare to embark on a thrilling journey of number manipulation, as you strive to achieve the highest score possible. But beware, as the grid fills up, making every move count becomes increasingly crucial.
Are you up for the challenge? Join us in this blog series as we delve deeper into problem solving. This project serves as an excellent opportunity to strengthen our understanding of Java while honing our problem-solving skills through the implementation of an engaging and challenging game. Whether you’re a novice or a seasoned player, there’s something here for everyone.
Get ready to exercise your brain, unleash your problem-solving skills, and enjoy the addictive nature of 2048. Let’s dive in and unlock the secrets of this mesmerizing puzzle game!
About 2048 Game Using Java
We will implement the following functionalities in the Java 2048 Game Project :
- We will define the Game2048 class with the main game logic.
- The game will be played on a 4×4 game board represented by a 2D array and the winning tile value will be set to 2048.
- Static variables will be used to declare the game board, score, and game over flag.
- The main method will serve as the program’s entry point, displaying a welcome message and instructions to the user.
- The initializeBoard method will set up the game board, initialize the score and game over flag, and generate two initial tiles using generateNewTile.
- The printBoard method will display the current game board state, including the score.
- The generateNewTile method will randomly generate a new tile (either 2 or 4) in an empty cell on the board.
- The generateNewTileOptimized method will improve tile generation by favoring cells with neighboring tiles of the same value.
- The moveTiles method will allow user input (w, s, a, or d) and call the appropriate movement method: moveUp, moveDown, moveLeft, or moveRight.
- Movement methods (moveUp, moveDown, moveLeft, and moveRight) will handle tile movement and merging, updating the score accordingly.
- The hasValidMoves method will check for any valid moves left on the board.
- The isBoardFull method will check if the game board is completely filled with tiles.
- The checkForWin method will determine if the winning tile (2048) is present, indicating the player has won the game.
- The main while loop in the main method will continue until the game over flag is set. After each move, the board will be printed, and a new tile will be generated.
- When the game over condition is met (no valid moves or full board), the loop will exit, and the final score will be displayed. The player will have an option to replay or exit the program.
Prerequisites For Java 2048 Game
To write and run the Java 2048 Game Project code, the following prerequisites are required:
1. Basic understanding of the Java programming language, including knowledge of loops, conditional statements, and object-oriented programming concepts.
2. Familiarity with array data structures and the Java.util package.
3. A working Java development environment, which includes installing the Java Development Kit (JDK) on your computer. You can download the JDK from the official Oracle website or use a package manager specific to your operating system.
4. An Integrated Development Environment (IDE) like IntelliJ IDEA, Eclipse, or NetBeans, which can greatly simplify Java development. Choose an IDE you are comfortable with and set it up.
Download Java 2048 Game Project Code
Please download the source code of the Java 2048 Game Project: Java 2048 Game Project Code.
Code Breakdown
1. Import Statements, Class Definition, Constant Declarations and Class Variables:
• The code starts with import statements to import necessary classes from the Java Standard Library: java.util.Random and java.util.Scanner.
• The class Game2048 is defined, which contains the main logic of the 2048 game.
• The following constants are declared:
• SIZE: Represents the size of the game board (4×4 for a standard 2048 game).
• WINNING_TILE: Represents the target tile value the player needs to reach to win the game (2048 in this case).
• The class contains the following class-level variables:
• board: A 2D integer array representing the game board.
• score: An integer representing the player’s score.
• gameOver: A boolean variable indicating whether the game is over or not.
import java.util.Random;
import java.util.Scanner;
public class Game2048 {
private static final int SIZE = 4;
private static final int WINNING_TILE = 2048;
private static int[][] board;
private static int score;
private static boolean gameOver;
2. main Method:
• Entry point of the program, where the game execution starts.
• Displays welcome messages, instructions, and rules of the game.
• Calls initializeBoard, printBoard, moveTiles, generateNewTileOptimized, isBoardFull, and checkForWin functions in a loop to handle the game logic.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Welcome to the 2048 Game by ProjectGurukul!");
System.out.println("Use the following keys to move:");
System.out.println("w - Up");
System.out.println("s - Down");
System.out.println("a - Left");
System.out.println("d - Right");
System.out.println("Try to reach " + WINNING_TILE + " to win!");
do {
initializeBoard(); // Initialize the game board
printBoard(); // Print the initial game board
while (!gameOver) {
moveTiles(); // Allow the user to move the tiles
generateNewTileOptimized(); // Generate a new tile with higher probability of a match
printBoard(); // Print the updated game board
}
if (isBoardFull()) {
System.out.println("Game Over!");
} else if (checkForWin()) {
System.out.println("Congratulations! You won!");
}
System.out.println("Your Score: " + score);
System.out.print("Do you want to replay the game? (yes/no): ");
String replayChoice = scanner.nextLine().toLowerCase();
if (!replayChoice.equals("yes")) {
System.out.println("Thank you for playing the 2048 Game by ProjectGurukul. Goodbye!");
break;
}
} while (true);
}
3. initializeBoard Method:
• Initializes the game board and sets the score and game over a flag.
• Generates two initial tiles with values of either 2 or 4 at random positions.
private static void initializeBoard() {
board = new int[SIZE][SIZE]; // Create the game board
score = 0; // Initialize the score
gameOver = false; // Set the game over flag to false
generateNewTile(); // Generate the initial tiles
generateNewTile();
}
4. printBoard Method:
• Prints the current state of the game board and the player’s score.
private static void printBoard() {
System.out.println("Score: " + score);
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
System.out.print(board[i][j] + "\t");
}
System.out.println();
}
}
5. generateNewTile Method:
• Generates a new tile with a value of either 2 or 4 at a random empty cell on the game board.
private static void generateNewTile() {
Random random = new Random();
int value = random.nextInt(10) < 9 ? 2 : 4; // Generate a new tile value (90% chance of 2, 10% chance of 4)
while (true) {
int row = random.nextInt(SIZE); // Generate random row and column indices
int col = random.nextInt(SIZE);
if (board[row][col] == 0) { // Check if the cell is empty
board[row][col] = value; // Place the new tile in the empty cell
break;
}
}
}
6. generateNewTileOptimized Method:
• Generates a new tile with a value of either 2 or 4 at an empty cell that has neighboring tiles with the same value.
• If no such cells are found, generate the new tile in any random empty cell.
private static void generateNewTileOptimized() {
Random random = new Random();
int value = random.nextInt(10) < 9 ? 2 : 4; // Generate a new tile value (90% chance of 2, 10% chance of 4)
// Find all empty cells with neighboring tiles of the same value
int[] emptyRow = new int[SIZE * SIZE];
int[] emptyCol = new int[SIZE * SIZE];
int emptyCount = 0;
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) {
if ((row > 0 && board[row - 1][col] == value)
|| (row < SIZE - 1 && board[row + 1][col] == value)
|| (col > 0 && board[row][col - 1] == value)
|| (col < SIZE - 1 && board[row][col + 1] == value)) {
emptyRow[emptyCount] = row;
emptyCol[emptyCount] = col;
emptyCount++;
}
}
}
}
if (emptyCount > 0) {
int index = random.nextInt(emptyCount);
int row = emptyRow[index];
int col = emptyCol[index];
board[row][col] = value;
} else {
// If there are no empty cells with neighboring tiles of the same value,
// generate the new tile in any random empty cell.
while (true) {
int row = random.nextInt(SIZE);
int col = random.nextInt(SIZE);
if (board[row][col] == 0) {
board[row][col] = value;
break;
}
}
}
}
7. moveTiles Method:
• Allows the user to move the tiles on the game board based on their input (w, s, a, or d).
• Calls moveUp, moveDown, moveLeft, or moveRight based on the user’s input.
• Checks if the game is over by verifying if the board is full and there are no more valid moves.
private static void moveTiles() {
Scanner scanner = new Scanner(System.in);
String direction;
do {
direction = scanner.nextLine().toLowerCase(); // Get the user's move direction
switch (direction) {
case "w":
moveUp(); // Move the tiles upwards
break;
case "s":
moveDown(); // Move the tiles downwards
break;
case "a":
moveLeft(); // Move the tiles to the left
break;
case "d":
moveRight(); // Move the tiles to the right
break;
default:
System.out.println("Invalid direction! Please try again.");
break;
}
} while (!direction.equals("w") && !direction.equals("s") && !direction.equals("a") && !direction.equals("d"));
if (!gameOver) {
if (isBoardFull() && !hasValidMoves()) {
gameOver = true; // Set the game over flag if the board is full and no more valid moves can be made
System.out.println("No more valid moves. Game Over!");
System.out.print("Do you want to try again? (yes/no): ");
String tryAgainChoice = scanner.nextLine().toLowerCase();
if (tryAgainChoice.equals("yes")) {
initializeBoard(); // Reset the game board and start a new round
printBoard();
gameOver = false; // Reset the game over flag for a new game
} else {
System.out.println("Thank you for playing the 2048 Game by ProjectGurukul. Goodbye!");
}
}
}
}
8. moveUp, moveDown, moveLeft, and moveRight Methods:
• Handle the movement of tiles in the specified direction.
• Merge identical adjacent tiles and slide the tiles in the given direction.
• Update the score accordingly when two tiles are merged.
private static void moveUp() {
for (int col = 0; col < SIZE; col++) {
for (int row = 1; row < SIZE; row++) {
if (board[row][col] != 0) {
int currentRow = row;
while (currentRow > 0 && board[currentRow - 1][col] == 0) {
board[currentRow - 1][col] = board[currentRow][col];
board[currentRow][col] = 0;
currentRow--;
}
if (currentRow > 0 && board[currentRow - 1][col] == board[currentRow][col]) {
board[currentRow - 1][col] *= 2;
score += board[currentRow - 1][col];
board[currentRow][col] = 0;
}
}
}
}
}
private static void moveDown() {
for (int col = 0; col < SIZE; col++) {
for (int row = SIZE - 2; row >= 0; row--) {
if (board[row][col] != 0) {
int currentRow = row;
while (currentRow < SIZE - 1 && board[currentRow + 1][col] == 0) {
board[currentRow + 1][col] = board[currentRow][col];
board[currentRow][col] = 0;
currentRow++;
}
if (currentRow < SIZE - 1 && board[currentRow + 1][col] == board[currentRow][col]) {
board[currentRow + 1][col] *= 2;
score += board[currentRow + 1][col];
board[currentRow][col] = 0;
}
}
}
}
}
private static void moveLeft() {
for (int row = 0; row < SIZE; row++) {
for (int col = 1; col < SIZE; col++) {
if (board[row][col] != 0) {
int currentCol = col;
while (currentCol > 0 && board[row][currentCol - 1] == 0) {
board[row][currentCol - 1] = board[row][currentCol];
board[row][currentCol] = 0;
currentCol--;
}
if (currentCol > 0 && board[row][currentCol - 1] == board[row][currentCol]) {
board[row][currentCol - 1] *= 2;
score += board[row][currentCol - 1];
board[row][currentCol] = 0;
}
}
}
}
}
private static void moveRight() {
for (int row = 0; row < SIZE; row++) {
for (int col = SIZE - 2; col >= 0; col--) {
if (board[row][col] != 0) {
int currentCol = col;
while (currentCol < SIZE - 1 && board[row][currentCol + 1] == 0) {
board[row][currentCol + 1] = board[row][currentCol];
board[row][currentCol] = 0;
currentCol++;
}
if (currentCol < SIZE - 1 && board[row][currentCol + 1] == board[row][currentCol]) {
board[row][currentCol + 1] *= 2;
score += board[row][currentCol + 1];
board[row][currentCol] = 0;
}
}
}
}
}
9. hasValidMoves Method:
• Checks if there are any valid moves left on the game board.
• Returns true if there is an empty cell or adjacent cells with the same value, indicating the possibility of making a valid move.
private static boolean hasValidMoves() {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) {
return true; // If there is an empty cell, there is a valid move
}
if (row < SIZE - 1 && board[row][col] == board[row + 1][col]) {
return true; // If there are adjacent cells with the same value, there is a valid move
}
if (col < SIZE - 1 && board[row][col] == board[row][col + 1]) {
return true; // If there are adjacent cells with the same value, there is a valid move
}
}
}
return false; // If no valid moves are found, return false
}
10. isBoardFull Method:
• Checks if the game board is full (all cells are filled).
• Returns true if the board is full, indicating that no more tiles can be generated.
private static boolean isBoardFull() {
for (int[] row : board) {
for (int tile : row) {
if (tile == 0) {
return false; // Return false if any cell is empty
}
}
}
return true; // Return true if all cells are filled
}
11. checkForWin Method:
• Checks if the player has won the game by reaching the WINNING_TILE value (2048 in this case).
• Returns true if there is a tile with the value WINNING_TILE on the game board.
private static boolean checkForWin() {
for (int[] row : board) {
for (int tile : row) {
if (tile == WINNING_TILE) {
return true;
}
}
}
return false;
}
}
Code to illustrate the 2048 Game Project
import java.util.Random;
import java.util.Scanner;
public class Game2048 {
private static final int SIZE = 4;
private static final int WINNING_TILE = 2048;
private static int[][] board;
private static int score;
private static boolean gameOver;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Welcome to the 2048 Game by ProjectGurukul!");
System.out.println("Use the following keys to move:");
System.out.println("w - Up");
System.out.println("s - Down");
System.out.println("a - Left");
System.out.println("d - Right");
System.out.println("Try to reach " + WINNING_TILE + " to win!");
do {
initializeBoard(); // Initialize the game board
printBoard(); // Print the initial game board
while (!gameOver) {
moveTiles(); // Allow the user to move the tiles
generateNewTileOptimized(); // Generate a new tile with higher probability of a match
printBoard(); // Print the updated game board
}
if (isBoardFull()) {
System.out.println("Game Over!");
} else if (checkForWin()) {
System.out.println("Congratulations! You won!");
}
System.out.println("Your Score: " + score);
System.out.print("Do you want to replay the game? (yes/no): ");
String replayChoice = scanner.nextLine().toLowerCase();
if (!replayChoice.equals("yes")) {
System.out.println("Thank you for playing the 2048 Game by ProjectGurukul. Goodbye!");
break;
}
} while (true);
}
private static void initializeBoard() {
board = new int[SIZE][SIZE]; // Create the game board
score = 0; // Initialize the score
gameOver = false; // Set the game over flag to false
generateNewTile(); // Generate the initial tiles
generateNewTile();
}
private static void printBoard() {
System.out.println("Score: " + score);
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
System.out.print(board[i][j] + "\t");
}
System.out.println();
}
}
private static void generateNewTile() {
Random random = new Random();
int value = random.nextInt(10) < 9 ? 2 : 4; // Generate a new tile value (90% chance of 2, 10% chance of 4)
while (true) {
int row = random.nextInt(SIZE); // Generate random row and column indices
int col = random.nextInt(SIZE);
if (board[row][col] == 0) { // Check if the cell is empty
board[row][col] = value; // Place the new tile in the empty cell
break;
}
}
}
private static void generateNewTileOptimized() {
Random random = new Random();
int value = random.nextInt(10) < 9 ? 2 : 4; // Generate a new tile value (90% chance of 2, 10% chance of 4)
// Find all empty cells with neighboring tiles of the same value
int[] emptyRow = new int[SIZE * SIZE];
int[] emptyCol = new int[SIZE * SIZE];
int emptyCount = 0;
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) {
if ((row > 0 && board[row - 1][col] == value)
|| (row < SIZE - 1 && board[row + 1][col] == value)
|| (col > 0 && board[row][col - 1] == value)
|| (col < SIZE - 1 && board[row][col + 1] == value)) {
emptyRow[emptyCount] = row;
emptyCol[emptyCount] = col;
emptyCount++;
}
}
}
}
if (emptyCount > 0) {
int index = random.nextInt(emptyCount);
int row = emptyRow[index];
int col = emptyCol[index];
board[row][col] = value;
} else {
// If there are no empty cells with neighboring tiles of the same value,
// generate the new tile in any random empty cell.
while (true) {
int row = random.nextInt(SIZE);
int col = random.nextInt(SIZE);
if (board[row][col] == 0) {
board[row][col] = value;
break;
}
}
}
}
private static void moveTiles() {
Scanner scanner = new Scanner(System.in);
String direction;
do {
direction = scanner.nextLine().toLowerCase(); // Get the user's move direction
switch (direction) {
case "w":
moveUp(); // Move the tiles upwards
break;
case "s":
moveDown(); // Move the tiles downwards
break;
case "a":
moveLeft(); // Move the tiles to the left
break;
case "d":
moveRight(); // Move the tiles to the right
break;
default:
System.out.println("Invalid direction! Please try again.");
break;
}
} while (!direction.equals("w") && !direction.equals("s") && !direction.equals("a") && !direction.equals("d"));
if (!gameOver) {
if (isBoardFull() && !hasValidMoves()) {
gameOver = true; // Set the game over flag if the board is full and no more valid moves can be made
System.out.println("No more valid moves. Game Over!");
System.out.print("Do you want to try again? (yes/no): ");
String tryAgainChoice = scanner.nextLine().toLowerCase();
if (tryAgainChoice.equals("yes")) {
initializeBoard(); // Reset the game board and start a new round
printBoard();
gameOver = false; // Reset the game over flag for a new game
} else {
System.out.println("Thank you for playing the 2048 Game by ProjectGurukul. Goodbye!");
}
}
}
}
private static boolean hasValidMoves() {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) {
return true; // If there is an empty cell, there is a valid move
}
if (row < SIZE - 1 && board[row][col] == board[row + 1][col]) {
return true; // If there are adjacent cells with the same value, there is a valid move
}
if (col < SIZE - 1 && board[row][col] == board[row][col + 1]) {
return true; // If there are adjacent cells with the same value, there is a valid move
}
}
}
return false; // If no valid moves are found, return false
}
private static void moveUp() {
for (int col = 0; col < SIZE; col++) {
for (int row = 1; row < SIZE; row++) {
if (board[row][col] != 0) {
int currentRow = row;
while (currentRow > 0 && board[currentRow - 1][col] == 0) {
board[currentRow - 1][col] = board[currentRow][col];
board[currentRow][col] = 0;
currentRow--;
}
if (currentRow > 0 && board[currentRow - 1][col] == board[currentRow][col]) {
board[currentRow - 1][col] *= 2;
score += board[currentRow - 1][col];
board[currentRow][col] = 0;
}
}
}
}
}
private static void moveDown() {
for (int col = 0; col < SIZE; col++) {
for (int row = SIZE - 2; row >= 0; row--) {
if (board[row][col] != 0) {
int currentRow = row;
while (currentRow < SIZE - 1 && board[currentRow + 1][col] == 0) {
board[currentRow + 1][col] = board[currentRow][col];
board[currentRow][col] = 0;
currentRow++;
}
if (currentRow < SIZE - 1 && board[currentRow + 1][col] == board[currentRow][col]) {
board[currentRow + 1][col] *= 2;
score += board[currentRow + 1][col];
board[currentRow][col] = 0;
}
}
}
}
}
private static void moveLeft() {
for (int row = 0; row < SIZE; row++) {
for (int col = 1; col < SIZE; col++) {
if (board[row][col] != 0) {
int currentCol = col;
while (currentCol > 0 && board[row][currentCol - 1] == 0) {
board[row][currentCol - 1] = board[row][currentCol];
board[row][currentCol] = 0;
currentCol--;
}
if (currentCol > 0 && board[row][currentCol - 1] == board[row][currentCol]) {
board[row][currentCol - 1] *= 2;
score += board[row][currentCol - 1];
board[row][currentCol] = 0;
}
}
}
}
}
private static void moveRight() {
for (int row = 0; row < SIZE; row++) {
for (int col = SIZE - 2; col >= 0; col--) {
if (board[row][col] != 0) {
int currentCol = col;
while (currentCol < SIZE - 1 && board[row][currentCol + 1] == 0) {
board[row][currentCol + 1] = board[row][currentCol];
board[row][currentCol] = 0;
currentCol++;
}
if (currentCol < SIZE - 1 && board[row][currentCol + 1] == board[row][currentCol]) {
board[row][currentCol + 1] *= 2;
score += board[row][currentCol + 1];
board[row][currentCol] = 0;
}
}
}
}
}
private static boolean isBoardFull() {
for (int[] row : board) {
for (int tile : row) {
if (tile == 0) {
return false; // Return false if any cell is empty
}
}
}
return true; // Return true if all cells are filled
}
private static boolean checkForWin() {
for (int[] row : board) {
for (int tile : row) {
if (tile == WINNING_TILE) {
return true;
}
}
}
return false;
}
}
Java 2048 Game Output
Summary
In conclusion, the 2048 Game has captured the hearts and minds of players worldwide with its enticing blend of strategy, logic, and addictive gameplay. Throughout this blog series, we’ve explored the intricacies of this numerical puzzle, uncovering valuable insights and techniques to improve our performance. As we bid farewell to this blog series, let’s carry the spirit of 2048 into our daily lives.
Let’s approach challenges with a strategic mindset, embrace the joy of problem-solving, and savour the satisfaction that comes from achieving our goals. Remember, the journey towards 2048 is not just about the final score, but also about the experiences, growth, and enjoyment we gain along the way. So, keep sliding those tiles, keep pushing your limits, and continue to explore the boundless possibilities that lie within the captivating world of 2048.



