├── .gitignore
├── README.md
├── bin
└── TetrisJFX.jar
├── pom.xml
├── sonar-project.properties
└── src
├── main
├── java
│ └── com
│ │ └── quirko
│ │ ├── app
│ │ └── GameController.java
│ │ ├── gui
│ │ ├── GameOverPanel.java
│ │ ├── GuiController.java
│ │ ├── Main.java
│ │ └── NotificationPanel.java
│ │ └── logic
│ │ ├── Board.java
│ │ ├── ClearRow.java
│ │ ├── DownData.java
│ │ ├── MatrixOperations.java
│ │ ├── Score.java
│ │ ├── SimpleBoard.java
│ │ ├── ViewData.java
│ │ ├── bricks
│ │ ├── Brick.java
│ │ ├── BrickGenerator.java
│ │ ├── IBrick.java
│ │ ├── JBrick.java
│ │ ├── LBrick.java
│ │ ├── OBrick.java
│ │ ├── RandomBrickGenerator.java
│ │ ├── SBrick.java
│ │ ├── TBrick.java
│ │ └── ZBrick.java
│ │ ├── events
│ │ ├── EventSource.java
│ │ ├── EventType.java
│ │ ├── InputEventListener.java
│ │ └── MoveEvent.java
│ │ └── rotator
│ │ ├── BrickRotator.java
│ │ └── NextShapeInfo.java
└── resources
│ ├── background_image.png
│ ├── digital.ttf
│ ├── gameLayout.fxml
│ └── window_style.css
└── test
└── java
└── com
└── quirko
└── logic
├── MatrixOperationsTest.java
├── bricks
└── IBrickTest.java
└── rotator
└── NextShapeInfoTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .sonar
3 | target
4 | jar_builder.bat
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JavaFX Tetris Clone Game
2 | =============
3 |
4 | JavaFX Tetris Clone Game
5 |
6 | ## Screenshots
7 |
8 |
9 |
10 |
11 |
12 | ## Gameplay
13 | See Youtube: https://www.youtube.com/watch?v=uzyOOlCvQN0
14 |
--------------------------------------------------------------------------------
/bin/TetrisJFX.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javafx-dev/JavaFX-Tetris-Clone/9078cea250826c3d01ffe37033dc26e136229b5a/bin/TetrisJFX.jar
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.quirko
8 | tetris
9 | 1.0-SNAPSHOT
10 |
11 | UTF-8
12 | 1.8
13 | 1.8
14 |
15 |
16 |
17 |
18 | com.zenjava
19 | javafx-maven-plugin
20 | 2.0
21 |
22 | com.quirko.gui.Main
23 |
24 |
25 |
26 |
27 |
28 |
29 | junit
30 | junit
31 | 4.11
32 |
33 |
34 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | # Required metadata
2 | sonar.projectKey=tetris
3 | sonar.projectName=Tetris FX
4 | sonar.projectVersion=1.0
5 |
6 | # Comma-separated paths to directories with sources (required)
7 | sonar.sources=src/main/java
8 |
9 | #sonar.binaries=target\classes
10 |
11 | # Language
12 | sonar.language=java
13 |
14 | # Encoding of the source files
15 | sonar.sourceEncoding=UTF-8
16 |
17 | sonar.junit.reportsPath=target/surefire-reports
18 | sonar.jacoco.reportPath=target/jacoco.exec
19 | sonar.dynamicAnalysis=reuseReports
20 | sonar.java.coveragePlugin=jacoco
--------------------------------------------------------------------------------
/src/main/java/com/quirko/app/GameController.java:
--------------------------------------------------------------------------------
1 | package com.quirko.app;
2 |
3 | import com.quirko.gui.GuiController;
4 | import com.quirko.logic.*;
5 | import com.quirko.logic.events.EventSource;
6 | import com.quirko.logic.events.InputEventListener;
7 | import com.quirko.logic.events.MoveEvent;
8 |
9 | public class GameController implements InputEventListener {
10 |
11 | private Board board = new SimpleBoard(25, 10);
12 |
13 | private final GuiController viewGuiController;
14 |
15 | public GameController(GuiController c) {
16 | viewGuiController = c;
17 | board.createNewBrick();
18 | viewGuiController.setEventListener(this);
19 | viewGuiController.initGameView(board.getBoardMatrix(), board.getViewData());
20 | viewGuiController.bindScore(board.getScore().scoreProperty());
21 | }
22 |
23 | @Override
24 | public DownData onDownEvent(MoveEvent event) {
25 | boolean canMove = board.moveBrickDown();
26 | ClearRow clearRow = null;
27 | if (!canMove) {
28 | board.mergeBrickToBackground();
29 | clearRow = board.clearRows();
30 | if (clearRow.getLinesRemoved() > 0) {
31 | board.getScore().add(clearRow.getScoreBonus());
32 | }
33 | if (board.createNewBrick()) {
34 | viewGuiController.gameOver();
35 | }
36 |
37 | viewGuiController.refreshGameBackground(board.getBoardMatrix());
38 |
39 | } else {
40 | if (event.getEventSource() == EventSource.USER) {
41 | board.getScore().add(1);
42 | }
43 | }
44 | return new DownData(clearRow, board.getViewData());
45 | }
46 |
47 | @Override
48 | public ViewData onLeftEvent(MoveEvent event) {
49 | board.moveBrickLeft();
50 | return board.getViewData();
51 | }
52 |
53 | @Override
54 | public ViewData onRightEvent(MoveEvent event) {
55 | board.moveBrickRight();
56 | return board.getViewData();
57 | }
58 |
59 | @Override
60 | public ViewData onRotateEvent(MoveEvent event) {
61 | board.rotateLeftBrick();
62 | return board.getViewData();
63 | }
64 |
65 |
66 | @Override
67 | public void createNewGame() {
68 | board.newGame();
69 | viewGuiController.refreshGameBackground(board.getBoardMatrix());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/gui/GameOverPanel.java:
--------------------------------------------------------------------------------
1 | package com.quirko.gui;
2 |
3 | import javafx.scene.control.Label;
4 | import javafx.scene.layout.BorderPane;
5 |
6 |
7 | public class GameOverPanel extends BorderPane {
8 |
9 | public GameOverPanel() {
10 | final Label gameOverLabel = new Label("GAME OVER");
11 | gameOverLabel.getStyleClass().add("gameOverStyle");
12 | setCenter(gameOverLabel);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/gui/GuiController.java:
--------------------------------------------------------------------------------
1 | package com.quirko.gui;
2 |
3 | import com.quirko.logic.DownData;
4 | import com.quirko.logic.ViewData;
5 | import com.quirko.logic.events.*;
6 | import javafx.animation.KeyFrame;
7 | import javafx.animation.Timeline;
8 | import javafx.beans.property.BooleanProperty;
9 | import javafx.beans.property.IntegerProperty;
10 | import javafx.beans.property.SimpleBooleanProperty;
11 | import javafx.beans.value.ChangeListener;
12 | import javafx.beans.value.ObservableValue;
13 | import javafx.event.ActionEvent;
14 | import javafx.event.EventHandler;
15 | import javafx.fxml.FXML;
16 | import javafx.fxml.Initializable;
17 | import javafx.scene.Group;
18 | import javafx.scene.control.ToggleButton;
19 | import javafx.scene.effect.Reflection;
20 | import javafx.scene.input.KeyCode;
21 | import javafx.scene.input.KeyEvent;
22 | import javafx.scene.layout.GridPane;
23 | import javafx.scene.paint.Color;
24 | import javafx.scene.paint.Paint;
25 | import javafx.scene.shape.Rectangle;
26 | import javafx.scene.text.Font;
27 | import javafx.scene.text.Text;
28 | import javafx.util.Duration;
29 |
30 | import java.net.URL;
31 | import java.util.ResourceBundle;
32 |
33 | public class GuiController implements Initializable {
34 |
35 | private static final int BRICK_SIZE = 20;
36 |
37 | @FXML
38 | private GridPane gamePanel;
39 |
40 | @FXML
41 | private Text scoreValue;
42 |
43 | @FXML
44 | private Group groupNotification;
45 |
46 | @FXML
47 | private GridPane nextBrick;
48 |
49 | @FXML
50 | private GridPane brickPanel;
51 |
52 | @FXML
53 | private ToggleButton pauseButton;
54 |
55 | @FXML
56 | private GameOverPanel gameOverPanel;
57 |
58 | private Rectangle[][] displayMatrix;
59 |
60 | private InputEventListener eventListener;
61 |
62 | private Rectangle[][] rectangles;
63 |
64 | private Timeline timeLine;
65 |
66 | private final BooleanProperty isPause = new SimpleBooleanProperty();
67 |
68 | private final BooleanProperty isGameOver = new SimpleBooleanProperty();
69 |
70 | @Override
71 | public void initialize(URL location, ResourceBundle resources) {
72 | Font.loadFont(getClass().getClassLoader().getResource("digital.ttf").toExternalForm(), 38);
73 | gamePanel.setFocusTraversable(true);
74 | gamePanel.requestFocus();
75 | gamePanel.setOnKeyPressed(new EventHandler() {
76 | @Override
77 | public void handle(KeyEvent keyEvent) {
78 | if (isPause.getValue() == Boolean.FALSE && isGameOver.getValue() == Boolean.FALSE) {
79 | if (keyEvent.getCode() == KeyCode.LEFT || keyEvent.getCode() == KeyCode.A) {
80 | refreshBrick(eventListener.onLeftEvent(new MoveEvent(EventType.LEFT, EventSource.USER)));
81 | keyEvent.consume();
82 | }
83 | if (keyEvent.getCode() == KeyCode.RIGHT || keyEvent.getCode() == KeyCode.D) {
84 | refreshBrick(eventListener.onRightEvent(new MoveEvent(EventType.RIGHT, EventSource.USER)));
85 | keyEvent.consume();
86 | }
87 | if (keyEvent.getCode() == KeyCode.UP || keyEvent.getCode() == KeyCode.W) {
88 | refreshBrick(eventListener.onRotateEvent(new MoveEvent(EventType.ROTATE, EventSource.USER)));
89 | keyEvent.consume();
90 | }
91 | if (keyEvent.getCode() == KeyCode.DOWN || keyEvent.getCode() == KeyCode.S) {
92 | moveDown(new MoveEvent(EventType.DOWN, EventSource.USER));
93 | keyEvent.consume();
94 | }
95 | }
96 | if (keyEvent.getCode() == KeyCode.N) {
97 | newGame(null);
98 | }
99 | if (keyEvent.getCode() == KeyCode.P) {
100 | pauseButton.selectedProperty().setValue(!pauseButton.selectedProperty().getValue());
101 | }
102 |
103 | }
104 | });
105 | gameOverPanel.setVisible(false);
106 | pauseButton.selectedProperty().bindBidirectional(isPause);
107 | pauseButton.selectedProperty().addListener(new ChangeListener() {
108 | @Override
109 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
110 | if (newValue) {
111 | timeLine.pause();
112 | pauseButton.setText("Resume");
113 | } else {
114 | timeLine.play();
115 | pauseButton.setText("Pause");
116 | }
117 | }
118 | });
119 | final Reflection reflection = new Reflection();
120 | reflection.setFraction(0.8);
121 | reflection.setTopOpacity(0.9);
122 | reflection.setTopOffset(-12);
123 | scoreValue.setEffect(reflection);
124 | }
125 |
126 | public void initGameView(int[][] boardMatrix, ViewData brick) {
127 | displayMatrix = new Rectangle[boardMatrix.length][boardMatrix[0].length];
128 | for (int i = 2; i < boardMatrix.length; i++) {
129 | for (int j = 0; j < boardMatrix[i].length; j++) {
130 | Rectangle rectangle = new Rectangle(BRICK_SIZE, BRICK_SIZE);
131 | rectangle.setFill(Color.TRANSPARENT);
132 | displayMatrix[i][j] = rectangle;
133 | gamePanel.add(rectangle, j, i - 2);
134 | }
135 | }
136 |
137 | rectangles = new Rectangle[brick.getBrickData().length][brick.getBrickData()[0].length];
138 | for (int i = 0; i < brick.getBrickData().length; i++) {
139 | for (int j = 0; j < brick.getBrickData()[i].length; j++) {
140 | Rectangle rectangle = new Rectangle(BRICK_SIZE, BRICK_SIZE);
141 | rectangle.setFill(getFillColor(brick.getBrickData()[i][j]));
142 | rectangles[i][j] = rectangle;
143 | brickPanel.add(rectangle, j, i);
144 | }
145 | }
146 | brickPanel.setLayoutX(gamePanel.getLayoutX() + brick.getxPosition() * brickPanel.getVgap() + brick.getxPosition() * BRICK_SIZE);
147 | brickPanel.setLayoutY(-42 + gamePanel.getLayoutY() + brick.getyPosition() * brickPanel.getHgap() + brick.getyPosition() * BRICK_SIZE);
148 |
149 | generatePreviewPanel(brick.getNextBrickData());
150 |
151 |
152 | timeLine = new Timeline(new KeyFrame(
153 | Duration.millis(400),
154 | ae -> moveDown(new MoveEvent(EventType.DOWN, EventSource.THREAD))
155 | ));
156 | timeLine.setCycleCount(Timeline.INDEFINITE);
157 | timeLine.play();
158 | }
159 |
160 | private Paint getFillColor(int i) {
161 | Paint returnPaint;
162 | switch (i) {
163 | case 0:
164 | returnPaint = Color.TRANSPARENT;
165 | break;
166 | case 1:
167 | returnPaint = Color.AQUA;
168 | break;
169 | case 2:
170 | returnPaint = Color.BLUEVIOLET;
171 | break;
172 | case 3:
173 | returnPaint = Color.DARKGREEN;
174 | break;
175 | case 4:
176 | returnPaint = Color.YELLOW;
177 | break;
178 | case 5:
179 | returnPaint = Color.RED;
180 | break;
181 | case 6:
182 | returnPaint = Color.BEIGE;
183 | break;
184 | case 7:
185 | returnPaint = Color.BURLYWOOD;
186 | break;
187 | default:
188 | returnPaint = Color.WHITE;
189 | break;
190 | }
191 | return returnPaint;
192 | }
193 |
194 | private void generatePreviewPanel(int[][] nextBrickData) {
195 | nextBrick.getChildren().clear();
196 | for (int i = 0; i < nextBrickData.length; i++) {
197 | for (int j = 0; j < nextBrickData[i].length; j++) {
198 | Rectangle rectangle = new Rectangle(BRICK_SIZE, BRICK_SIZE);
199 | setRectangleData(nextBrickData[i][j], rectangle);
200 | if (nextBrickData[i][j] != 0) {
201 | nextBrick.add(rectangle, j, i);
202 | }
203 | }
204 | }
205 | }
206 |
207 | private void refreshBrick(ViewData brick) {
208 | if (isPause.getValue() == Boolean.FALSE) {
209 | brickPanel.setLayoutX(gamePanel.getLayoutX() + brick.getxPosition() * brickPanel.getVgap() + brick.getxPosition() * BRICK_SIZE);
210 | brickPanel.setLayoutY(-42 + gamePanel.getLayoutY() + brick.getyPosition() * brickPanel.getHgap() + brick.getyPosition() * BRICK_SIZE);
211 | for (int i = 0; i < brick.getBrickData().length; i++) {
212 | for (int j = 0; j < brick.getBrickData()[i].length; j++) {
213 | setRectangleData(brick.getBrickData()[i][j], rectangles[i][j]);
214 | }
215 | }
216 | generatePreviewPanel(brick.getNextBrickData());
217 | }
218 | }
219 |
220 | public void refreshGameBackground(int[][] board) {
221 | for (int i = 2; i < board.length; i++) {
222 | for (int j = 0; j < board[i].length; j++) {
223 | setRectangleData(board[i][j], displayMatrix[i][j]);
224 | }
225 | }
226 | }
227 |
228 | private void setRectangleData(int color, Rectangle rectangle) {
229 | rectangle.setFill(getFillColor(color));
230 | rectangle.setArcHeight(9);
231 | rectangle.setArcWidth(9);
232 | }
233 |
234 | private void moveDown(MoveEvent event) {
235 | if (isPause.getValue() == Boolean.FALSE) {
236 | DownData downData = eventListener.onDownEvent(event);
237 | if (downData.getClearRow() != null && downData.getClearRow().getLinesRemoved() > 0) {
238 | NotificationPanel notificationPanel = new NotificationPanel("+" + downData.getClearRow().getScoreBonus());
239 | groupNotification.getChildren().add(notificationPanel);
240 | notificationPanel.showScore(groupNotification.getChildren());
241 | }
242 | refreshBrick(downData.getViewData());
243 | }
244 | gamePanel.requestFocus();
245 | }
246 |
247 | public void setEventListener(InputEventListener eventListener) {
248 | this.eventListener = eventListener;
249 | }
250 |
251 | public void bindScore(IntegerProperty integerProperty) {
252 | scoreValue.textProperty().bind(integerProperty.asString());
253 | }
254 |
255 | public void gameOver() {
256 | timeLine.stop();
257 | gameOverPanel.setVisible(true);
258 | isGameOver.setValue(Boolean.TRUE);
259 |
260 | }
261 |
262 | public void newGame(ActionEvent actionEvent) {
263 | timeLine.stop();
264 | gameOverPanel.setVisible(false);
265 | eventListener.createNewGame();
266 | gamePanel.requestFocus();
267 | timeLine.play();
268 | isPause.setValue(Boolean.FALSE);
269 | isGameOver.setValue(Boolean.FALSE);
270 | }
271 |
272 | public void pauseGame(ActionEvent actionEvent) {
273 | gamePanel.requestFocus();
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/gui/Main.java:
--------------------------------------------------------------------------------
1 | package com.quirko.gui;
2 |
3 | import com.quirko.app.GameController;
4 | import javafx.application.Application;
5 | import javafx.fxml.FXMLLoader;
6 | import javafx.scene.Parent;
7 | import javafx.scene.Scene;
8 | import javafx.stage.Stage;
9 |
10 | import java.net.URL;
11 | import java.util.ResourceBundle;
12 |
13 | public class Main extends Application {
14 |
15 | @Override
16 | public void start(Stage primaryStage) throws Exception {
17 |
18 | URL location = getClass().getClassLoader().getResource("gameLayout.fxml");
19 | ResourceBundle resources = null;
20 | FXMLLoader fxmlLoader = new FXMLLoader(location, resources);
21 | Parent root = fxmlLoader.load();
22 | GuiController c = fxmlLoader.getController();
23 |
24 | primaryStage.setTitle("TetrisJFX");
25 | Scene scene = new Scene(root, 400, 510);
26 | primaryStage.setScene(scene);
27 | primaryStage.show();
28 | new GameController(c);
29 | }
30 |
31 |
32 | public static void main(String[] args) {
33 | launch(args);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/gui/NotificationPanel.java:
--------------------------------------------------------------------------------
1 | package com.quirko.gui;
2 |
3 | import javafx.animation.FadeTransition;
4 | import javafx.animation.ParallelTransition;
5 | import javafx.animation.TranslateTransition;
6 | import javafx.collections.ObservableList;
7 | import javafx.event.ActionEvent;
8 | import javafx.event.EventHandler;
9 | import javafx.scene.Node;
10 | import javafx.scene.control.Label;
11 | import javafx.scene.effect.Effect;
12 | import javafx.scene.effect.Glow;
13 | import javafx.scene.layout.BorderPane;
14 | import javafx.scene.paint.Color;
15 | import javafx.util.Duration;
16 |
17 | public class NotificationPanel extends BorderPane {
18 |
19 | public NotificationPanel(String text) {
20 | setMinHeight(200);
21 | setMinWidth(220);
22 | final Label score = new Label(text);
23 | score.getStyleClass().add("bonusStyle");
24 | final Effect glow = new Glow(0.6);
25 | score.setEffect(glow);
26 | score.setTextFill(Color.WHITE);
27 | setCenter(score);
28 |
29 | }
30 |
31 | public void showScore(ObservableList list) {
32 | FadeTransition ft = new FadeTransition(Duration.millis(2000), this);
33 | TranslateTransition tt = new TranslateTransition(Duration.millis(2500), this);
34 | tt.setToY(this.getLayoutY() - 40);
35 | ft.setFromValue(1);
36 | ft.setToValue(0);
37 | ParallelTransition transition = new ParallelTransition(tt, ft);
38 | transition.setOnFinished(new EventHandler() {
39 | @Override
40 | public void handle(ActionEvent event) {
41 | list.remove(NotificationPanel.this);
42 | }
43 | });
44 | transition.play();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/Board.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | public interface Board {
4 |
5 | boolean moveBrickDown();
6 |
7 | boolean moveBrickLeft();
8 |
9 | boolean moveBrickRight();
10 |
11 | boolean rotateLeftBrick();
12 |
13 | boolean createNewBrick();
14 |
15 | int[][] getBoardMatrix();
16 |
17 | ViewData getViewData();
18 |
19 | void mergeBrickToBackground();
20 |
21 | ClearRow clearRows();
22 |
23 | Score getScore();
24 |
25 | void newGame();
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/ClearRow.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | public final class ClearRow {
4 |
5 | private final int linesRemoved;
6 | private final int[][] newMatrix;
7 | private final int scoreBonus;
8 |
9 | public ClearRow(int linesRemoved, int[][] newMatrix, int scoreBonus) {
10 | this.linesRemoved = linesRemoved;
11 | this.newMatrix = newMatrix;
12 | this.scoreBonus = scoreBonus;
13 | }
14 |
15 | public int getLinesRemoved() {
16 | return linesRemoved;
17 | }
18 |
19 | public int[][] getNewMatrix() {
20 | return MatrixOperations.copy(newMatrix);
21 | }
22 |
23 | public int getScoreBonus() {
24 | return scoreBonus;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/DownData.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | public final class DownData {
4 | private final ClearRow clearRow;
5 | private final ViewData viewData;
6 |
7 | public DownData(ClearRow clearRow, ViewData viewData) {
8 | this.clearRow = clearRow;
9 | this.viewData = viewData;
10 | }
11 |
12 | public ClearRow getClearRow() {
13 | return clearRow;
14 | }
15 |
16 | public ViewData getViewData() {
17 | return viewData;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/MatrixOperations.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | import java.util.ArrayDeque;
4 | import java.util.ArrayList;
5 | import java.util.Deque;
6 | import java.util.List;
7 | import java.util.stream.Collectors;
8 |
9 | public class MatrixOperations {
10 |
11 |
12 | //We don't want to instantiate this utility class
13 | private MatrixOperations(){
14 |
15 | }
16 |
17 | public static boolean intersect(final int[][] matrix, final int[][] brick, int x, int y) {
18 | for (int i = 0; i < brick.length; i++) {
19 | for (int j = 0; j < brick[i].length; j++) {
20 | int targetX = x + i;
21 | int targetY = y + j;
22 | if (brick[j][i] != 0 && (checkOutOfBound(matrix, targetX, targetY) || matrix[targetY][targetX] != 0)) {
23 | return true;
24 | }
25 | }
26 | }
27 | return false;
28 | }
29 |
30 | private static boolean checkOutOfBound(int[][] matrix, int targetX, int targetY) {
31 | boolean returnValue = true;
32 | if (targetX >= 0 && targetY < matrix.length && targetX < matrix[targetY].length) {
33 | returnValue = false;
34 | }
35 | return returnValue;
36 | }
37 |
38 | public static int[][] copy(int[][] original) {
39 | int[][] myInt = new int[original.length][];
40 | for (int i = 0; i < original.length; i++) {
41 | int[] aMatrix = original[i];
42 | int aLength = aMatrix.length;
43 | myInt[i] = new int[aLength];
44 | System.arraycopy(aMatrix, 0, myInt[i], 0, aLength);
45 | }
46 | return myInt;
47 | }
48 |
49 | public static int[][] merge(int[][] filledFields, int[][] brick, int x, int y) {
50 | int[][] copy = copy(filledFields);
51 | for (int i = 0; i < brick.length; i++) {
52 | for (int j = 0; j < brick[i].length; j++) {
53 | int targetX = x + i;
54 | int targetY = y + j;
55 | if (brick[j][i] != 0) {
56 | copy[targetY][targetX] = brick[j][i];
57 | }
58 | }
59 | }
60 | return copy;
61 | }
62 |
63 | public static ClearRow checkRemoving(final int[][] matrix) {
64 | int[][] tmp = new int[matrix.length][matrix[0].length];
65 | Deque newRows = new ArrayDeque<>();
66 | List clearedRows = new ArrayList<>();
67 |
68 | for (int i = 0; i < matrix.length; i++) {
69 | int[] tmpRow = new int[matrix[i].length];
70 | boolean rowToClear = true;
71 | for (int j = 0; j < matrix[0].length; j++) {
72 | if (matrix[i][j] == 0) {
73 | rowToClear = false;
74 | }
75 | tmpRow[j] = matrix[i][j];
76 | }
77 | if (rowToClear) {
78 | clearedRows.add(i);
79 | } else {
80 | newRows.add(tmpRow);
81 | }
82 | }
83 | for (int i = matrix.length - 1; i >= 0; i--) {
84 | int[] row = newRows.pollLast();
85 | if (row != null) {
86 | tmp[i] = row;
87 | } else {
88 | break;
89 | }
90 | }
91 | int scoreBonus = 50 * clearedRows.size() * clearedRows.size();
92 | return new ClearRow(clearedRows.size(), tmp, scoreBonus);
93 | }
94 |
95 | public static List deepCopyList(List list){
96 | return list.stream().map(MatrixOperations::copy).collect(Collectors.toList());
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/Score.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | import javafx.beans.property.IntegerProperty;
4 | import javafx.beans.property.SimpleIntegerProperty;
5 |
6 | public final class Score {
7 |
8 | private final IntegerProperty score = new SimpleIntegerProperty(0);
9 |
10 | public IntegerProperty scoreProperty() {
11 | return score;
12 | }
13 |
14 | public void add(int i){
15 | score.setValue(score.getValue() + i);
16 | }
17 |
18 | public void reset() {
19 | score.setValue(0);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/SimpleBoard.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | import com.quirko.logic.bricks.Brick;
4 | import com.quirko.logic.bricks.BrickGenerator;
5 | import com.quirko.logic.bricks.RandomBrickGenerator;
6 | import com.quirko.logic.rotator.BrickRotator;
7 | import com.quirko.logic.rotator.NextShapeInfo;
8 |
9 | import java.awt.*;
10 |
11 | public class SimpleBoard implements Board {
12 |
13 | private final int width;
14 | private final int height;
15 | private final BrickGenerator brickGenerator;
16 | private final BrickRotator brickRotator;
17 | private int[][] currentGameMatrix;
18 | private Point currentOffset;
19 | private final Score score;
20 |
21 | public SimpleBoard(int width, int height) {
22 | this.width = width;
23 | this.height = height;
24 | currentGameMatrix = new int[width][height];
25 | brickGenerator = new RandomBrickGenerator();
26 | brickRotator = new BrickRotator();
27 | score = new Score();
28 | }
29 |
30 | @Override
31 | public boolean moveBrickDown() {
32 | int[][] currentMatrix = MatrixOperations.copy(currentGameMatrix);
33 | Point p = new Point(currentOffset);
34 | p.translate(0, 1);
35 | boolean conflict = MatrixOperations.intersect(currentMatrix, brickRotator.getCurrentShape(), (int) p.getX(), (int) p.getY());
36 | if (conflict) {
37 | return false;
38 | } else {
39 | currentOffset = p;
40 | return true;
41 | }
42 | }
43 |
44 |
45 | @Override
46 | public boolean moveBrickLeft() {
47 | int[][] currentMatrix = MatrixOperations.copy(currentGameMatrix);
48 | Point p = new Point(currentOffset);
49 | p.translate(-1, 0);
50 | boolean conflict = MatrixOperations.intersect(currentMatrix, brickRotator.getCurrentShape(), (int) p.getX(), (int) p.getY());
51 | if (conflict) {
52 | return false;
53 | } else {
54 | currentOffset = p;
55 | return true;
56 | }
57 | }
58 |
59 | @Override
60 | public boolean moveBrickRight() {
61 | int[][] currentMatrix = MatrixOperations.copy(currentGameMatrix);
62 | Point p = new Point(currentOffset);
63 | p.translate(1, 0);
64 | boolean conflict = MatrixOperations.intersect(currentMatrix, brickRotator.getCurrentShape(), (int) p.getX(), (int) p.getY());
65 | if (conflict) {
66 | return false;
67 | } else {
68 | currentOffset = p;
69 | return true;
70 | }
71 | }
72 |
73 | @Override
74 | public boolean rotateLeftBrick() {
75 | int[][] currentMatrix = MatrixOperations.copy(currentGameMatrix);
76 | NextShapeInfo nextShape = brickRotator.getNextShape();
77 | boolean conflict = MatrixOperations.intersect(currentMatrix, nextShape.getShape(), (int) currentOffset.getX(), (int) currentOffset.getY());
78 | if (conflict) {
79 | return false;
80 | } else {
81 | brickRotator.setCurrentShape(nextShape.getPosition());
82 | return true;
83 | }
84 | }
85 |
86 | @Override
87 | public boolean createNewBrick() {
88 | Brick currentBrick = brickGenerator.getBrick();
89 | brickRotator.setBrick(currentBrick);
90 | currentOffset = new Point(4, 0);
91 | return MatrixOperations.intersect(currentGameMatrix, brickRotator.getCurrentShape(), (int) currentOffset.getX(), (int) currentOffset.getY());
92 | }
93 |
94 | @Override
95 | public int[][] getBoardMatrix() {
96 | return currentGameMatrix;
97 | }
98 |
99 | @Override
100 | public ViewData getViewData() {
101 | return new ViewData(brickRotator.getCurrentShape(), (int) currentOffset.getX(), (int) currentOffset.getY(), brickGenerator.getNextBrick().getShapeMatrix().get(0));
102 | }
103 |
104 | @Override
105 | public void mergeBrickToBackground() {
106 | currentGameMatrix = MatrixOperations.merge(currentGameMatrix, brickRotator.getCurrentShape(), (int) currentOffset.getX(), (int) currentOffset.getY());
107 | }
108 |
109 | @Override
110 | public ClearRow clearRows() {
111 | ClearRow clearRow = MatrixOperations.checkRemoving(currentGameMatrix);
112 | currentGameMatrix = clearRow.getNewMatrix();
113 | return clearRow;
114 |
115 | }
116 |
117 | @Override
118 | public Score getScore() {
119 | return score;
120 | }
121 |
122 |
123 | @Override
124 | public void newGame() {
125 | currentGameMatrix = new int[width][height];
126 | score.reset();
127 | createNewBrick();
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/ViewData.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | public final class ViewData {
4 |
5 | private final int[][] brickData;
6 | private final int xPosition;
7 | private final int yPosition;
8 | private final int[][] nextBrickData;
9 |
10 | public ViewData(int[][] brickData, int xPosition, int yPosition, int[][] nextBrickData) {
11 | this.brickData = brickData;
12 | this.xPosition = xPosition;
13 | this.yPosition = yPosition;
14 | this.nextBrickData = nextBrickData;
15 | }
16 |
17 | public int[][] getBrickData() {
18 | return MatrixOperations.copy(brickData);
19 | }
20 |
21 | public int getxPosition() {
22 | return xPosition;
23 | }
24 |
25 | public int getyPosition() {
26 | return yPosition;
27 | }
28 |
29 | public int[][] getNextBrickData() {
30 | return MatrixOperations.copy(nextBrickData);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/Brick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import java.util.List;
4 |
5 | public interface Brick {
6 |
7 | List getShapeMatrix();
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/BrickGenerator.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | public interface BrickGenerator {
4 |
5 | Brick getBrick();
6 |
7 | Brick getNextBrick();
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/IBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class IBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public IBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {1, 1, 1, 1},
16 | {0, 0, 0, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {0, 1, 0, 0},
21 | {0, 1, 0, 0},
22 | {0, 1, 0, 0},
23 | {0, 1, 0, 0}
24 | });
25 | }
26 |
27 | @Override
28 | public List getShapeMatrix() {
29 | return MatrixOperations.deepCopyList(brickMatrix);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/JBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class JBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public JBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {2, 2, 2, 0},
16 | {0, 0, 2, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {0, 0, 0, 0},
21 | {0, 2, 2, 0},
22 | {0, 2, 0, 0},
23 | {0, 2, 0, 0}
24 | });
25 | brickMatrix.add(new int[][]{
26 | {0, 0, 0, 0},
27 | {0, 2, 0, 0},
28 | {0, 2, 2, 2},
29 | {0, 0, 0, 0}
30 | });
31 | brickMatrix.add(new int[][]{
32 | {0, 0, 2, 0},
33 | {0, 0, 2, 0},
34 | {0, 2, 2, 0},
35 | {0, 0, 0, 0}
36 | });
37 | }
38 |
39 | @Override
40 | public List getShapeMatrix() {
41 | return MatrixOperations.deepCopyList(brickMatrix);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/LBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class LBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public LBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {0, 3, 3, 3},
16 | {0, 3, 0, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {0, 0, 0, 0},
21 | {0, 3, 3, 0},
22 | {0, 0, 3, 0},
23 | {0, 0, 3, 0}
24 | });
25 | brickMatrix.add(new int[][]{
26 | {0, 0, 0, 0},
27 | {0, 0, 3, 0},
28 | {3, 3, 3, 0},
29 | {0, 0, 0, 0}
30 | });
31 | brickMatrix.add(new int[][]{
32 | {0, 3, 0, 0},
33 | {0, 3, 0, 0},
34 | {0, 3, 3, 0},
35 | {0, 0, 0, 0}
36 | });
37 | }
38 |
39 | @Override
40 | public List getShapeMatrix() {
41 | return MatrixOperations.deepCopyList(brickMatrix);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/OBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class OBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public OBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {0, 4, 4, 0},
16 | {0, 4, 4, 0},
17 | {0, 0, 0, 0}
18 | });
19 | }
20 |
21 | @Override
22 | public List getShapeMatrix() {
23 | return MatrixOperations.deepCopyList(brickMatrix);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/RandomBrickGenerator.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import java.util.ArrayDeque;
4 | import java.util.ArrayList;
5 | import java.util.Deque;
6 | import java.util.List;
7 | import java.util.concurrent.ThreadLocalRandom;
8 |
9 | public class RandomBrickGenerator implements BrickGenerator {
10 |
11 | private final List brickList;
12 |
13 | private final Deque nextBricks = new ArrayDeque<>();
14 |
15 | public RandomBrickGenerator() {
16 | brickList = new ArrayList<>();
17 | brickList.add(new IBrick());
18 | brickList.add(new JBrick());
19 | brickList.add(new LBrick());
20 | brickList.add(new OBrick());
21 | brickList.add(new SBrick());
22 | brickList.add(new TBrick());
23 | brickList.add(new ZBrick());
24 | nextBricks.add(brickList.get(ThreadLocalRandom.current().nextInt(brickList.size())));
25 | nextBricks.add(brickList.get(ThreadLocalRandom.current().nextInt(brickList.size())));
26 | }
27 |
28 | @Override
29 | public Brick getBrick() {
30 | if (nextBricks.size() <= 1) {
31 | nextBricks.add(brickList.get(ThreadLocalRandom.current().nextInt(brickList.size())));
32 | }
33 | return nextBricks.poll();
34 | }
35 |
36 | @Override
37 | public Brick getNextBrick() {
38 | return nextBricks.peek();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/SBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class SBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public SBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {0, 5, 5, 0},
16 | {5, 5, 0, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {5, 0, 0, 0},
21 | {5, 5, 0, 0},
22 | {0, 5, 0, 0},
23 | {0, 0, 0, 0}
24 | });
25 | }
26 |
27 | @Override
28 | public List getShapeMatrix() {
29 | return MatrixOperations.deepCopyList(brickMatrix);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/TBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class TBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public TBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {6, 6, 6, 0},
16 | {0, 6, 0, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {0, 6, 0, 0},
21 | {0, 6, 6, 0},
22 | {0, 6, 0, 0},
23 | {0, 0, 0, 0}
24 | });
25 | brickMatrix.add(new int[][]{
26 | {0, 6, 0, 0},
27 | {6, 6, 6, 0},
28 | {0, 0, 0, 0},
29 | {0, 0, 0, 0}
30 | });
31 | brickMatrix.add(new int[][]{
32 | {0, 6, 0, 0},
33 | {6, 6, 0, 0},
34 | {0, 6, 0, 0},
35 | {0, 0, 0, 0}
36 | });
37 | }
38 |
39 | @Override
40 | public List getShapeMatrix() {
41 | return MatrixOperations.deepCopyList(brickMatrix);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/bricks/ZBrick.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | final class ZBrick implements Brick {
9 |
10 | private final List brickMatrix = new ArrayList<>();
11 |
12 | public ZBrick() {
13 | brickMatrix.add(new int[][]{
14 | {0, 0, 0, 0},
15 | {7, 7, 0, 0},
16 | {0, 7, 7, 0},
17 | {0, 0, 0, 0}
18 | });
19 | brickMatrix.add(new int[][]{
20 | {0, 7, 0, 0},
21 | {7, 7, 0, 0},
22 | {7, 0, 0, 0},
23 | {0, 0, 0, 0}
24 | });
25 | }
26 |
27 | @Override
28 | public List getShapeMatrix() {
29 | return MatrixOperations.deepCopyList(brickMatrix);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/events/EventSource.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.events;
2 |
3 | public enum EventSource {
4 | USER, THREAD
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/events/EventType.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.events;
2 |
3 | public enum EventType {
4 | DOWN, LEFT, RIGHT, ROTATE
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/events/InputEventListener.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.events;
2 |
3 | import com.quirko.logic.ViewData;
4 | import com.quirko.logic.DownData;
5 |
6 | public interface InputEventListener {
7 |
8 | DownData onDownEvent(MoveEvent event);
9 |
10 | ViewData onLeftEvent(MoveEvent event);
11 |
12 | ViewData onRightEvent(MoveEvent event);
13 |
14 | ViewData onRotateEvent(MoveEvent event);
15 |
16 | void createNewGame();
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/events/MoveEvent.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.events;
2 |
3 | public final class MoveEvent {
4 | private final EventType eventType;
5 | private final EventSource eventSource;
6 |
7 | public MoveEvent(EventType eventType, EventSource eventSource) {
8 | this.eventType = eventType;
9 | this.eventSource = eventSource;
10 | }
11 |
12 | public EventType getEventType() {
13 | return eventType;
14 | }
15 |
16 | public EventSource getEventSource() {
17 | return eventSource;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/rotator/BrickRotator.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.rotator;
2 |
3 | import com.quirko.logic.bricks.Brick;
4 |
5 | public class BrickRotator {
6 |
7 | private Brick brick;
8 | private int currentShape = 0;
9 |
10 | public NextShapeInfo getNextShape() {
11 | int nextShape = currentShape;
12 | nextShape = (++nextShape) % brick.getShapeMatrix().size();
13 | return new NextShapeInfo(brick.getShapeMatrix().get(nextShape), nextShape);
14 | }
15 |
16 | public int[][] getCurrentShape() {
17 | return brick.getShapeMatrix().get(currentShape);
18 | }
19 |
20 | public void setCurrentShape(int currentShape) {
21 | this.currentShape = currentShape;
22 | }
23 |
24 | public void setBrick(Brick brick) {
25 | this.brick = brick;
26 | currentShape = 0;
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/quirko/logic/rotator/NextShapeInfo.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.rotator;
2 |
3 | import com.quirko.logic.MatrixOperations;
4 |
5 | public final class NextShapeInfo {
6 |
7 | private final int[][] shape;
8 | private final int position;
9 |
10 | public NextShapeInfo(final int[][] shape, final int position) {
11 | this.shape = shape;
12 | this.position = position;
13 | }
14 |
15 | public int[][] getShape() {
16 | return MatrixOperations.copy(shape);
17 | }
18 |
19 | public int getPosition() {
20 | return position;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/resources/background_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javafx-dev/JavaFX-Tetris-Clone/9078cea250826c3d01ffe37033dc26e136229b5a/src/main/resources/background_image.png
--------------------------------------------------------------------------------
/src/main/resources/digital.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javafx-dev/JavaFX-Tetris-Clone/9078cea250826c3d01ffe37033dc26e136229b5a/src/main/resources/digital.ttf
--------------------------------------------------------------------------------
/src/main/resources/gameLayout.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/main/resources/window_style.css:
--------------------------------------------------------------------------------
1 | .root {
2 | -fx-background-image: url("background_image.png");
3 | }
4 |
5 | .nextBrick {
6 | -fx-border-width: 2px;
7 | -fx-border-color: whitesmoke;
8 | -fx-border-radius: 17px;
9 | }
10 |
11 | .gameBoard {
12 | -fx-border-color: linear-gradient(#2A5058, #61a2b1);
13 | -fx-border-width: 12px;
14 | -fx-border-radius: 12px;
15 | }
16 |
17 | .nextBrickLabel {
18 | -fx-font-family: "Let's go Digital";
19 | -fx-padding: 20px 0 0 0;
20 | -fx-font-size: 16px;
21 | -fx-text-fill: yellow;
22 | }
23 |
24 | .ipad-dark-grey {
25 | -fx-background-color: linear-gradient(#686868 0%, #232723 25%, #373837 75%, #757575 100%),
26 | linear-gradient(#020b02, #3a3a3a),
27 | linear-gradient(#9d9e9d 0%, #6b6a6b 20%, #343534 80%, #242424 100%),
28 | linear-gradient(#8a8a8a 0%, #6b6a6b 20%, #343534 80%, #262626 100%),
29 | linear-gradient(#777777 0%, #606060 50%, #505250 51%, #2a2b2a 100%);
30 | -fx-background-insets: 0, 1, 4, 5, 6;
31 | -fx-background-radius: 9, 8, 5, 4, 3;
32 | -fx-padding: 8;
33 | /*-fx-font-family: "Helvetica";*/
34 | -fx-font-family: "Let's go Digital";
35 | -fx-font-size: 22px;
36 | -fx-font-weight: bold;
37 | -fx-text-fill: white;
38 | -fx-effect: dropshadow(three-pass-box, rgba(255, 255, 255, 0.2), 1, 0.0, 0, 1);
39 | }
40 |
41 | .rectangleStyle {
42 | -fx-fill: linear-gradient(from 41px 34px to 50px 50px, reflect, #ff7f50 30%, #faebd7 47%);
43 | }
44 |
45 | .vbox {
46 | -fx-spacing: 12;
47 | }
48 |
49 | .helpInfo {
50 | -fx-fill: white;
51 | -fx-alignment: center-left;
52 | -fx-text-alignment: left;
53 | -fx-font-size: 10px;
54 | }
55 |
56 | .bonusStyle {
57 | -fx-font-size: 40px;
58 | -fx-font-weight: bold;
59 | }
60 |
61 | .gameOverStyle {
62 | -fx-font-family: "Let's go Digital";
63 | -fx-font-size: 48;
64 | -fx-background-color: red;
65 | }
66 |
67 | .scoreClass{
68 | -fx-font-family: "Let's go Digital";
69 | -fx-font-size: 38;
70 | -fx-fill: yellow;
71 | -fx-text-fill: yellow;
72 | }
--------------------------------------------------------------------------------
/src/test/java/com/quirko/logic/MatrixOperationsTest.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic;
2 |
3 | import org.junit.After;
4 | import org.junit.AfterClass;
5 | import org.junit.Before;
6 | import org.junit.BeforeClass;
7 | import org.junit.Test;
8 | import static org.junit.Assert.*;
9 | public class MatrixOperationsTest {
10 |
11 |
12 | @BeforeClass
13 | public static void setUpClass() {
14 | }
15 |
16 | @AfterClass
17 | public static void tearDownClass() {
18 | }
19 |
20 | @Before
21 | public void setUp() {
22 | }
23 |
24 | @After
25 | public void tearDown() {
26 | }
27 |
28 | @Test
29 | public void testNoIntersect() {
30 | int[][] matrix = new int[][]{
31 | {0,0,0,0,0,0,0},
32 | {0,0,0,0,0,0,0},
33 | {0,0,0,0,0,0,0},
34 | {0,0,0,0,0,0,0},
35 | {0,0,0,0,0,0,0},
36 | {0,0,0,0,0,0,0},
37 | {0,0,0,0,0,0,0},
38 | {0,0,0,0,0,0,0},
39 | {0,0,0,0,0,0,0},
40 | {0,0,0,0,0,0,0},
41 | };
42 | int[][] brick = new int[][]{
43 | {0,1,0,0},
44 | {0,1,0,0},
45 | {0,1,0,0},
46 | {0,1,0,0},
47 | };
48 | int x = 0;
49 | int y = 0;
50 | boolean expResult = false;
51 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
52 | assertEquals(expResult, result);
53 | }
54 |
55 |
56 | @Test
57 | public void testIntersect() {
58 | int[][] matrix = new int[][]{
59 | {0,0,0,0,0,0,0},
60 | {0,1,0,0,0,0,0},
61 | {0,0,0,0,0,0,0},
62 | {0,0,0,0,0,0,0},
63 | {0,0,0,0,0,0,0},
64 | {0,0,0,0,0,0,0},
65 | {0,0,0,0,0,0,0},
66 | {0,0,0,0,0,0,0},
67 | {0,0,0,0,0,0,0},
68 | {0,0,0,0,0,0,0},
69 | };
70 | int[][] brick = new int[][]{
71 | {0,1,0,0},
72 | {0,1,0,0},
73 | {0,1,0,0},
74 | {0,1,0,0},
75 | };
76 | int x = 0;
77 | int y = 0;
78 | boolean expResult = true;
79 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
80 | assertEquals(expResult, result);
81 | }
82 |
83 |
84 | @Test
85 | public void testNoIntersect1() {
86 | int[][] matrix = new int[][]{
87 | {1,0,0,0,0,0,0},
88 | {1,0,0,0,0,0,0},
89 | {1,0,0,0,0,0,0},
90 | {1,0,0,0,0,0,0},
91 | {0,0,0,0,0,0,0},
92 | {0,0,0,0,0,0,0},
93 | {0,0,0,0,0,0,0},
94 | {0,0,0,0,0,0,0},
95 | {0,0,0,0,0,0,0},
96 | {0,0,0,0,0,0,0},
97 | };
98 | int[][] brick = new int[][]{
99 | {0,1,0,0},
100 | {0,1,0,0},
101 | {0,1,0,0},
102 | {0,1,0,0},
103 | };
104 | int x = 0;
105 | int y = 0;
106 | boolean expResult = false;
107 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
108 | assertEquals(expResult, result);
109 | }
110 |
111 | @Test
112 | public void testNoIntersect2() {
113 | int[][] matrix = new int[][]{
114 | {0,0,0,0,0,0,0},
115 | {0,0,0,0,0,0,0},
116 | {0,0,0,0,0,0,0},
117 | {0,0,0,0,0,0,0},
118 | {1,1,0,0,0,0,0},
119 | {1,1,0,0,0,0,0},
120 | {1,1,0,0,0,0,0},
121 | {1,1,0,0,0,0,0},
122 | {0,0,0,0,0,0,0},
123 | {0,0,0,0,0,0,0},
124 | };
125 | int[][] brick = new int[][]{
126 | {0,1,0,0},
127 | {0,1,0,0},
128 | {0,1,0,0},
129 | {0,1,0,0},
130 | };
131 | int x = 0;
132 | int y = 0;
133 | boolean expResult = false;
134 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
135 | assertEquals(expResult, result);
136 | }
137 |
138 | @Test
139 | public void testIntersect1() {
140 | int[][] matrix = new int[][]{
141 | {0,0,0,0,0,0,0},
142 | {0,0,0,0,0,0,0},
143 | {0,0,0,0,0,0,0},
144 | {0,0,0,0,0,0,0},
145 | {1,1,0,0,0,0,0},
146 | {1,1,0,0,0,0,0},
147 | {1,1,0,0,0,0,0},
148 | {1,1,0,0,0,0,0},
149 | {0,0,0,0,0,0,0},
150 | {0,0,0,0,0,0,0},
151 | };
152 | int[][] brick = new int[][]{
153 | {0,1,0,0},
154 | {0,1,0,0},
155 | {0,1,0,0},
156 | {0,1,0,0},
157 | };
158 | int x = 0;
159 | int y = 1;
160 | boolean expResult = true;
161 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
162 | assertEquals(expResult, result);
163 | }
164 |
165 | @Test
166 | public void testIntersect2() {
167 | int[][] matrix = new int[][]{
168 | {0,0,0,1,0,0,0},
169 | {0,0,0,1,0,0,0},
170 | {0,0,0,1,0,0,0},
171 | {0,0,0,1,0,0,0},
172 | {1,1,0,0,0,0,0},
173 | {1,1,0,0,0,0,0},
174 | {1,1,0,0,0,0,0},
175 | {1,1,0,0,0,0,0},
176 | {0,0,0,0,0,0,0},
177 | {0,0,0,0,0,0,0},
178 | };
179 | int[][] brick = new int[][]{
180 | {0,1,0,0},
181 | {0,1,0,0},
182 | {0,1,0,0},
183 | {0,1,0,0},
184 | };
185 | int x = 2;
186 | int y = 0;
187 | boolean expResult = true;
188 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
189 | assertEquals(expResult, result);
190 | }
191 |
192 |
193 | @Test
194 | public void testIntersect3() {
195 | int[][] matrix = new int[][]{
196 | {0,0,0,0,0,0,0},
197 | {0,0,0,0,0,0,0},
198 | {0,0,0,0,0,0,0},
199 | {0,0,0,0,0,0,0},
200 | {0,0,0,1,0,0,0},
201 | {0,0,0,0,0,0,0},
202 | {0,0,0,0,0,0,0},
203 | {0,0,0,0,0,0,0},
204 | {0,0,0,0,0,0,0},
205 | {0,0,0,0,0,0,0},
206 | };
207 | int[][] brick = new int[][]{
208 | {0,1,0,0},
209 | {0,1,0,0},
210 | {0,1,0,0},
211 | {0,1,0,0},
212 | };
213 | int x = 2;
214 | int y = 1;
215 | boolean expResult = true;
216 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
217 | assertEquals(expResult, result);
218 | }
219 |
220 | @Test
221 | public void testOutOfBoundry1() {
222 | int[][] matrix = new int[][]{
223 | {0,0,0,0,0,0,0},
224 | {0,0,0,0,0,0,0},
225 | {0,0,0,0,0,0,0},
226 | {0,0,0,0,0,0,0},
227 | {0,0,0,0,0,0,0},
228 | {0,0,0,0,0,0,0},
229 | {0,0,0,0,0,0,0},
230 | {0,0,0,0,0,0,0},
231 | {0,0,0,0,0,0,0},
232 | {0,0,0,0,0,0,0},
233 | };
234 | int[][] brick = new int[][]{
235 | {1,1,1,1},
236 | {0,0,0,0},
237 | {0,0,0,0},
238 | {0,0,0,0},
239 | };
240 | int x = 5;
241 | int y = 0;
242 | boolean expResult = true;
243 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
244 | assertEquals(expResult, result);
245 | }
246 |
247 | @Test
248 | public void testOutOfBoundry2() {
249 | int[][] matrix = new int[][]{
250 | {0,0,0,0,0,0,0},
251 | {0,0,0,0,0,0,0},
252 | {0,0,0,0,0,0,0},
253 | {0,0,0,0,0,0,0},
254 | {0,0,0,0,0,0,0},
255 | {0,0,0,0,0,0,0},
256 | {0,0,0,0,0,0,0},
257 | {0,0,0,0,0,0,0},
258 | {0,0,0,0,0,0,0},
259 | {0,0,0,0,0,0,0},
260 | };
261 | int[][] brick = new int[][]{
262 | {1,1,1,1},
263 | {0,0,0,0},
264 | {0,0,0,0},
265 | {0,0,0,0},
266 | };
267 | int x = 0;
268 | int y = 45;
269 | boolean expResult = true;
270 | boolean result = MatrixOperations.intersect(matrix, brick, x, y);
271 | assertEquals(expResult, result);
272 | }
273 |
274 | @Test
275 | public void testCheckRemoving() {
276 | int[][] matrix = new int[][]{
277 | {0,0,0,0,0,0,0},
278 | {0,0,0,0,0,0,0},
279 | {0,0,0,0,0,0,0},
280 | {0,0,0,0,0,0,0},
281 | {0,0,0,0,0,0,0},
282 | {0,0,0,0,0,0,0},
283 | {1,1,1,1,1,1,1},
284 | {0,0,0,0,0,0,0},
285 | {0,0,0,0,0,0,0},
286 | {0,0,0,0,0,0,0},
287 | };
288 | ClearRow result = MatrixOperations.checkRemoving(matrix);
289 | assertEquals(1, result.getLinesRemoved());
290 | }
291 |
292 | @Test
293 | public void testCheckRemoving1() {
294 | int[][] matrix = new int[][]{
295 | {0,0,0,0,0,0,0},
296 | {0,0,0,0,0,0,0},
297 | {0,0,0,0,0,0,0},
298 | {0,0,0,0,0,0,0},
299 | {0,0,0,0,0,0,0},
300 | {0,0,0,0,0,0,0},
301 | {1,1,1,1,1,1,1},
302 | {0,0,0,0,0,0,0},
303 | {0,0,0,0,0,0,0},
304 | {1,1,1,1,1,1,1},
305 | };
306 | ClearRow result = MatrixOperations.checkRemoving(matrix);
307 | assertEquals(2, result.getLinesRemoved());
308 | }
309 |
310 |
311 | @Test
312 | public void testCheckRemoving2() {
313 | int[][] matrix = new int[][]{
314 | {0,0,0,0,0,0,0},
315 | {0,0,0,0,0,0,0},
316 | {0,0,0,0,0,0,0},
317 | {0,0,0,0,0,0,0},
318 | {1,1,0,0,0,0,0},
319 | {1,1,1,0,0,0,0},
320 | {1,1,1,1,1,1,1},
321 | {1,1,1,1,1,1,0},
322 | {1,1,1,1,1,0,0},
323 | {1,1,1,1,1,1,1},
324 | };
325 |
326 | int[][] expected = new int[][]{
327 | {0,0,0,0,0,0,0},
328 | {0,0,0,0,0,0,0},
329 | {0,0,0,0,0,0,0},
330 | {0,0,0,0,0,0,0},
331 | {0,0,0,0,0,0,0},
332 | {0,0,0,0,0,0,0},
333 | {1,1,0,0,0,0,0},
334 | {1,1,1,0,0,0,0},
335 | {1,1,1,1,1,1,0},
336 | {1,1,1,1,1,0,0},
337 | };
338 | ClearRow result = MatrixOperations.checkRemoving(matrix);
339 | assertArrayEquals(expected, result.getNewMatrix());
340 | }
341 |
342 |
343 | @Test
344 | public void testCheckRemoving3() {
345 | int[][] matrix = new int[][]{
346 | {0,0,0,0,0,0,0},
347 | {0,0,0,0,0,0,0},
348 | {0,0,0,0,0,0,0},
349 | {0,0,0,0,0,0,1},
350 | {1,1,0,0,0,0,0},
351 | {1,1,1,0,0,0,0},
352 | {1,1,1,1,1,1,1},
353 | {1,1,1,1,1,1,0},
354 | {1,1,1,1,1,0,0},
355 | {1,1,1,1,1,1,1},
356 | };
357 |
358 | int[][] expected = new int[][]{
359 | {0,0,0,0,0,0,0},
360 | {0,0,0,0,0,0,0},
361 | {0,0,0,0,0,0,0},
362 | {0,0,0,0,0,0,0},
363 | {0,0,0,0,0,0,0},
364 | {0,0,0,0,0,0,1},
365 | {1,1,0,0,0,0,0},
366 | {1,1,1,0,0,0,0},
367 | {1,1,1,1,1,1,0},
368 | {1,1,1,1,1,0,0},
369 | };
370 | ClearRow result = MatrixOperations.checkRemoving(matrix);
371 | assertArrayEquals(expected, result.getNewMatrix());
372 | }
373 |
374 | @Test
375 | public void testCheckRemoving4() {
376 | int[][] matrix = new int[][]{
377 | {0,0,0,0,0,0,0},
378 | {0,0,0,0,0,0,0},
379 | {0,0,0,0,0,0,0},
380 | {0,0,0,0,0,0,0},
381 | {0,0,0,0,0,0,0},
382 | {0,0,0,0,0,0,0},
383 | {1,1,1,1,1,1,1},
384 | {1,1,1,1,1,1,1},
385 | {1,1,1,1,1,1,1},
386 | {1,1,1,1,1,1,1},
387 | };
388 |
389 | int[][] expected = new int[][]{
390 | {0,0,0,0,0,0,0},
391 | {0,0,0,0,0,0,0},
392 | {0,0,0,0,0,0,0},
393 | {0,0,0,0,0,0,0},
394 | {0,0,0,0,0,0,0},
395 | {0,0,0,0,0,0,0},
396 | {0,0,0,0,0,0,0},
397 | {0,0,0,0,0,0,0},
398 | {0,0,0,0,0,0,0},
399 | {0,0,0,0,0,0,0},
400 | };
401 | ClearRow result = MatrixOperations.checkRemoving(matrix);
402 | assertArrayEquals(expected, result.getNewMatrix());
403 | }
404 |
405 | }
--------------------------------------------------------------------------------
/src/test/java/com/quirko/logic/bricks/IBrickTest.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.bricks;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | public class IBrickTest {
7 |
8 | @Test
9 | public void testGetShapeMatrix() throws Exception {
10 | Brick brick = new IBrick();
11 | brick.getShapeMatrix().get(0)[0][0] = 2;
12 | brick.getShapeMatrix().get(0)[1][0] = 3;
13 | Assert.assertEquals(0, brick.getShapeMatrix().get(0)[0][0]);
14 | Assert.assertEquals(1, brick.getShapeMatrix().get(0)[1][0]);
15 | }
16 |
17 | @Test
18 | public void testGetShapeMatrixList() throws Exception {
19 | Brick brick = new IBrick();
20 | brick.getShapeMatrix().remove(0);
21 | Assert.assertEquals(2, brick.getShapeMatrix().size());
22 | }
23 | }
--------------------------------------------------------------------------------
/src/test/java/com/quirko/logic/rotator/NextShapeInfoTest.java:
--------------------------------------------------------------------------------
1 | package com.quirko.logic.rotator;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | public class NextShapeInfoTest {
7 |
8 | @Test
9 | public void testGetShape() throws Exception {
10 | int[][] shape = new int[][]{{1, 2, 3}, {1, 2, 3}};
11 | int position = 3;
12 | NextShapeInfo nextShapeInfo = new NextShapeInfo(shape, position);
13 | nextShapeInfo.getShape()[0][0] = 23;
14 | Assert.assertEquals(1, nextShapeInfo.getShape()[0][0]);
15 | }
16 |
17 | @Test
18 | public void testGetPosition() throws Exception {
19 |
20 | }
21 | }
--------------------------------------------------------------------------------