├── .gitignore ├── src └── main │ ├── java │ └── lk │ │ └── ijse │ │ └── dep │ │ ├── service │ │ ├── .gitkeep │ │ ├── Piece.java │ │ ├── BoardUI.java │ │ ├── Player.java │ │ ├── Board.java │ │ └── impl │ │ │ ├── HumanPlayer.java │ │ │ ├── AiPlayer.java │ │ │ ├── Winner.java │ │ │ └── BoardImpl.java │ │ ├── Launcher.java │ │ ├── AppInitializer.java │ │ ├── util │ │ └── DEPAlert.java │ │ └── controller │ │ ├── CreatePlayerController.java │ │ └── BoardController.java │ └── resources │ ├── asset │ ├── error.png │ ├── info.png │ ├── warning.png │ └── connect-four.png │ ├── style │ ├── CreatePlayer.css │ ├── Style.css │ └── Board.css │ └── view │ ├── CreatePlayer.fxml │ └── Board.fxml ├── LICENSE.txt ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | target/ 4 | shade/ 5 | 6 | *.iml -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/.gitkeep: -------------------------------------------------------------------------------- 1 | It is okay to remove this file! -------------------------------------------------------------------------------- /src/main/resources/asset/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/K4VI12/connect-four-game/HEAD/src/main/resources/asset/error.png -------------------------------------------------------------------------------- /src/main/resources/asset/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/K4VI12/connect-four-game/HEAD/src/main/resources/asset/info.png -------------------------------------------------------------------------------- /src/main/resources/style/CreatePlayer.css: -------------------------------------------------------------------------------- 1 | .jfx-text-field{ 2 | -fx-font-size: 24px; 3 | -fx-alignment: center; 4 | } 5 | -------------------------------------------------------------------------------- /src/main/resources/asset/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/K4VI12/connect-four-game/HEAD/src/main/resources/asset/warning.png -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/Piece.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service; 2 | 3 | public enum Piece { 4 | GREEN,BLUE,EMPTY; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/asset/connect-four.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/K4VI12/connect-four-game/HEAD/src/main/resources/asset/connect-four.png -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/Launcher.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep; 2 | 3 | public class Launcher { 4 | 5 | public static void main(String[] args) { 6 | AppInitializer.main(args); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/BoardUI.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service; 2 | 3 | import lk.ijse.dep.service.impl.Winner; 4 | 5 | public interface BoardUI { 6 | public void update(int col, boolean isHuman); 7 | public void notifyWinner( Winner winner); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/Player.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service; 2 | 3 | public class Player { 4 | 5 | protected Board board; 6 | 7 | public Player() { 8 | } 9 | 10 | public Player(Board board) { 11 | this.board = board; 12 | } 13 | 14 | public void movePiece(int col){ 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/Board.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service; 2 | 3 | import lk.ijse.dep.service.impl.Winner; 4 | 5 | public interface Board { 6 | public int NUM_OF_ROWS = 5; 7 | public int NUM_OF_COLS = 6; 8 | 9 | 10 | public BoardUI getBoardUI(); 11 | public int findNextAvailableSpot(int col); 12 | public boolean isLegalMove(int col); 13 | public boolean existLegalMoves(); 14 | public void updateMove(int col,Piece move); 15 | public Winner findWinner(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/style/Style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | -fx-font-family: Ubuntu Serif; 3 | } 4 | 5 | .label{ 6 | -fx-font-size: 14px; 7 | } 8 | 9 | .title{ 10 | -fx-font-size: 28px; 11 | -fx-font-weight: 700; 12 | } 13 | 14 | .pane{ 15 | -fx-background-color: white; 16 | } 17 | 18 | .jfx-button{ 19 | -fx-font-size: 20px; 20 | -fx-font-weight: bold; 21 | -fx-background-color: #00b2ff; 22 | -fx-text-fill: #ffffff; 23 | -fx-cursor: hand; 24 | } 25 | 26 | 27 | 28 | .small{ 29 | -fx-font-size: 10px; 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/impl/HumanPlayer.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service.impl; 2 | 3 | import lk.ijse.dep.service.Board; 4 | import lk.ijse.dep.service.Piece; 5 | import lk.ijse.dep.service.Player; 6 | 7 | public class HumanPlayer extends Player { 8 | 9 | public HumanPlayer(Board newBoard) { 10 | this.board = newBoard; 11 | } 12 | 13 | @Override 14 | public void movePiece(int col) { 15 | 16 | if (board.isLegalMove(col)) { 17 | board.updateMove(col, Piece.BLUE); 18 | board.getBoardUI().update(col,true); 19 | Winner winner = board.findWinner(); 20 | if (winner != null) { 21 | board.getBoardUI().notifyWinner(winner); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/AppInitializer.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep; 2 | 3 | import javafx.application.Application; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Scene; 6 | import javafx.stage.Stage; 7 | 8 | import java.io.IOException; 9 | 10 | public class AppInitializer extends Application { 11 | 12 | public static void main(String[] args) { 13 | launch(args); 14 | } 15 | 16 | @Override 17 | public void start(Stage primaryStage) throws IOException { 18 | primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("/view/CreatePlayer.fxml")))); 19 | primaryStage.setResizable(false); 20 | primaryStage.setTitle("Connect 4 Game - Create Player"); 21 | primaryStage.show(); 22 | primaryStage.centerOnScreen(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/impl/AiPlayer.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service.impl; 2 | 3 | import lk.ijse.dep.service.Board; 4 | import lk.ijse.dep.service.Piece; 5 | import lk.ijse.dep.service.Player; 6 | 7 | import java.util.Random; 8 | 9 | public class AiPlayer extends Player { 10 | public AiPlayer(Board board) { 11 | this.board = board; 12 | } 13 | 14 | @Override 15 | public void movePiece(int col) { 16 | Random random = new Random(); 17 | //col = random.nextInt(5); 18 | col = minimax(); 19 | if (board.isLegalMove(col)) { 20 | board.updateMove(col, Piece.GREEN); 21 | board.getBoardUI().update(col,false); 22 | Winner winner = board.findWinner(); 23 | if (winner != null) { 24 | board.getBoardUI().notifyWinner(winner); 25 | } 26 | } 27 | } 28 | 29 | private int minimax() { 30 | // Piece[][] pieces = this.board.getPieces(); 31 | 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DEP. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/util/DEPAlert.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.util; 2 | 3 | import javafx.scene.control.Alert; 4 | import javafx.scene.control.ButtonType; 5 | import javafx.scene.image.Image; 6 | import javafx.scene.image.ImageView; 7 | 8 | public class DEPAlert extends Alert { 9 | 10 | public DEPAlert(AlertType alertType, String title, String header, String message, ButtonType... buttonTypes) { 11 | super(alertType, message, buttonTypes); 12 | setTitle(title); 13 | setHeaderText(header); 14 | 15 | String image = null; 16 | switch (alertType){ 17 | case ERROR: 18 | image = "/asset/error.png"; 19 | break; 20 | case INFORMATION: 21 | image = "/asset/info.png"; 22 | break; 23 | case WARNING: 24 | image = "/asset/warning.png"; 25 | break; 26 | } 27 | 28 | if (image !=null){ 29 | ImageView imgView = new ImageView(new Image(image)); 30 | imgView.setFitWidth(32); 31 | imgView.setFitHeight(32); 32 | setGraphic(imgView); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/style/Board.css: -------------------------------------------------------------------------------- 1 | .col{ 2 | -fx-border-width: 1 1 2 1; 3 | -fx-border-color: #dcdcdc; 4 | -fx-border-radius: 5px; 5 | -fx-background-radius: 5px; 6 | -fx-border-style: dashed solid solid solid; 7 | } 8 | 9 | .col-first{ 10 | -fx-border-width: 1 1 2 2; 11 | } 12 | 13 | .col-last{ 14 | -fx-border-width: 1 2 2 1; 15 | } 16 | 17 | .col-ai{ 18 | -fx-background-color: #e0ffcc; 19 | -fx-border-width: 2; 20 | -fx-border-color: #a6a6a6; 21 | -fx-border-radius: 5px; 22 | } 23 | 24 | .col-filled:hover{ 25 | -fx-background-color: #ffbfbf !important; 26 | -fx-border-color: #ff4f4f !important; 27 | } 28 | 29 | .col-human:hover{ 30 | -fx-cursor: hand; 31 | -fx-background-color: #d7f7ff; 32 | -fx-border-width: 2; 33 | -fx-border-color: #a6a6a6; 34 | -fx-border-radius: 5px; 35 | } 36 | 37 | .four{ 38 | -fx-font-size: 42px; 39 | } 40 | 41 | #lblStatus{ 42 | -fx-font-size: 24px; 43 | -fx-background-color: #f1f1f1; 44 | -fx-font-weight: bold; 45 | -fx-background-radius: 10px; 46 | } 47 | 48 | .circle-ai{ 49 | -fx-stroke-width: 0; 50 | -fx-fill: #00f504; 51 | } 52 | 53 | .circle-human{ 54 | -fx-stroke-width: 0; 55 | -fx-fill: #1e90ff; 56 | } 57 | 58 | .human{ 59 | -fx-text-fill: #1e90ff; 60 | -fx-background-color: #eafffc !important; 61 | } 62 | 63 | .ai{ 64 | -fx-text-fill: #00f531; 65 | -fx-background-color: #eaffe4 !important; 66 | } 67 | 68 | .final{ 69 | -fx-text-fill: #ff0089; 70 | } 71 | 72 | .winning-rect{ 73 | -fx-fill: rgb(255, 184, 0); 74 | -fx-opacity: 0.5; 75 | -fx-stroke-width: 1; 76 | -fx-stroke: #ff0089; 77 | -fx-arc-height: 10px; 78 | -fx-arc-width: 10px; 79 | } 80 | 81 | #pneOver{ 82 | -fx-background-color: rgba(100, 98, 98, 0.43); 83 | -fx-background-radius: 10px; 84 | } 85 | 86 | .jfx-button:hover{ 87 | -fx-text-fill: black !important; 88 | } -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/impl/Winner.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service.impl; 2 | 3 | import lk.ijse.dep.service.Piece; 4 | 5 | public class Winner { 6 | 7 | private Piece winningPiece; 8 | private int col1; 9 | private int col2; 10 | private int row1; 11 | private int row2; 12 | 13 | public Winner(Piece winningPiece) { 14 | this.winningPiece = winningPiece; 15 | this.col1 = -1; 16 | this.col2 = -1; 17 | this.row1 = -1; 18 | this.row2 = -1; 19 | } 20 | 21 | public Winner(Piece winningPiece, int col1, int row1, int col2, int row2) { 22 | this.winningPiece = winningPiece; 23 | this.col1 = col1; 24 | this.col2 = col2; 25 | this.row1 = row1; 26 | this.row2 = row2; 27 | } 28 | 29 | public Piece getWinningPiece() { 30 | return winningPiece; 31 | } 32 | 33 | public void setWinningPiece(Piece winningPiece) { 34 | this.winningPiece = winningPiece; 35 | } 36 | 37 | public int getCol1() { 38 | return col1; 39 | } 40 | 41 | public void setCol1(int col1) { 42 | this.col1 = col1; 43 | } 44 | 45 | public int getCol2() { 46 | return col2; 47 | } 48 | 49 | public void setCol2(int col2) { 50 | this.col2 = col2; 51 | } 52 | 53 | public int getRow1() { 54 | return row1; 55 | } 56 | 57 | public void setRow1(int row1) { 58 | this.row1 = row1; 59 | } 60 | 61 | public int getRow2() { 62 | return row2; 63 | } 64 | 65 | public void setRow2(int row2) { 66 | this.row2 = row2; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "Winner{" + 72 | "winningPiece=" + winningPiece + 73 | ", col1=" + col1 + 74 | ", col2=" + col2 + 75 | ", row1=" + row1 + 76 | ", row2=" + row2 + 77 | '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/resources/view/CreatePlayer.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/controller/CreatePlayerController.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.controller; 2 | 3 | import com.jfoenix.controls.JFXButton; 4 | import com.jfoenix.controls.JFXTextField; 5 | import javafx.application.Platform; 6 | import javafx.event.ActionEvent; 7 | import javafx.event.Event; 8 | import javafx.fxml.FXMLLoader; 9 | import javafx.scene.Scene; 10 | import javafx.scene.control.Alert; 11 | import javafx.scene.input.MouseEvent; 12 | import javafx.scene.shape.CubicCurve; 13 | import javafx.stage.Stage; 14 | import lk.ijse.dep.util.DEPAlert; 15 | 16 | import java.io.IOException; 17 | 18 | public class CreatePlayerController { 19 | public JFXTextField txtName; 20 | public JFXButton btnPlay; 21 | public CubicCurve curve; 22 | 23 | public void btnPlayOnAction(ActionEvent actionEvent) throws IOException { 24 | String name = txtName.getText(); 25 | if (name.isBlank()){ 26 | new DEPAlert(Alert.AlertType.ERROR, "Error", "Empty Name", "Name can't be empty").show(); 27 | txtName.requestFocus(); 28 | txtName.selectAll(); 29 | return; 30 | }else if (!name.matches("[A-Za-z ]+")){ 31 | new DEPAlert(Alert.AlertType.WARNING, "Error", "Invalid Name", "Please enter a valid name").show(); 32 | txtName.requestFocus(); 33 | txtName.selectAll(); 34 | return; 35 | } 36 | Stage stage = new Stage(); 37 | FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/view/Board.fxml")); 38 | stage.setScene(new Scene(fxmlLoader.load())); 39 | ((BoardController)(fxmlLoader.getController())).initData(name); 40 | stage.setResizable(false); 41 | stage.setTitle("Connect 4 Game - Player: " + name); 42 | stage.show(); 43 | stage.centerOnScreen(); 44 | // stage.setOnCloseRequest(Event::consume); 45 | btnPlay.getScene().getWindow().hide(); 46 | Platform.runLater(stage::sizeToScene); 47 | } 48 | 49 | public void rootOnMouseExited(MouseEvent mouseEvent) { 50 | curve.setControlX2(451.8468017578125); 51 | curve.setControlY2(-36); 52 | } 53 | 54 | public void rootOnMouseMove(MouseEvent mouseEvent) { 55 | curve.setControlX2(mouseEvent.getX()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drawing The Connect-4 Game Assignment 2 | In this assignment, you will implement the complete logic behind the "Connect 4 Game" including the artificial Intelligence part of the computer player. 3 | Please read the assignment carefully before proceeding. You can find the assignment [here](https://drive.google.com/file/d/1qlqLBfI3Xu0p_BXRNbRneMWtyLlakXGp/view?usp=sharing). 4 | In case if you have any doubts regarding the assignment please make sure to clarify them upfront. 5 | 6 | ### How to use this repo 7 | * `git clone https://github.com/Ranjith-Suranga/connect-four-game-assignment.git` 8 | * Open the `pom.xml` via IntelliJ IDEA 9 | * Make sure to the open it as a project, if prompt 10 | * Reload the `pom.xml` file via **Maven Tool Window** 11 | * Create a run configuration for Maven via `Run > Edit Configuration` 12 | * Add `javafx:run` as the `Run` command 13 | * That's it. 14 | * **But do not try to run or compile the application yet** 15 | * **Follow the instructions in the assignment** 16 | 17 | ### FAQ 18 | 19 | **Q: Can I delete the `.gitkeep` file in `lk.ijse.dep.service` package?**
20 | Yes, you can 21 | 22 | **Q: Why can't I run any games that are in the release page?**
23 | Open a terminal window and type `java -version` to find out the java version. You should have JDK 11 installed on your system to run these games. If you have JDK 11 installed on your system and still unable to run the games, then seek your course instructor's help to get it work. 24 | 25 | **Q: I have found some broken links and spelling mistakes in the assignment. How can I inform?**
26 | Please open a [new issue](https://github.com/Ranjith-Suranga/connect-four-game-assignment/issues/new) mentioning the broken link or spelling mistake. Thank you for informing! 27 | 28 | **Q: I have doubts to clarify regarding the assignment, what should I do?**
29 | Please contact your course instructor 😉 30 | 31 | ### Game 32 | If you want to find out how the game looks like at the end of each step, check out the [release page](https://github.com/Ranjith-Suranga/connect-four-game-assignment/releases) and follow the instructions there. 33 | 34 | ### Version 35 | 0.0.5 36 | 37 | ### License 38 | Copyright © 2022 DEP. All rights reserved
39 | This project is licensed under the [MIT](LICENSE.txt) License. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | lk.ijse.dep 8 | connect-four-assignment 9 | 0.1.0 10 | 11 | 12 | 11 13 | 11 14 | 15 | 16 | 17 | 18 | org.openjfx 19 | javafx-fxml 20 | 18.0.2 21 | 22 | 23 | 24 | com.jfoenix 25 | jfoenix 26 | 9.0.1 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-compiler-plugin 35 | 3.8.1 36 | 37 | 11 38 | 39 | 40 | 41 | org.openjfx 42 | javafx-maven-plugin 43 | 0.0.8 44 | 45 | lk.ijse.dep.AppInitializer 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-shade-plugin 51 | 3.2.0 52 | 53 | 54 | package 55 | 56 | shade 57 | 58 | 59 | true 60 | project-classifier 61 | shade\${project.artifactId}.jar 62 | 63 | 65 | lk.ijse.dep.Launcher 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/service/impl/BoardImpl.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.service.impl; 2 | 3 | import lk.ijse.dep.service.Board; 4 | import lk.ijse.dep.service.BoardUI; 5 | import lk.ijse.dep.service.Piece; 6 | 7 | public class BoardImpl implements Board { 8 | 9 | private Piece[][] pieces; 10 | private BoardUI boardUI; 11 | 12 | public BoardImpl(BoardUI boardUI) { 13 | this.boardUI = boardUI; 14 | pieces = new Piece[NUM_OF_COLS][NUM_OF_ROWS]; 15 | for (int i = 0; i < NUM_OF_COLS; i++) { 16 | for (int j = 0; j =0){ 43 | for (int i = 0; i < NUM_OF_ROWS; i++) { 44 | if (pieces[col][i]==Piece.EMPTY){ 45 | return i; 46 | } 47 | } 48 | } 49 | return -1; 50 | } 51 | 52 | @Override 53 | public boolean isLegalMove(int col) { 54 | return findNextAvailableSpot(col)!=-1? true:false; 55 | } 56 | 57 | @Override 58 | public boolean existLegalMoves() { 59 | for (int i = 0; i < NUM_OF_COLS; i++) { 60 | if (isLegalMove(i)) { 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | @Override 68 | public void updateMove(int col, Piece move) { 69 | if (isLegalMove(col)) { 70 | pieces[col][findNextAvailableSpot(col)] = move; 71 | } 72 | } 73 | 74 | @Override 75 | public Winner findWinner() { 76 | for (int i = 0; i < NUM_OF_COLS; i++) { 77 | int countBlue = 0; 78 | int countGreen = 0; 79 | for (int j = 0; j < NUM_OF_ROWS; j++) { 80 | if (pieces[i][j]==Piece.BLUE) { 81 | countBlue++; 82 | if (countBlue==4) { 83 | return new Winner(Piece.BLUE,i,j-3,i,j); 84 | } 85 | } 86 | if(pieces[i][j]==Piece.GREEN){ 87 | countGreen++; 88 | countBlue = 0; 89 | if(countGreen==4){ 90 | return new Winner(Piece.GREEN,i,j-3,i,j); 91 | } 92 | } 93 | } 94 | } 95 | 96 | for (int i = 0; i < NUM_OF_ROWS; i++) { 97 | int countBlue = 0; 98 | int countGreen = 0; 99 | for (int j = 0; j < NUM_OF_COLS; j++) { 100 | if (pieces[j][i]==Piece.BLUE) { 101 | countBlue++; 102 | if (countBlue==4) { 103 | return new Winner(Piece.BLUE,j-3,i,j,i); 104 | } 105 | } 106 | if(pieces[j][i]==Piece.GREEN){ 107 | countGreen++; 108 | countBlue = 0; 109 | if(countGreen==4){ 110 | return new Winner(Piece.GREEN,j-3,i,j,i); 111 | } 112 | } 113 | if(pieces[j][i]==Piece.EMPTY){ 114 | countBlue = 0; 115 | countGreen = 0; 116 | } 117 | } 118 | } 119 | /*if(existLegalMoves()){ 120 | return new Winner(Piece.EMPTY); 121 | }*/ 122 | if (!existLegalMoves()) { 123 | return new Winner(Piece.EMPTY); 124 | } 125 | return null; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/resources/view/Board.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 96 | 97 | 98 | 99 | 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/lk/ijse/dep/controller/BoardController.java: -------------------------------------------------------------------------------- 1 | package lk.ijse.dep.controller; 2 | 3 | import com.jfoenix.controls.JFXButton; 4 | import javafx.animation.KeyFrame; 5 | import javafx.animation.Timeline; 6 | import javafx.animation.TranslateTransition; 7 | import javafx.application.Platform; 8 | import javafx.event.ActionEvent; 9 | import javafx.scene.Group; 10 | import javafx.scene.control.Label; 11 | import javafx.scene.layout.AnchorPane; 12 | import javafx.scene.layout.Pane; 13 | import javafx.scene.layout.VBox; 14 | import javafx.scene.shape.Circle; 15 | import javafx.scene.shape.Rectangle; 16 | import javafx.util.Duration; 17 | import lk.ijse.dep.service.*; 18 | import lk.ijse.dep.service.impl.AiPlayer; 19 | import lk.ijse.dep.service.impl.BoardImpl; 20 | import lk.ijse.dep.service.impl.HumanPlayer; 21 | import lk.ijse.dep.service.impl.Winner; 22 | 23 | public class BoardController implements BoardUI { 24 | 25 | private static final int RADIUS = 42; 26 | 27 | public Label lblStatus; 28 | public Group grpCols; 29 | public AnchorPane root; 30 | public Pane pneOver; 31 | public JFXButton btnPlayAgain; 32 | 33 | private String playerName; 34 | private boolean isAiPlaying; 35 | private boolean isGameOver; 36 | 37 | private Player humanPlayer; 38 | private Player aiPlayer; 39 | 40 | private void initializeGame() { 41 | Board newBoard = new BoardImpl(this); 42 | humanPlayer = new HumanPlayer(newBoard); 43 | aiPlayer = new AiPlayer(newBoard); 44 | } 45 | 46 | public void initialize() { 47 | initializeGame(); 48 | grpCols.getChildren().stream().map(n -> (VBox) n).forEach(vbox -> vbox.setOnMouseClicked(mouseEvent -> colOnClick(vbox))); 49 | } 50 | 51 | private void colOnClick(VBox col) { 52 | if (!isAiPlaying && !isGameOver) humanPlayer.movePiece(grpCols.getChildren().indexOf(col)); 53 | } 54 | 55 | public void initData(String playerName) { 56 | this.playerName = playerName; 57 | } 58 | 59 | @Override 60 | public void update(int col, boolean isHuman) { 61 | if (isGameOver) return; 62 | VBox vCol = (VBox) grpCols.lookup("#col" + col); 63 | if (vCol.getChildren().size() == 5) 64 | throw new RuntimeException("Double check your logic, no space available within the column: " + col); 65 | if (!isHuman) { 66 | vCol.getStyleClass().add("col-ai"); 67 | } 68 | Circle circle = new Circle(RADIUS); 69 | circle.getStyleClass().add(isHuman ? "circle-human" : "circle-ai"); 70 | vCol.getChildren().add(0, circle); 71 | if (vCol.getChildren().size() == 5) vCol.getStyleClass().add("col-filled"); 72 | TranslateTransition tt = new TranslateTransition(Duration.millis(250), circle); 73 | tt.setFromY(-50); 74 | tt.setToY(circle.getLayoutY()); 75 | tt.playFromStart(); 76 | lblStatus.getStyleClass().clear(); 77 | lblStatus.getStyleClass().add(isHuman ? "ai" : "human"); 78 | if (isHuman) { 79 | isAiPlaying = true; 80 | grpCols.getChildren().stream().map(n -> (VBox) n).forEach(vbox -> vbox.getStyleClass().remove("col-human")); 81 | KeyFrame delayFrame = new KeyFrame(Duration.millis(300), actionEvent -> { 82 | if (!isGameOver) lblStatus.setText("Wait, AI is playing"); 83 | }); 84 | KeyFrame keyFrame = new KeyFrame(Duration.seconds(0.5), actionEvent -> { 85 | if (!isGameOver) aiPlayer.movePiece(-1); 86 | }); 87 | new Timeline(delayFrame, keyFrame).playFromStart(); 88 | } else { 89 | KeyFrame delayFrame = new KeyFrame(Duration.millis(300), actionEvent -> { 90 | grpCols.getChildren().stream().map(n -> (VBox) n).forEach(vbox -> { 91 | vbox.getStyleClass().remove("col-ai"); 92 | vbox.getStyleClass().add("col-human"); 93 | }); 94 | }); 95 | new Timeline(delayFrame).playFromStart(); 96 | isAiPlaying = false; 97 | lblStatus.setText(playerName + ", it is your turn now!"); 98 | } 99 | } 100 | 101 | @Override 102 | public void notifyWinner(Winner winner) { 103 | isGameOver = true; 104 | lblStatus.getStyleClass().clear(); 105 | lblStatus.getStyleClass().add("final"); 106 | switch (winner.getWinningPiece()) { 107 | case BLUE: 108 | lblStatus.setText(String.format("%s, you have won the game !", playerName)); 109 | break; 110 | case GREEN: 111 | lblStatus.setText("Game is over, AI has won the game !"); 112 | break; 113 | case EMPTY: 114 | lblStatus.setText("Game is tied !"); 115 | } 116 | if (winner.getWinningPiece() != Piece.EMPTY) { 117 | VBox vCol = (VBox) grpCols.lookup("#col" + winner.getCol1()); 118 | Rectangle rect = new Rectangle((winner.getCol2() - winner.getCol1() + 1) * vCol.getWidth(), 119 | (winner.getRow2() - winner.getRow1() + 1) * (((RADIUS + 2) * 2))); 120 | rect.setId("rectOverlay"); 121 | root.getChildren().add(rect); 122 | rect.setLayoutX(vCol.localToScene(0, 0).getX()); 123 | rect.setLayoutY(vCol.localToScene(0, 0).getY() + (4 - winner.getRow2()) * ((RADIUS + 2) * 2)); 124 | rect.getStyleClass().add("winning-rect"); 125 | } 126 | pneOver.setVisible(true); 127 | pneOver.toFront(); 128 | Platform.runLater(btnPlayAgain::requestFocus); 129 | } 130 | 131 | public void btnPlayAgainOnAction(ActionEvent actionEvent) { 132 | initializeGame(); 133 | isAiPlaying = false; 134 | isGameOver = false; 135 | pneOver.setVisible(false); 136 | lblStatus.getStyleClass().clear(); 137 | lblStatus.setText("LET'S PLAY !"); 138 | grpCols.getChildren().stream().map(n -> (VBox) n).forEach(vbox -> { 139 | vbox.getChildren().clear(); 140 | vbox.getStyleClass().remove("col-ai"); 141 | vbox.getStyleClass().remove("col-filled"); 142 | vbox.getStyleClass().add("col-human"); 143 | }); 144 | root.getChildren().remove(root.lookup("#rectOverlay")); 145 | } 146 | } 147 | --------------------------------------------------------------------------------