2

I tried to recreate Connect Four, and I succeeded. But I wanted to give the player an indication of where the winning four discs were, by switching the color every so often. I am new to threads and the concept of time in programming.

I also succeeded in giving the user this indication, but after I close the application, the console still gives output, also when I use setOnCloseRequest.

A few other questions:
1: for colors I used html names, is it better to use a hex triplet or no preference.
2: To stop the grid and other elements from hugging the left side of the screen, I added a border with the same color as the background, is there a better way of doing this?
3: I did not create a method for translating the keycode to an integer but instead created in the init function. I did this because I don't know how to pass the keyevent. How to do this?

Here is the code:

import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class FourInARow extends Application { GridPane boardGrid = new GridPane(); Label[][] labels = new Label[7][7]; Label statusLabel = new Label(); int[][] cell = new int[7][6]; int player = 0; int won = 0; String baseStyle = "-fx-background-radius: 40; -fx-min-width: 80; -fx-min-height: 80; -fx-alignment: center; -fx-border-width: 2; -fx-border-color: #000000;-fx-background-color: "; ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); public static void main(String[] args){launch(args);} private void init(Stage window){ createLabels(); startGame(); Label above = new Label("Try to connect four discs in a row!"); above.setStyle("-fx-font-size: 30; -fx-alignment: center; -fx-min-width: 600"); boardGrid.setStyle("-fx-background-color: silver;-fx-border-color: #F4F4F4;-fx-border-width: 0 20 0 20"); Button newGame = new Button("New Game"); newGame.setStyle("-fx-min-width: 100;-fx-font-size:20"); newGame.setOnAction(e -> startGame()); statusLabel.setStyle("-fx-font-size: 30;-fx-alignment: center; -fx-min-width: 300;"); HBox below = new HBox(); below.setStyle("-fx-border-width: 0 0 0 20;-fx-border-color: #F4F4F4"); below.getChildren().addAll(newGame, statusLabel); VBox layout = new VBox(); layout.getChildren().addAll(above, boardGrid, below); Scene scene = new Scene(layout, 600, 620); scene.setOnKeyPressed(e -> { if (won == 0) { try { String k = e.getCode().toString(); int l = k.length(); int col = Integer.parseInt(k.substring(l - 1, l)) - 1; placeDisc(col, player); switchPlayer(); updateScreen(); } catch (NumberFormatException | ArrayIndexOutOfBoundsException error) { System.out.println("error: " + error); } } }); window.setScene(scene); window.setTitle("Connect Four"); threadThing(); } private void threadThing() { service.scheduleAtFixedRate(() -> { try { wonStyle(); } catch (InterruptedException e) { e.printStackTrace(); } }, 0, 1, TimeUnit.SECONDS); } private void startGame() { cell = new int[7][6]; won = player = 0; statusLabel.setText(""); updateScreen(); } private void updateScreen() { for (int i = 0; i < 7; i++) { for (int j = 0; j < 6; j++) { labels[i][j].setStyle(baseStyle + addStyle(cell[i][j])); } labels[i][6].setText(Integer.toString(i+1)); labels[i][6].setStyle("-fx-alignment: center;-fx-min-width: 80;-fx-background-color: #F4F4F4;-fx-font-size: 30;"); } switch(won) { case 1: statusLabel.setText("Blue has won!");break; case 2: statusLabel.setText("Yellow has won!");break; } } private String addStyle(int cell) { String style = "silver"; switch(cell){ case 1: style = "blue"; break; case 2: style = "yellow"; break; case 3: style = "darkblue"; break; case 4: style = "gold;"; break; } return style; } private void placeDisc(int col, int player) { for (int i = 5; i >= 0 ; i--) { if(cell[col][i] == 0){ cell[col][i] = 1; if(player == 1) cell[col][i] = 2; break; }else{ if(i==0) switchPlayer(); } } checkWon(); } private void checkWon() { for (int i = 0; i < 7; i++) { for (int j = 0; j < 6; j++) { if (cell[i][j] != 0) { try { if (cell[i][j] == cell[i][j + 1] && cell[i][j] == cell[i][j + 2] && cell[i][j] == cell[i][j + 3]) { won = cell[i][j]; cell[i][j] = cell[i][j + 1] = cell[i][j + 2] = cell[i][j + 3] = cell[i][j] + 2; } }catch(ArrayIndexOutOfBoundsException error) {} try { if (cell[i][j] == cell[i + 1][j] && cell[i][j] == cell[i + 2][j] && cell[i][j] == cell[i + 3][j]) { System.out.println("Horizontal win"); won = cell[i][j]; cell[i][j] = cell[i + 1][j] = cell[i + 2][j] = cell[i + 3][j] = cell[i][j] + 2; } }catch(ArrayIndexOutOfBoundsException error) {} try { if (cell[i][j] == cell[i + 1][j + 1] && cell[i][j] == cell[i + 2][j + 2] && cell[i][j] == cell[i + 3][j + 3]) { won = cell[i][j]; cell[i][j] = cell[i + 1][j + 1] = cell[i + 2][j + 2] = cell[i + 3][j + 3] = cell[i][j] + 2; } }catch(ArrayIndexOutOfBoundsException error) {} try { if (cell[i][j] == cell [i + 1][j - 1] && cell[i][j] == cell[i + 2][j - 2] && cell[i][j] == cell[i + 3][j - 3]) { won = cell[i][j]; cell[i][j] = cell[i + 1][j - 1] = cell[i + 2][j - 2] = cell[i + 3][j - 3] = cell[i][j] + 2; } }catch(ArrayIndexOutOfBoundsException error) {} } } } } private void switchPlayer() { if(player == 0) player = 2; player--; } private void createLabels() { for (int i = 0; i < 7; i++) { for (int j = 0; j < 7; j++) { labels[i][j] = new Label(); boardGrid.add(labels[i][j], i, j); } } } private void wonStyle() throws InterruptedException { System.out.println("Test"); boolean run = false; for (int i = 0; i < 7; i++) { for (int j = 0; j < 6; j++) { if(cell[i][j] > 2 && !run){ Thread.sleep(500); addStyleFlicker(); run = true; } } } } private void addStyleFlicker() throws InterruptedException { String[] styleOne = {"-fx-background-radius: 40; -fx-min-width: 80; -fx-min-height: 80; -fx-alignment: center; -fx-border-width: 2; -fx-border-color: #000000;-fx-background-color: blue;", "-fx-background-radius: 40; -fx-min-width: 80; -fx-min-height: 80; -fx-alignment: center; -fx-border-width: 2; -fx-border-color: #000000;-fx-background-color: darkblue;"}; String[] styleTwo = {"-fx-background-radius: 40; -fx-min-width: 80; -fx-min-height: 80; -fx-alignment: center; -fx-border-width: 2; -fx-border-color: #000000;-fx-background-color: yellow;", "-fx-background-radius: 40; -fx-min-width: 80; -fx-min-height: 80; -fx-alignment: center; -fx-border-width: 2; -fx-border-color: #000000;-fx-background-color: gold;"}; for (int i = 0; i < 7; i++) { for (int j = 0; j < 6; j++) { if(cell[i][j] == 3){ labels[i][j].setStyle(styleOne[0]); }else if(cell[i][j] == 4){ labels[i][j].setStyle(styleTwo[0]); } } } Thread.sleep(500); for (int i = 0; i < 7; i++) { for (int j = 0; j < 6; j++) { if(cell[i][j] == 3){ labels[i][j].setStyle(styleOne[1]); }else if(cell[i][j] == 4) { labels[i][j].setStyle(styleTwo[1]); } } } } @Override public void start(Stage window) throws Exception { init(window); window.show(); } } 
5
  • possible duplicate of How to close a javafx application? Commented Apr 24, 2015 at 13:35
  • I've viewed that post but didn't manage to find a solution. Commented Apr 24, 2015 at 13:36
  • 1
    Create the executor with a thread factory that creates daemon threads. (Will post an answer with code if no one else does before I get back to my computer. ) Commented Apr 24, 2015 at 13:39
  • I suspect you need to shut down your executor, and make sure that your Runnable implementation within that resets the interrupted flag once an InterruptedException is raised/thrown Commented Apr 24, 2015 at 13:40
  • stackoverflow.com/a/31519051/3796962 -- Please find this link, it may be help you. Thanks.. Commented May 12, 2016 at 11:04

2 Answers 2

5

Per the Java API docs on the Thread class...

The Java Virtual Machine continues to execute threads until either of the following occurs:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.

So, you need that Executor to create Daemon threads that will die when the JVM is ready to exit. You do this with a custom thread factory. Here's a really simple example:

Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } }); 
Sign up to request clarification or add additional context in comments.

5 Comments

That also worked, thanks David, is one of both answers better than the other? Or more suitable for this case?
I think they're both valid, but System.exit(0) IMO is a bandaid over the real cause which is the non-daemon thread keeping the JVM from exiting. In any event, it's good to know about daemon threads so you can use them in situations where calling System.exit might not be a great idea, like web applications or libraries that third parties might depend on.
@RichardKoetchruyter Gotta agree with David. This answer encourages better practices. Your application is still running due to another thread still being alive. Setting that thread to daemon ensures the VM can still exit even though that thread may still be running. System.exit is more of a cheap fix, basically stating "I don't know what's keeping my VM running, so exit everything". This answer states "this thread will not cause the VM to continue running"
What's the difference between calling Runtime,getRuntime().exit(0) and System.exit(0)? Is the former better practice?
From what I can tell, System.exit() calls Runtime.getRuntime().exit(), so it shouldn't matter. There's also a Runtime.getRuntime().halt() option, which is more akin to a force-kill (think kill -9 on the terminal). It won't run finalizers, it won't close resources, and it won't run shutdown hooks.
2

Overriding the stop() method from the Application class will allow you to close your console app:

@Override public void stop() { System.exit(0); } 

1 Comment

I guess kill -9 also works, but I'd strongly suggest to stick to gracefully sopping all threads.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.