Skip to content

Commit

Permalink
Final: Game working
Browse files Browse the repository at this point in the history
  • Loading branch information
hoangsonww committed Nov 18, 2024
1 parent b41ec3e commit 358f99c
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 20 deletions.
28 changes: 28 additions & 0 deletions src/main/java/org/example/game2048javafx/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.example.game2048javafx;

public class Controller {
private final Model model;
private final View view;

public Controller(Model model, View view) {
this.model = model;
this.view = view;
}

public void startGame() {
model.resetBoard();
model.addRandomTile();
model.addRandomTile();
view.updateUI(model.getBoard(), model.getScore(), model.getBestScore());
}

public void handleKeyPress(String direction) {
if (model.move(direction)) {
model.addRandomTile();
view.updateUI(model.getBoard(), model.getScore(), model.getBestScore());
if (model.checkGameOver()) {
System.out.println("Game Over!");
}
}
}
}
59 changes: 41 additions & 18 deletions src/main/java/org/example/game2048javafx/Game2048.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javafx.animation.ScaleTransition;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
Expand All @@ -24,6 +25,8 @@ public class Game2048 extends GridPane {

private final Label scoreLabel = new Label("SCORE: 0");
private final Label bestScoreLabel = new Label("BEST: 0");
private final Label footerLabel = new Label("Join the numbers and get to the 2048 tile!");
private final Label gameOverLabel = new Label("Game Over! Try Again?");

public Game2048() {
setHgap(10);
Expand All @@ -36,6 +39,11 @@ public Game2048() {

scoreLabel.setFont(poppinsRegular);
bestScoreLabel.setFont(poppinsRegular);
footerLabel.setFont(poppinsRegular);
footerLabel.setStyle("-fx-text-fill: #776e65;");
gameOverLabel.setFont(poppinsBold);
gameOverLabel.setStyle("-fx-text-fill: red; -fx-font-size: 24px;");
gameOverLabel.setVisible(false);

// Initialize grid tiles
for (int row = 0; row < SIZE; row++) {
Expand Down Expand Up @@ -67,7 +75,7 @@ public Game2048() {
}

if (moved) {
addRandomTile();
addRandomTile(); // Add exactly one tile after a valid move
updateUIWithAnimations();
checkGameOver();
}
Expand Down Expand Up @@ -121,28 +129,40 @@ public HBox getHeader() {
return header;
}

// Create the footer (instruction label)
public Label getFooter() {
Font poppinsRegular = Font.loadFont(getClass().getResourceAsStream("/org/example/game2048javafx/fonts/Poppins-Regular.ttf"), 16);
// Create the footer with buttons
public VBox getFooter() {
Font poppinsRegular = Font.loadFont(getClass().getResourceAsStream("/org/example/game2048javafx/fonts/Poppins-Regular.ttf"), 18);

Label footerLabel = new Label("Join the numbers and get to the 2048 tile!");
footerLabel.setFont(poppinsRegular);
footerLabel.setStyle(
"-fx-font-size: 16px; " +
"-fx-text-fill: #776e65; " +
"-fx-padding: 10; " +
"-fx-background-radius: 15;");
footerLabel.setAlignment(Pos.CENTER);
footerLabel.setMinHeight(50);
footerLabel.setMaxWidth(Double.MAX_VALUE);
return footerLabel;
// New Game Button
Button newGameButton = new Button("New Game");
newGameButton.setFont(poppinsRegular);
newGameButton.setStyle("-fx-padding: 10; -fx-background-color: #8f7a66; -fx-text-fill: white; -fx-background-radius: 15;");
newGameButton.setOnAction(event -> {
startGame();
requestFocus(); // Ensure focus returns to the grid after button click
gameOverLabel.setVisible(false);
});

// Exit Game Button
Button exitGameButton = new Button("Exit Game");
exitGameButton.setFont(poppinsRegular);
exitGameButton.setStyle("-fx-padding: 10; -fx-background-color: #8f7a66; -fx-text-fill: white; -fx-background-radius: 15;");
exitGameButton.setOnAction(event -> System.exit(0));

HBox buttonContainer = new HBox(10, newGameButton, exitGameButton);
buttonContainer.setAlignment(Pos.CENTER);

VBox footer = new VBox(10, footerLabel, gameOverLabel, buttonContainer);
footer.setAlignment(Pos.CENTER);
footer.setStyle("-fx-padding: 10;");
return footer;
}

// Start the game
public void startGame() {
resetBoard();
addRandomTile();
addRandomTile();
addRandomTile(); // Always start with exactly two tiles
updateUI();
}

Expand Down Expand Up @@ -247,6 +267,10 @@ private boolean move(String direction) {
setLine(direction, i, merged);
}

if (moved) {
updateScores();
}

return moved;
}

Expand Down Expand Up @@ -289,7 +313,6 @@ private int[] mergeLine(int[] line) {
}
}

updateScores();
return merged;
}

Expand All @@ -316,6 +339,6 @@ private void checkGameOver() {
if (col < SIZE - 1 && board[row][col] == board[row][col + 1]) return; // Mergeable horizontally
}
}
System.out.println("Game Over!");
gameOverLabel.setVisible(true);
}
}
6 changes: 4 additions & 2 deletions src/main/java/org/example/game2048javafx/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
root.setTop(game.getHeader()); // Add header
root.setCenter(game); // Add game grid
root.setBottom(game.getFooter()); // Add footer label
root.setBottom(game.getFooter()); // Add footer

Scene scene = new Scene(root, 550, 650);
Scene scene = new Scene(root, 550, 700);
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm()); // Load CSS

scene.setOnKeyPressed(event -> game.fireEvent(event)); // Ensure focus remains on grid

primaryStage.setTitle("Play 2048!");
primaryStage.setScene(scene);
primaryStage.show();
Expand Down
119 changes: 119 additions & 0 deletions src/main/java/org/example/game2048javafx/Model.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.example.game2048javafx;

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

public class Model {
private final int SIZE = 4;
private final int[][] board = new int[SIZE][SIZE];
private int score = 0;
private int bestScore = 0;

public int[][] getBoard() {
return board;
}

public int getScore() {
return score;
}

public int getBestScore() {
return bestScore;
}

public void resetBoard() {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
board[row][col] = 0;
}
}
score = 0;
}

public void addRandomTile() {
List<int[]> emptySpaces = new ArrayList<>();
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) {
emptySpaces.add(new int[]{row, col});
}
}
}
if (!emptySpaces.isEmpty()) {
Random rand = new Random();
int[] space = emptySpaces.get(rand.nextInt(emptySpaces.size()));
board[space[0]][space[1]] = rand.nextDouble() < 0.9 ? 2 : 4;
}
}

public boolean move(String direction) {
boolean moved = false;
for (int i = 0; i < SIZE; i++) {
int[] line = getLine(direction, i);
int[] merged = mergeLine(line);
if (!arrayEquals(line, merged)) moved = true;
setLine(direction, i, merged);
}
return moved;
}

private int[] getLine(String direction, int index) {
int[] line = new int[SIZE];
for (int i = 0; i < SIZE; i++) {
switch (direction) {
case "UP" -> line[i] = board[i][index];
case "DOWN" -> line[i] = board[SIZE - 1 - i][index];
case "LEFT" -> line[i] = board[index][i];
case "RIGHT" -> line[i] = board[index][SIZE - 1 - i];
}
}
return line;
}

private void setLine(String direction, int index, int[] line) {
for (int i = 0; i < SIZE; i++) {
switch (direction) {
case "UP" -> board[i][index] = line[i];
case "DOWN" -> board[SIZE - 1 - i][index] = line[i];
case "LEFT" -> board[index][i] = line[i];
case "RIGHT" -> board[index][SIZE - 1 - i] = line[i];
}
}
}

private int[] mergeLine(int[] line) {
int[] merged = new int[SIZE];
int pos = 0;

for (int i = 0; i < SIZE; i++) {
if (line[i] == 0) continue;
if (pos > 0 && merged[pos - 1] == line[i]) {
merged[pos - 1] *= 2;
score += merged[pos - 1];
} else {
merged[pos++] = line[i];
}
}
if (score > bestScore) bestScore = score;
return merged;
}

private boolean arrayEquals(int[] a, int[] b) {
for (int i = 0; i < SIZE; i++) {
if (a[i] != b[i]) return false;
}
return true;
}

public boolean checkGameOver() {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) return false;
if (row < SIZE - 1 && board[row][col] == board[row + 1][col]) return false;
if (col < SIZE - 1 && board[row][col] == board[row][col + 1]) return false;
}
}
return true;
}
}
75 changes: 75 additions & 0 deletions src/main/java/org/example/game2048javafx/View.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.example.game2048javafx;

import javafx.animation.ScaleTransition;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.util.Duration;

public class View extends GridPane {
private final int SIZE = 4;
private final StackPane[][] tiles = new StackPane[SIZE][SIZE];
private final Label scoreLabel = new Label("SCORE: 0");
private final Label bestScoreLabel = new Label("BEST: 0");

public View() {
setHgap(10);
setVgap(10);
setAlignment(Pos.CENTER);
setStyle("-fx-padding: 10; -fx-background-color: #bbada0;");
}

public void setupGrid() {
Font poppinsBold = Font.loadFont(getClass().getResourceAsStream("/org/example/game2048javafx/fonts/Poppins-Bold.ttf"), 30);

for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
StackPane stack = new StackPane();
Rectangle bg = new Rectangle(100, 100);
bg.setArcWidth(15);
bg.setArcHeight(15);
bg.setFill(Color.web("#cdc1b4"));

Text text = new Text();
text.setFont(poppinsBold);
text.setFill(Color.web("#776e65"));
stack.getChildren().addAll(bg, text);
tiles[row][col] = stack;
add(stack, col, row);
}
}
}

public HBox getHeader() {
Font poppinsBold = Font.loadFont(getClass().getResourceAsStream("/org/example/game2048javafx/fonts/Poppins-Bold.ttf"), 32);

Label titleLabel = new Label("2048 Game");
titleLabel.setFont(poppinsBold);
titleLabel.setStyle(
"-fx-font-size: 32px; " +
"-fx-background-color: #edc22e; " +
"-fx-text-fill: white; " +
"-fx-padding: 15; " +
"-fx-border-radius: 15; " +
"-fx-background-radius: 15;");

HBox header = new HBox(20, titleLabel, scoreLabel, bestScoreLabel);
header.setAlignment(Pos.CENTER_LEFT);
return header;
}

public Label getFooter() {
return new Label("Join the numbers and get to the 2048 tile!");
}

public void updateUI(int[][] board, int score, int bestScore) {
scoreLabel.setText("SCORE: " + score);
bestScoreLabel.setText("BEST: " + bestScore);
// Update tiles
// Similar logic as in the original
}
}

0 comments on commit 358f99c

Please sign in to comment.