1

This method repeatedly reads commands and execute them until the game is over. The finished variable, when true, means that the player/user has hit Quit and wants to end the game - thus exiting the loop and executing till the end of the method.

import org.apache.commons.lang3.time.StopWatch;

private Parser parser;
private Command command;
private StopWatch stopWatch;

public void play() 
{   

    stopWatch = new StopWatch();
    stopWatch.start();
    long timeLimit = 5000;

    boolean finished = false;

    printWelcome();

    while (finished == false) 
    {   
       System.out.println(stopWatch.getTime() + " milliseconds");
       if (stopWatch.getTime() >= timeLimit)
       {
          System.out.println("Time limit of " + timeLimit + " milli seconds has been reached. Good bye!");
          return;
       }
       else
       {
          command = parser.getCommand();
          finished = processCommand(command);
       }
    }     
    System.out.println("Thank you for playing.  Good bye.");       
}

But I'm observing a strange behaviour with the loop. It loops perfectly fine (displaying the continuous count of stopWatch.getTime() when the following lines are omitted :

  else
  {
     command = parser.getCommand();
     finished = processCommand(command); 
  }

But when I put them back in, it stops displaying the continuous incrementing time of the stopWatch as it increases towards the timeLimit (at which point, it should stop). Even if the player hasn't entered any command or input.

The stopWatch is clearly running in the background, but it doesn't display the count.

Please advise. Thanks.

MAK
  • 125
  • 2
  • 13
  • What does the function processCommand(command); do? It probably returns true :) – Norbert Jun 10 '15 at 19:16
  • The `else` statement block may be waiting for the user's input or it may return `true`. – But I'm Not A Wrapper Class Jun 10 '15 at 19:16
  • What does `Parser#getCommand` do? What about `processCommand`? Can you post the source? If you are waiting for input from the user, the program is going to wait until you enter smething. – Vivin Paliath Jun 10 '15 at 19:16
  • what is parser.getCommand() doing? Is it blocking? if the program is waiting for user input at that point then it will stop looping. – Seth Jun 10 '15 at 19:22

3 Answers3

3

Even if the player hasn't entered any command or input.

If Parser#getCommand is a method that is waiting for user input, execution will block until the user enters input. This means the while loop won't run and you won't see it printing out the new time.

Doing what you ask would make a very jarring experience for the user anyway, since it would mean that it would keep printing while they are typing in the command, and that's not very user-friendly.

You can use a producer-consumer approach for this. I have adapted the code from the linked question:

// Shared queue
final Queue<String> commands = new ConcurrentLinkedQueue<>();

// Non-blocking consumer
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        // non-blocking
        if((String command = commands.poll()) != null) {
            processCommand(command);
        }
    }
}, 0, 10, TimeUnit.MILLISECONDS);

// Blocking producer (this is basically the logic from Parser#getCommand)
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
    commands.add(sc.next());
}

However, this solution doesn't really let you implement the timeout. So you would have to create a new class that "wraps" the Runnable so that you can cancel it after a timeout (similar to this approach):

public class TimeOutableRunnable implements Runnable {

    private final Queue<String> commands;
    private final Runnable wrapped;
    private volatile ScheduledFuture<?> self;

    public TimeOutableRunnable(Runnable wrapped, Queue<String> commands) {         
        this.commands = commands;
        this.wrapped = wrapped;         
    }

    @Override
    public void run() {
        if(commands.isEmpty()) {
            self.cancel(false);
        } else {
            wrapped.run();
        }
    }

    public void runWithTimeout(ScheduledExecutorService executor, long timeout, TimeUnit unit) {
        self = executor.scheduleAtFixedRate(this, 0, timeout, unit);
    }
}

Then you could do:

final Queue<String> commands = new ConcurrentLinkedQueue<>();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
new TimeOutableRunnable(new Runnable() {
    @Override
    public void run() {
        // non-blocking
        while((String command = commands.poll()) != null) {
            processCommand(command);
        }
    }
}, commands).runWithTimeout(executor, timeLimit, TimeUnit.MILLISECONDS);

I haven't tested this out and I wouldn't rate myself very high on concurrency so let me know if there is something fundamentally wrong with this approach.

Community
  • 1
  • 1
Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
2

I presume your parser.getCommand(); is blocking until there is input. As this while loop runs in one thread, the program is stopped at this point.

The easiest way to check if I'm right is to enter any command, and you should get some output from this line:

       System.out.println(stopWatch.getTime() + " milliseconds");

You either need to implement a timeout in this method, or have a second thread counting time and interrupting the wait.

Fox
  • 2,348
  • 19
  • 19
  • Yes, you are both right. I didn't realise that the line `parser.getCommand();` would stop the program altogether. And yes, entering any command does result in execution of the following line: `System.out.println(stopWatch.getTime() + " milliseconds");` – MAK Jun 10 '15 at 19:24
0

Perhaps I'm misunderstanding your question, but it sounds like your problem that it isn't printing continuously? If so, its because your parer is waiting for user input. Threading could fix this problem, run your timer and print in a seperate thread

JShell
  • 624
  • 2
  • 7
  • 22