Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

2048 Puzzle

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 28

National College of Computer Studies

Paknajol, Kathmandu

A Report on 2048 Game using java

Submitted By: Submitted to:

Ishan Pradhan Mr. Yuba Raj Devkota


BIM 4th semester
Roll no:7
NCCS Reg No: NCCSBIM1075
Date: 03/14/2023
Abstract

This project involved implementing a digital version of the popular puzzle game 2048 using
Java. The game requires the player to move numbered tiles around a grid and combine them to
reach the goal of creating a tile with a value of 2048. The implementation included features such
as score tracking and a graphical user interface developed with Java's Swing library. The goal of
the project was to provide an entertaining and challenging game while demonstrating proficiency
in Java programming. The project was successful in achieving this objective, resulting in a fully
functional implementation of the 2048 game.

i
Acknowledgement

I would like to express my sincere gratitude to the faculty and staff of NCCS College for
providing me with an excellent learning environment and the opportunity to work on this project.
I would particularly like to thank our teacher, Mr. Yuba Raj Devkota, for his invaluable
guidance, support, and encouragement throughout the project. His expertise, insights, and
suggestions were instrumental in helping me to understand the complexities of the game and to
develop a functional and engaging implementation of 2048.

Finally, I would like to acknowledge the developers of the original 2048 game for creating such
an addictive and challenging puzzle that has inspired so many adaptations and variations. Their
creativity and innovation have set a high standard for game development and have provided me
with a valuable learning experience in Java programming.

ii
Table of Contents
Abstract.............................................................................................................................................i
Acknowledgement...........................................................................................................................ii
Introduction......................................................................................................................................1
Objectives........................................................................................................................................1
Problem Statement...........................................................................................................................2
DSA topics used..............................................................................................................................2
Source Codes...................................................................................................................................3
Board.java....................................................................................................................................3
ColorScheme.java......................................................................................................................11
Controls.java..............................................................................................................................12
Game.java..................................................................................................................................13
Grid.java....................................................................................................................................14
Tile.java.....................................................................................................................................17
Window.java..............................................................................................................................18
Main.java...................................................................................................................................20
Output............................................................................................................................................21
Conclusion.....................................................................................................................................22
Reference.......................................................................................................................................23
Introduction

2048 is a popular single player sliding block puzzle game that was created by Italian
developer Gabriele Cirulli in March 2014. The game is played on a 4x4 grid, and the objective is
to slide numbered tiles on the grid to combine them and create a tile with the number 2048.

Each turn, the player can slide all the tiles on the grid in one of four directions: up, down, left, or
right. If two tiles with the same number collide, they merge into a tile with double the value. For
example, if two tiles with the number 2 collide, they merge into a tile with the number 4. The
game is won when a tile with the number 2048 is created, but the game continues even after that
point.

Objectives

 Developing an interface that is visually appealing and easy to use.

 Implementing game mechanics that allow players to slide tiles in all four
directions and merge tiles with the same value.

 Creating an algorithm that generates new tiles on the grid after each move.

 Providing options for customizing the game, such as changing the tile colors or
grid size.

 Developing an efficient algorithm to check for game over conditions.

 Ensuring that the game is optimized for performance and can run on a variety of
devices and platforms.

1
Problem Statement

The problem statement of a 2048 game typically involves creating a challenging and engaging
gameplay experience for the player. The game involves a 4x4 grid of tiles that can be moved in
four directions: up, down, left, and right. The objective of the game is to slide tiles around the
grid and merge tiles with the same value to create a tile with a value of 2048.

The game becomes increasingly difficult as the player merges tiles and creates larger tiles with
higher values. The player must strategize and plan their moves carefully to avoid running out of
space on the grid and to prevent tiles from becoming stuck in corners or along the edges.

The problem statement also involves implementing efficient algorithms to slide and merge tiles,
generate new tiles, and check for game over conditions. The game should be optimized for
performance and should run smoothly on a variety of devices and platforms.

DSA topics used

 Arrays and matrices: To represent the game board and tiles, and to perform operations on
them such as merging and sliding.

 Stacks or queues: To implement undo functionality by storing previous game states.

 Searching and sorting: To search for available empty cells and to sort tiles based on their
value.

 Recursion: To implement the algorithms that slide tiles and merge tiles recursively.

 Random number generation: To generate new tiles with a value of 2 or 4 in random


locations on the game board.

 Graph traversal algorithms: To check for game over conditions, such as when the game
board is full and no more moves can be made.

2
 HashMap: To store key-value pairs of integer and Color objects.

Source Codes
Board.java
package game;

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

/**
* Board
* @author petrnemecek
*
*/
public class Board {

private int size; // size of the grid


private int score; // game score
private int emptyTiles; // number of tiles with zero
value
private int initTiles = 2; // number of tiles board starts with
(usually two tiles)
private boolean gameover = false; // game is over when 2048 tile is found
private String wonOrLost; // won or lost
private boolean genNewTile = false; // generate new tile when any tile moved
private List<List<Tile>> tiles; // board

/**
* {@link Constructor}
*
*/
public Board(int size) {
super();
this.size = size;
this.emptyTiles = this.size * this.size;
this.tiles = new ArrayList<>();

start();
}

3
private void initialize() {
for (int row = 0; row < this.size; row++) {
tiles.add(new ArrayList<Tile>());
for (int col = 0; col < this.size; col++) {
tiles.get(row).add(new Tile());
}
}
}

private void start() {


Game.CONTROLS.bind();
initialize();
genInitTiles();
//show();
}

public int getSize() {


return size;
}

public void setSize(int size) {


this.size = size;
}

public List<List<Tile>> getTiles() {


return tiles;
}

public void setTiles(List<List<Tile>> tiles) {


this.tiles = tiles;
}

public Tile getTileAt(int row, int col) {


return tiles.get(row).get(col);
}

public void setTileAt(int row, int col, Tile t) {


tiles.get(row).set(col, t);
}

public void remTileAt(int row, int col) {


4
tiles.get(row).remove(col);
}

public int getScore() {


return score;
}

/**
* merges two touching {@link Tile} with the same number into one
* @param sequence of {@link Tile}
* @return merged sequence of {@link Tile}
*/
private List<Tile> mergeTiles(List<Tile> sequence) {
for (int l = 0; l < sequence.size() - 1; l++) {
if (sequence.get(l).getValue() == sequence.get(l + 1).getValue()) {
int value;
if ((value = sequence.get(l).merging()) == 2048) {
gameover = true;
}
score += value;
sequence.remove(l + 1);
genNewTile = true; // board has changed its state
emptyTiles++;
}
}
return sequence;
}

/**
* creates empty {@link Tile} instances and adds them to the left (resp. top) of merged
sequence to fit the board
* @param merged sequence of {@link Tile}
* @return refilled sequence with required number of empty {@link Tile}
*/
private List<Tile> addEmptyTilesFirst(List<Tile> merged) {
for (int k = merged.size(); k < size; k++) {
merged.add(0, new Tile());
}
return merged;
}

5
/**
* creates empty {@link Tile} instances and adds them to the right (resp. bottom) of
merged sequence to fit the board
* @param merged sequence of {@link Tile}
* @return refilled sequence with required number of empty {@link Tile}
*/
private List<Tile> addEmptyTilesLast(List<Tile> merged) { // boolean last/first
for (int k = merged.size(); k < size; k++) {
merged.add(k, new Tile());
}
return merged;
}

private List<Tile> removeEmptyTilesRows(int row) {

List<Tile> moved = new ArrayList<>();

for (int col = 0; col < size; col++) {


if (!getTileAt(row, col).isEmpty()) { // NOT empty
moved.add(getTileAt(row, col));
}
}

return moved;
}

private List<Tile> removeEmptyTilesCols(int row) {

List<Tile> moved = new ArrayList<>();

for (int col = 0; col < size; col++) {


if (!getTileAt(col, row).isEmpty()) { // NOT empty
moved.add(getTileAt(col, row));
}
}

return moved;
}

private List<Tile> setRowToBoard(List<Tile> moved, int row) {


for (int col = 0; col < tiles.size(); col++) {
6
if (moved.get(col).hasMoved(row, col)) {
genNewTile = true;
}
setTileAt(row, col, moved.get(col));
}

return moved;
}

private List<Tile> setColToBoard(List<Tile> moved, int row) {


for (int col = 0; col < tiles.size(); col++) {
if (moved.get(col).hasMoved(col, row)) {
genNewTile = true;
}
setTileAt(col, row, moved.get(col));
}

return moved;
}

public void moveUp() {

List<Tile> moved;

for (int row = 0; row < size; row++) {

moved = removeEmptyTilesCols(row);
moved = mergeTiles(moved);
moved = addEmptyTilesLast(moved);
moved = setColToBoard(moved, row);

public void moveDown() {

List<Tile> moved;

for (int row = 0; row < size; row++) {

7
moved = removeEmptyTilesCols(row);
moved = mergeTiles(moved);
moved = addEmptyTilesFirst(moved);
moved = setColToBoard(moved, row);

public void moveLeft() {

List<Tile> moved;

for (int row = 0; row < size; row++) {

moved = removeEmptyTilesRows(row);
moved = mergeTiles(moved);
moved = addEmptyTilesLast(moved);
moved = setRowToBoard(moved, row);

public void moveRight() {

List<Tile> moved;

for (int row = 0; row < size; row++) {

moved = removeEmptyTilesRows(row);
moved = mergeTiles(moved);
moved = addEmptyTilesFirst(moved);
moved = setRowToBoard(moved, row);

public void isGameOver() {

8
if (gameover) {
// end(true);
setWonOrLost("WON");
} else {
if (isFull()) {
if (!isMovePossible()) {
// you lost (board is full with no tiles to merge)
// end(false);
setWonOrLost("LOST");
}

} else {
newRandomTile(); // game continues
}
}
}

private boolean isFull() {


return emptyTiles == 0;
}

private boolean isMovePossible() {


for (int row = 0; row < size; row++) {
for (int col = 0; col < size - 1; col++) {
if (getTileAt(row, col).getValue() == getTileAt(row, col +
1).getValue()) {
return true;
}
}
}

for (int row = 0; row < size - 1; row++) {


for (int col = 0; col < size; col++) {
if (getTileAt(col, row).getValue() == getTileAt(col, row +
1).getValue()) {
return true;
}
}
}
return false;
}
9
private void genInitTiles() {
for (int i = 0; i < initTiles; i++) {
genNewTile = true;
newRandomTile();
}
}

private void newRandomTile() {


if (genNewTile) {
int row;
int col;
int value = Math.random() < 0.9 ? 2 : 4;
do {
row = (int) (Math.random () * 4);
col = (int) (Math.random () * 4);
} while (getTileAt(row, col).getValue() != 0);
setTileAt(row, col, new Tile(value, row, col));
emptyTiles--;
genNewTile = false;
}
}

protected void show() {


for (int i = 0; i < 2; ++i) System.out.println();
System.out.println("SCORE: " + score);
for (int i = 0; i < tiles.size(); i++) {
for (int j = 0; j < tiles.get(i).size(); j++) {
System.out.format("%-5d", getTileAt(i, j).getValue());
}
System.out.println();
}
}

public String getWonOrLost() {


return wonOrLost;
}

public void setWonOrLost(String wonOrLost) {


this.wonOrLost = wonOrLost;
}
10
}

ColorScheme.java

package game;

import java.awt.Color;
import java.util.HashMap;

public class ColorScheme {

public final static Color WINBG = new Color (0XFAF8EF);


public final static Color GRIDBG = new Color (0XBBADA0);

public final static Color BRIGHT = new Color (0X776E65);


public final static Color LIGHT = new Color (0XF9F6F2);

private HashMap<Integer, Color> background = new HashMap<>();

public ColorScheme() {
initBackrounds();
}

private void initBackrounds() {


background.put(0, new Color (238, 228, 218, 90));
background.put(2, new Color (0XEEE4DA));
background.put(4, new Color (0XEDE0C8));
background.put(8, new Color (0XF2B179));
background.put(16, new Color (0XF59563));
background.put(32, new Color (0XF67C5F));
background.put(64, new Color (0XF65E3B));
background.put(128, new Color (0XEDCF72));
background.put(256, new Color (0XEDCC61));
background.put(512, new Color (0XEDC850));
background.put(1024, new Color (0XEDC53F));
background.put(2048, new Color (0XEDC22E));
}

public Color getTileBackground(int value) {

11
return background.get(value);
}

public Color getTileColor(int value) {


if (value <= 8) {
return BRIGHT;
} else {
return LIGHT;
}
}

Controls.java
package game;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Controls implements KeyListener {

@Override
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {

int keyCode = e.getKeyCode();

switch (keyCode) {
case KeyEvent.VK_UP:
Game.BOARD.moveUp();
break;
case KeyEvent.VK_DOWN:
Game.BOARD.moveDown();
break;
case KeyEvent.VK_LEFT:
Game.BOARD.moveLeft();
break;
case KeyEvent.VK_RIGHT:

12
Game.BOARD.moveRight();
break;
case KeyEvent.VK_ESCAPE:
Game.WINDOW.dispose();
break;
default:
break;
}

Game.BOARD.isGameOver();
//Game.BOARD.show();
Game.WINDOW.repaint();

public void bind() {


Game.WINDOW.addKeyListener(this);
}

public void unbind() {


Game.WINDOW.removeKeyListener(this);
}

Game.java
package game;

public class Game {


public static final ColorScheme COLORS = new ColorScheme();
public static final Window WINDOW = new Window("2048");
public static final Controls CONTROLS = new Controls();
public static final Board BOARD = new Board(4);

}
Grid.java
package game;

import java.awt.Color;
import java.awt.Font;

13
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JPanel;

public class Grid extends JPanel {

private static final long serialVersionUID = 1L;

private static final int TILE_RADIUS = 15;


private static final int WIN_MARGIN = 20;
private static final int TILE_SIZE = 65;
private static final int TILE_MARGIN = 15;
private static final String FONT = "Tahoma";

public Grid() {
super(true); // turn on doublebuffering
}

public void paintComponent(Graphics g2) {


super.paintComponent(g2);

Graphics2D g = ((Graphics2D) g2); // cast to get context for drawing

/* turn on antialiasing for smooth and non-pixelated edges */


g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

drawBackground(g);
drawTitle(g);
drawScoreBoard(g);
drawBoard(g);

g.dispose(); // release memory


}

private static void drawTitle(Graphics g) {


g.setFont( new Font(FONT, Font.BOLD, 38) );
14
g.setColor( ColorScheme.BRIGHT );
g.drawString("2048", WIN_MARGIN, 50);
}

private void drawScoreBoard(Graphics2D g) {


int width = 80;
int height = 40;
int xOffset = Game.WINDOW.getWidth() - WIN_MARGIN - width;
int yOffset = 20;
g.fillRoundRect(xOffset, yOffset, width, height, TILE_RADIUS,
TILE_RADIUS);
g.setFont( new Font(FONT, Font.BOLD, 10) );
g.setColor( new Color(0XFFFFFF) );
g.drawString("SCORE", xOffset + 22, yOffset + 15);
g.setFont( new Font(FONT, Font.BOLD, 12) );
g.drawString(String.valueOf(Game.BOARD.getScore()), xOffset + 35, yOffset +
30);
}

private static void drawBackground(Graphics g) {


g.setColor(ColorScheme.WINBG);
g.fillRect(0, 0, Game.WINDOW.getWidth(), Game.WINDOW.getHeight());

private static void drawBoard(Graphics g) {


g.translate(WIN_MARGIN, 80);
g.setColor(ColorScheme.GRIDBG);
g.fillRoundRect(0, 0, Game.WINDOW.getWidth() - (WIN_MARGIN * 2), 320 +
TILE_MARGIN, TILE_RADIUS, TILE_RADIUS);

for (int row = 0; row < 4; row++) {


for (int col = 0; col < 4; col++) {
drawTile(g, Game.BOARD.getTileAt(row, col), col, row);
}
}
}

private static void drawTile(Graphics g, Tile tile, int x, int y) {


int value = tile.getValue();
int xOffset = x * (TILE_MARGIN + TILE_SIZE) + TILE_MARGIN;
15
int yOffset = y * (TILE_MARGIN + TILE_SIZE) + TILE_MARGIN;
g.setColor(Game.COLORS.getTileBackground(value));
g.fillRoundRect(xOffset, yOffset, TILE_SIZE, TILE_SIZE, TILE_RADIUS,
TILE_RADIUS);

g.setColor(Game.COLORS.getTileColor(value));

final int size = value < 100 ? 36 : value < 1000 ? 32 : 24;
final Font font = new Font(FONT, Font.BOLD, size);
g.setFont(font);

String s = String.valueOf(value);
final FontMetrics fm = g.getFontMetrics(font);

final int w = fm.stringWidth(s);


final int h = -(int) fm.getLineMetrics(s, g).getBaselineOffsets()[2];

if (value != 0) {
Game.BOARD.getTileAt(y, x).setPosition(y, x); // tile gets its new
position
g.drawString(s, xOffset + (TILE_SIZE - w) / 2, yOffset + TILE_SIZE -
(TILE_SIZE - h) / 2 - 2);
}

if (Game.BOARD.getWonOrLost() != null && !


Game.BOARD.getWonOrLost().isEmpty()) {
g.setColor(new Color(255, 255, 255, 40));
g.fillRect(0, 0, Game.WINDOW.getWidth(),
Game.WINDOW.getHeight());
g.setColor(ColorScheme.BRIGHT);
g.setFont(new Font(FONT, Font.BOLD, 30));
g.drawString("You " + Game.BOARD.getWonOrLost() + "!", 68, 150);
Game.CONTROLS.unbind();
}

}
16
Tile.java
package game;

public class Tile {

private int value;


private int row;
private int col;

public Tile(int value) {


super();
this.value = value;
}

public Tile(int value, int row, int col) {


this.value = value;
this.row = row;
this.col = col;
}

public Tile() {
new Tile(0);
}

public int getValue() {


return value;
}

public void setValue(int value) {


this.value = value;
}

public void setPosition(int row, int col) {


this.row = row;
this.col = col;
}

public int merging() {

17
return (this.value += this.value);
}

/**
* checks whether numbered (nonzero) tile changes its position
* @param row
* @param col
* @return true if tile changed its position, false if not
*/
public boolean hasMoved(int row, int col) {
return (!isEmpty() && ((this.row != row) || (this.col != col)));
}

public boolean isEmpty() {


return (value == 0);
}

@Override
public String toString() {
return "Tile [value=" + value + "]";
}

Window.java
package game;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

18
/**
* Window
* @author petrnemecek
*
*/

public class Window extends JFrame {

/**
*
*/
private static final long serialVersionUID = -8804446439773037674L;
private int width = 375;
private int height = 450;

public Window(String title) {


super(title); // Window title

this.setLayout( new BorderLayout());


this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // close window to
exit
this.setSize(this.width, this.height);
this.setLocationRelativeTo(null); // centering

this.setResizable(false);
this.setFocusable(true); // set focus on window so KeyListener works

getContentPane().add( new Grid() );

JPanel panel = new JPanel();


panel.add(createSimpleButton("NEW GAME"));

panel.setPreferredSize(new Dimension(1000, 760));

this.setVisible(true); // show window

private static JButton createSimpleButton(String text) {


19
JButton button = new JButton(text);
button.setForeground(Color.BLACK);
button.setBackground(Color.WHITE);
Border line = new LineBorder(Color.BLACK);
Border margin = new EmptyBorder(5, 15, 5, 15);
Border compound = new CompoundBorder(line, margin);
button.setBorder(compound);
return button;
}

public int getWidth() {


return width;
}

public int getHeight() {


return height;
}

Main.java
package game;

public class Main {

public static void main(String[ ] args) {

new Game();

}
}

Output

20
Image 1: 2048 puzzle game

Image 2: Lose game screen.


Conclusion

In conclusion, the 2048 game created in Java is a simple yet addictive puzzle game that can be
enjoyed by people of all ages. As a developer, it was a fun and challenging project to work on,
and it allowed me to practice my skills in programming, game development, and user interface
design. Through this project, I learned the importance of planning, organization, and attention to

21
detail in software development. Overall, I hope that others will enjoy playing it as much as I
enjoyed making it.

Reference

2048 - Java. (2023, 03 09). From github: https://github.com/pnemi/2048-JAVA


Javapoint - Tutorials Lists. (2023, 03 09). From javapoint: www.javapoint.com

22
stack overflow. (2023, 03 09). From stack overflow: www.stackoverflow.com
w3schools. (2023, 03 09). From w3schools: www.w3schools.com

23

You might also like