This game is made for JFrame. You can control using the arrows. The numbers in the cells are the degrees of the number 2. It is possible to change the initial position of the window, its size, and the number of rows-columns(they are equal) in the field. You can also change the width of the frames of the cells, indent between them. You can adjust what degree of the number 2 you need to get in order to win. I hope that you point out to me weaknesses in my code, if any.
main method:
final int windowXPos = 100;
final int windowYPos = 100;
final int windowWidthAndHeight = 800;
final int spacesBetweenCells = 4;
final int widthOfFrame = 4;
final int inARow = 4;
final int valueOfCellToWin = 11;
GameWindow2048 window = new GameWindow2048(windowXPos,windowYPos, windowWidthAndHeight, spacesBetweenCells, widthOfFrame, inARow, valueOfCellToWin);
class Area skeleton:
public class Area {
Cell[][] area;
final int valueOfCellToWin;
public Area(int inARow, int valueOfCellToWin) {
area = new Cell[inARow][inARow];
fillAreaWithEmpty();
addNewCell();
addNewCell();
this.valueOfCellToWin = valueOfCellToWin;
}
private void fillAreaWithEmpty() {
for(int y = 0; y < area.length; y++) {
for(int x = 0; x < area[0].length; x++) {
area[y][x] = new Cell();
area[y][x].setEmpty();
}
}
}
void playAgain() {
fillAreaWithEmpty();
addNewCell();
addNewCell();
}
boolean isWin() {}
boolean addNewCell(){}
void makeAMove(Direction direction) {}
private int getAmountOfEmptyCells() {}
}
class Area full code:
public class Area {
Cell[][] area;
final int valueOfCellToWin;
public Area(int inARow, int valueOfCellToWin) {
area = new Cell[inARow][inARow];
fillAreaWithEmpty();
addNewCell();
addNewCell();
this.valueOfCellToWin = valueOfCellToWin;
}
private void fillAreaWithEmpty() {
for(int y = 0; y < area.length; y++) {
for(int x = 0; x < area[0].length; x++) {
area[y][x] = new Cell();
area[y][x].setEmpty();
}
}
}
void playAgain() {
fillAreaWithEmpty();
addNewCell();
addNewCell();
}
boolean isWin() {
for(int y = 0; y < area.length; y++) {
for(int x = 0; x < area[0].length; x++) {
if(area[y][x].getPowerOf2() == valueOfCellToWin) {
return true;
}
}
}
return false;
}
boolean addNewCell(){
int amountOfEmptyCells = getAmountOfEmptyCells();
int numOfCellToFill;
int counterOfEmptyCells = 0;
if(amountOfEmptyCells == 0) {
return false;
}
numOfCellToFill = Math.abs(new Random().nextInt()%amountOfEmptyCells)+1;
out:
for(int y = 0; y < area.length; y++) {
for(int x = 0; x < area[0].length; x++) {
if(area[y][x].isEmpty()) {
counterOfEmptyCells++;
if(counterOfEmptyCells == numOfCellToFill) {
area[y][x].setBeginNumber();
break out;
}
}
}
}
return true;
}
void makeAMove(Direction direction) {
if(direction == Direction.UP) {
for(int x = 0; x < area[0].length; x++) {
for(int y = 1; y <= area.length - 1; y++) {
if(y == 0) {
continue;
}
if(area[y][x].isEmpty()) {
continue;
}
if(area[y-1][x].isEmpty()) { //empty above - move there
area[y-1][x].setPowerOf2(area[y][x].getPowerOf2());
area[y][x].setEmpty();
y-=2; //as we may need to move this cell even higher.
continue;
}
if(area[y][x].getPowerOf2() == area[y-1][x].getPowerOf2()) { // are the same? make a merge
area[y][x].setEmpty();
area[y-1][x].increasePowerOf2();
// merging occurs, above the cell y-1 x there are no empty cells according to the algorithm, therefore we do not return
}
}
}
}
if(direction == Direction.DOWN) {
for(int x = 0; x < area[0].length; x++) {
for(int y = area.length - 2; y >= 0; y--) {
if(y == area.length - 1) {
continue;
}
if(area[y][x].isEmpty()) {
continue;
}
if(area[y+1][x].isEmpty()) {
area[y+1][x].setPowerOf2(area[y][x].getPowerOf2()); //empty below - move there
area[y][x].setEmpty();
y+=2; //as we may need to move this cell even lower.
continue;
}
if(area[y][x].getPowerOf2() == area[y+1][x].getPowerOf2()) {
area[y][x].setEmpty();
area[y+1][x].increasePowerOf2();
// merging occurs, below the cell y + 1 x there are no empty cells according to the algorithm, so do not return
}
}
}
}
if(direction == Direction.RIGHT) {
for(int y = 0; y < area.length; y++) {
for(int x = area[0].length - 2; x >=0; x--) {
if(x == area.length - 1) {
continue;
}
if(area[y][x].isEmpty()) {
continue;
}
if(area[y][x+1].isEmpty()) {
area[y][x+1].setPowerOf2(area[y][x].getPowerOf2());
area[y][x].setEmpty();
x+=2; //as we may need to move this cell to the right.
continue;
}
if(area[y][x].getPowerOf2() == area[y][x+1].getPowerOf2()) {
area[y][x].setEmpty();
area[y][x+1].increasePowerOf2();
//merging occurs, to the right of the cell y x+1 there are no empty cells according to the algorithm, so do not return
}
}
}
}
if(direction == Direction.LEFT) {
for(int y = 0; y < area.length; y++) {
for(int x = 1; x <= area[0].length-1; x++) {
if(x == 0) {
continue;
}
if(area[y][x].isEmpty()) {
continue;
}
if(area[y][x-1].isEmpty()) {
area[y][x-1].setPowerOf2(area[y][x].getPowerOf2());
area[y][x].setEmpty();
x-=2; // as we may need to move this cell to the left.
continue;
}
if(area[y][x].getPowerOf2() == area[y][x-1].getPowerOf2()) {
area[y][x].setEmpty();
area[y][x-1].increasePowerOf2();
//merging occurs, to the left of the cell y x-1 there are no empty cells according to the algorithm, so do not return
}
}
}
}
}
private int getAmountOfEmptyCells() {
int amountOfEmptyCells = 0;
for(int y = 0; y < area.length; y++) {
for(int x = 0; x < area[0].length; x++) {
if(area[y][x].isEmpty()) {
amountOfEmptyCells++;
}
}
}
return amountOfEmptyCells;
}
Cell[][] getArea() {
return area;
}
}
enum GamesState fullcode:
enum GamesState{
CONTINUETHEGAME, WIN, DEFEAT
}
class Cell fullcode:
class Cell{
private int powerOf2; // 0 - is emptyCell
void setBeginNumber() {
powerOf2 = (Math.abs(new Random().nextInt()%10) != 9) ? 1 : 2;
}
void increasePowerOf2() {
this.powerOf2++;
}
int getPowerOf2() {
return powerOf2;
}
boolean isEmpty() {
return powerOf2 == 0;
}
void setPowerOf2(int powerOf2) {
this.powerOf2 = powerOf2;
}
void setEmpty() {
this.powerOf2 = 0;
}
}
class Component:
public class Component extends JPanel {
final Cell[][] area;
final int windowWidthAndHeight;
final int widthOfOneCellInsideFrame;
final int spacesBetweenCells;
final int widthOfFrame;
final int widthOfRectOfFrame;
final int fontSize;
final Font font;
GamesState statusOfGame = GamesState.CONTINUETHEGAME;
public Component(Cell[][] area, int windowWidthAndHeight, int spacesBetweenCells, int widthOfFrame) {
this.area = area;
this.windowWidthAndHeight = windowWidthAndHeight;
this.spacesBetweenCells = spacesBetweenCells;
this.widthOfFrame = widthOfFrame;
widthOfOneCellInsideFrame = (windowWidthAndHeight - (2 * area.length * widthOfFrame + (area.length - 1) * spacesBetweenCells)) / area.length;
widthOfRectOfFrame = widthOfFrame * 2 + widthOfOneCellInsideFrame;
fontSize = widthOfOneCellInsideFrame / 3;
font = new Font("Tahoma", Font.BOLD|Font.ITALIC, fontSize);
}
@Override
public void paintComponent(Graphics gr){
gr.clearRect(0, 0, windowWidthAndHeight, windowWidthAndHeight);
gr.setColor(Color.lightGray);
gr.fillRect(0,0, windowWidthAndHeight, windowWidthAndHeight);
System.out.println(statusOfGame);
if(statusOfGame == GamesState.CONTINUETHEGAME) {
printArea(gr);
} else if (statusOfGame == GamesState.WIN){
gr.setColor(Color.BLUE);
gr.setFont(new Font("Tahoma", Font.BOLD|Font.ITALIC, windowWidthAndHeight / 25));
gr.drawString("U WON! If u wanna play again, press 'r'", windowWidthAndHeight/6,windowWidthAndHeight/3);
} else {
gr.setColor(Color.RED);
gr.setFont(new Font("Tahoma", Font.BOLD|Font.ITALIC, windowWidthAndHeight / 25));
gr.drawString("DEFEAT! If u wanna try again, press 'r'", windowWidthAndHeight/6,windowWidthAndHeight/3);
}
}
void printArea(Graphics gr) {
for(int y = 0, YPos; y < area.length; y++) {
for(int x = 0, XPos; x < area[0].length; x++) {
YPos = 2 * y * widthOfFrame;
XPos = 2 * x * widthOfFrame;
if(y != 0) {
YPos += y * (spacesBetweenCells + widthOfOneCellInsideFrame);
}
if(x != 0) {
XPos += x * (spacesBetweenCells + widthOfOneCellInsideFrame);
}
gr.setColor(Color.BLACK);
gr.fillRect(XPos,YPos, widthOfRectOfFrame, widthOfRectOfFrame);
gr.setColor(Color.BLUE);
gr.fillRect(XPos + widthOfFrame,YPos + widthOfFrame, widthOfOneCellInsideFrame, widthOfOneCellInsideFrame);
gr.setColor(Color.MAGENTA);
if(!area[y][x].isEmpty()) {
gr.setFont(font);
gr.drawString(Integer.toString(area[y][x].getPowerOf2()), XPos + widthOfFrame + (widthOfOneCellInsideFrame / 2) - fontSize / 4, YPos + widthOfFrame + (widthOfOneCellInsideFrame / 2) - fontSize / 4);
}
}
}
}
public void setGameState(GamesState statusOfGame) {
this.statusOfGame = statusOfGame;
}
}
class GameWindow2048 with enum Direction:
public class GameWindow2048 extends JFrame{
final int windowWidthAndHeight;
final int spacesBetweenCells;
final int widthOfFrame;
final Area area;
final Component component;
Direction directionTemp;
GamesState statusOfGame = GamesState.CONTINUETHEGAME;
public GameWindow2048(int windowXPos, int windowYPos, int windowWidthAndHeight, int spacesBetweenCells, int widthOfFrame, int inARow, int valueOfCellToWin){ // сделать builder
this.windowWidthAndHeight = windowWidthAndHeight;
this.spacesBetweenCells = spacesBetweenCells;
this.widthOfFrame = widthOfFrame;
addKeyListener(new myKey());
setFocusable(true);
setBounds(windowXPos,windowYPos,windowWidthAndHeight,windowWidthAndHeight + 20); //20 - калибровка
setTitle("2048");
Container container = getContentPane();
area = new Area(inARow, valueOfCellToWin);
component = new Component(area.getArea(), windowWidthAndHeight - 14, spacesBetweenCells, widthOfFrame); //14 - калибровка
container.add(component);
setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public class myKey implements KeyListener{
public void keyReleased(KeyEvent e) { //game
if(statusOfGame == GamesState.CONTINUETHEGAME) {
directionTemp = getDirection(e);
if(directionTemp != null) {
area.makeAMove(directionTemp);
if(!area.addNewCell()) {
statusOfGame = GamesState.DEFEAT;
component.setGameState(statusOfGame);
}
if(area.isWin()) {
System.out.println("Win");
statusOfGame = GamesState.WIN;
component.setGameState(statusOfGame);
}
}
component.repaint();
} else {
System.out.println(e.getKeyCode());
if(e.getKeyCode() == 82) {
area.playAgain();
statusOfGame = GamesState.CONTINUETHEGAME;
component.setGameState(statusOfGame);
component.repaint();
}
}
}
Direction getDirection(KeyEvent e) {
switch(e.getKeyCode()) {
case 39:
return Direction.RIGHT;
case 37:
return Direction.LEFT;
case 38:
return Direction.UP;
case 40:
return Direction.DOWN;
default:
return null;
}
}
public void keyPressed(KeyEvent e){}
public void keyTyped(KeyEvent e){}
}
enum Direction{
RIGHT, LEFT, UP, DOWN
}
}