0

Before I start I want to explain that I've looked at many other questions like this and many videos on the net but none of them help me understand how timer works or how I'm about to create one for myself, hope none thinks this is a unnecessary post.

I don't understand for example what delay is used for, I would love to learn everything about Timers and how they are used.

So I'm creating a 15 puzzle game, the game is done but I wanted to add a another function.

A timer that is displayed on a JLabel. Specifics:

  1. Timer counts down from 5 or 10 minutes.
  2. Wanted format for the timer "mm:ss".
  3. As soon as the first button is pressed the timer starts to countdown.
  4. If solved before the timer runs out you win, else you lose.
  5. If you press on the "New Game" button it resets to 5 or 10 minutes.
  6. And the countdown starts again as soon as you press the first button in the game.

I would love if you could explain what the params are used for and what Timer I should use for my case. Also I'm using java swing.

Here is my game so far if you are interested:

class GameLogic extends JFrame implements ActionListener {

private JPanel grid = new JPanel();
private JPanel overHead = new JPanel();
private JButton newGameButton = new JButton("NEW GAME");
private JButton[][] buttons = new JButton[4][4];
private JButton[][] winPattern = new JButton[4][4];
private JButton button0 = new JButton("");
private JLabel timerLabel = new JLabel("TIMER");
private int emptyIndex;
private int sourceIndex;
private int sourceRow;
private int sourceCol;
private int blankRow;
private int blankCol;
private int movesCounter = 0;
private JLabel movesLabel = new JLabel("MOVES");

public GameLogic() {
    setLayout(new BorderLayout());
    add(overHead, BorderLayout.NORTH);
    overHead.setLayout(new BorderLayout());
    overHead.add(newGameButton, BorderLayout.WEST);
    overHead.add(movesLabel, BorderLayout.EAST);
    overHead.add(timerLabel, BorderLayout.CENTER);
    movesLabel.setFont(new Font("Street Cred", Font.PLAIN, 20));
    newGameButton.setPreferredSize(new Dimension(100, 50));
    newGameButton.setFont(new Font("Street Cred", Font.PLAIN, 13));
    overHead.setBorder(new EmptyBorder(50, 50, 10, 50));
    grid.setBorder(new EmptyBorder(50, 50, 50, 50));
    newGameButton.addActionListener(this);
    add(grid, BorderLayout.CENTER);
    setBackground(Color.RED);
    grid.setLayout(new GridLayout(4, 4));
    try {
        GraphicsEnvironment ge =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
        ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File("street cred.ttf")));
    } catch (IOException | FontFormatException e) {
        //Handle exception
    }
    int i = 1;
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (row == 3 && col == 3) {
                buttons[row][col] = button0;
                grid.add(buttons[row][col]);
                buttons[row][col].setBackground(Color.WHITE);
                buttons[row][col].setName("button0");
                winPattern[row][col] = button0;
            } else {
                buttons[row][col] = new JButton(i + "");
                grid.add(buttons[row][col]);
                buttons[row][col].addActionListener(this);
                buttons[row][col].setBackground(Color.RED);
                buttons[row][col].setName("button" + i);
                buttons[row][col].setFont(new Font("Street Cred", Font.PLAIN, 40));
                winPattern[row][col] = buttons[row][col];
                i++;
            }
        }
    }

    do {
        shuffle();
    } while (!isSolvable());
    try {
        setIconImage(ImageIO.read(new File("C:\\Users\\Allan\\Documents\\Nackademin\\OOP\\test2\\icon.png")));
    } catch (IOException e) {
        e.printStackTrace();
    }
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
    setTitle("PUZZLE GAME");
    setCursor(new Cursor(Cursor.HAND_CURSOR));
    setResizable(false);
    setLocation(500, 200);
    setSize(600, 600);
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public boolean isSolvable() {
    int inv_counter = 0;
    int[] values = new int[16];
    // Lägger alla komponenters nummer i en int array
    for (int i = 0; i < grid.getComponents().length; i++) {
        if (grid.getComponents()[i] == button0) {
            values[i] = 0;
        } else {
            JButton temp = (JButton) grid.getComponents()[i];
            values[i] = Integer.parseInt(temp.getText());
        }

    }

    for (int i = 0; i < values.length - 1; i++) {
        for (int j = i + 1; j < values.length; j++) {
            if (values[i] < values[j]) {
                inv_counter++;
            }
        }
    }

    return inv_counter % 2 == 0;
}


public boolean isSwappable(JButton button) {

    // för att hitta platsen på knappen man trycker och även den blanka platsen
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (buttons[row][col] == button) {
                sourceRow = row;
                sourceCol = col;
            } else if (buttons[row][col] == button0) {
                blankRow = row;
                blankCol = col;
            }
        }
    }
    sourceIndex = (sourceRow * 4) + sourceCol;
    emptyIndex = (blankRow * 4) + blankCol;
    // om den är till höger
    if (sourceCol != 3 && sourceRow == blankRow && buttons[sourceRow][sourceCol + 1] == button0) {
        return true;
    }
    // om den är till vänster
    else if (sourceCol != 0 && sourceRow == blankRow && buttons[blankRow][sourceCol - 1] == button0) {
        return true;
    }
    //om den är nedanför
    else if (sourceRow != 0 && sourceCol == blankCol && buttons[sourceRow - 1][sourceCol] == button0) {
        return true;
    }
    //om den är ovanför
    else if (sourceRow != 3 && sourceCol == blankCol && buttons[sourceRow + 1][sourceCol] == button0) {
        return true;
    }
    return false;
}

public void swap(JButton source) {
    JButton tempButton = buttons[sourceRow][sourceCol];
    buttons[sourceRow][sourceCol] = buttons[blankRow][blankCol];
    buttons[blankRow][blankCol] = tempButton;
    grid.remove(button0);
    grid.remove(source);

    if (emptyIndex < sourceIndex) {
        grid.add(source, emptyIndex);
        grid.add(button0, sourceIndex);
    } else if (emptyIndex > sourceIndex) {
        grid.add(button0, sourceIndex);
        grid.add(source, emptyIndex);
    }
    revalidate();
    repaint();
}

public void shuffle() {
    Random random = new Random();

    //randomize positions for 2D array buttons
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            int randomNumber = random.nextInt(16);
            int randomRow = randomNumber / 4;
            int randomCol = randomNumber % 4;
            JButton temp = buttons[row][col];
            buttons[row][col] = buttons[randomRow][randomCol];
            buttons[randomRow][randomCol] = temp;
        }
    }
    //remove all components from panel
    grid.removeAll();

    // add components with randomized position to panel
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            grid.add(buttons[row][col]);
        }
    }
    revalidate();
    repaint();
}

public boolean isSolved() {
    int counter = 0;
    for (int row = 0; row < buttons.length; row++) {
        for (int col = 0; col < buttons.length; col++) {
            if (winPattern[row][col].getText().equals(buttons[row][col].getText())) {
                counter++;
            }
        }
    }
    if (counter == 16) {
        return true;
    } else {
        return false;
    }
}

public void moves() {
    movesCounter++;
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
}

public void timerCountDown() {
}

public void reset() {
    movesCounter = 0;
    movesLabel.setText("<html>MOVES<br><html>" + "----- " + movesCounter + " -----");
}

public void newGame() {
    do {
        shuffle();
        reset();
    } while (!isSolvable());
}

@Override
public void actionPerformed(ActionEvent e) {
    JButton source = (JButton) e.getSource();

    if (source == newGameButton) {
        newGame();
    } else if (isSwappable(source)) {
        swap(source);
        moves();
    }

    if (isSolved()) {
        JOptionPane.showMessageDialog(null, "YOU BEAT THE GAME!\nMoves: " + movesCounter + "\nTime: ");
        newGame();
    }
}
}

Sterconium
  • 559
  • 4
  • 20
AllanJ
  • 147
  • 1
  • 15
  • 1
    There's a mix of terms here. You are talking about a countdown timer, basically, an oven clock. In Java, a timer is an object used for performing a task repeatedly at fixed interval, starting after a given delay. You can use a Java timer to implement an "oven clock" timer, by setting a task that decrements the displayed time every second and terminating the timer when it reaches zero. Is it clearer now? – RealSkeptic Oct 29 '19 at 10:32
  • I've seen people use 1000 for delay as milliseconds/ a second. So you mean, for each second I could set a new text for label for example? Or am I wrong? – AllanJ Oct 29 '19 at 10:37
  • 1
    Yes, exactly. Note that there are two Timer classes in Java - `java.swing.Timer` which is probably what you need, and `java.util.Timer` which is used for things other than Swing applications. – RealSkeptic Oct 29 '19 at 10:43
  • *I've looked at many other questions like this* - show us the links to the question and tell us what you don't understand. Why did you post hundreds of lines of code? Your question is about displaying text every second when you click on a button. The "puzzle" logic is irrelevant to the question. To learn the basics of a Timer create a frame with a JLabel and a Start button. Then when you start the Timer you update the label every second. For example, check out: https://stackoverflow.com/a/7816604/131872 for a simple example. If there is something you don't understand ask a specific question. – camickr Oct 29 '19 at 14:38

1 Answers1

0

You can use a ScheduledExecutorService (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html)

They allow you to run a task (in your case, a timer) at a precise moment, and to repeat the task after a given delay.

To create your ScheduledExecutorService :

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

Then you need to run your task, including delay and period :

ScheduledFuture<?> handler = scheduler.scheduleAtFixedRate(yourTask, 0, 1, TimeUnit.SECONDS);

"yourTask" is a Runnable. You can either use a lambda, such as () -> System.out.println("BEEP") or a method reference, such as this::run, with a run method that would display on your screen the time. You can have two fields minutes and seconds initially set at 5 and 0 (or 10 and 0 of you want 10 minutes, that you would change every time therun` method is called, then display on your screen the correct time :

private void run(){
    seconds--;
    if(seconds < 0) {
        minutes--;
        seconds = 59;
    }
    displayTimeOnScreen();

    if(minutes == 0 && seconds == 0) {
        handler.cancel(true);
    }
}

You can use handler.cancel(true) to stop the task.

Akami
  • 189
  • 8
  • Sorry, are you asking me to create a task after handler has been created. In that case myTask has not been initialized, but we also need the `handler.cancel(true);` inside the run() scope. I can't make it work. – AllanJ Oct 29 '19 at 11:50
  • Nope, the handler is created when you launch the task – Akami Oct 29 '19 at 12:16
  • Concerning your scope issues, just set both the scheduler and the task as fields – Akami Oct 29 '19 at 12:17
  • You should be using a Swing Timer to schedule the event since the code will run on the Event Dispatch Thread (EDT) and Swing components need to updated on the EDT. If your don't use the Swing Timer then you need to wrap your logic in a SwingUtilities.invokeLater(...) to place the code on the EDT. – camickr Oct 29 '19 at 14:44