0

I have taken a task upon myself to learn Java. My idea was to create a simple game with only the text console. The "AI" (timer) will periodically send a string and the player has to write a correct string in response, otherwise s/he loses a life.

My first question therefore is: Is there a simple way to combine timer and scanner? I need it to constantly "watch" the console line for strings.

After some time of searching and tries where I mostly struggled to scan the text while generating or generate strings while scanning I found following code but it has an issue at:

if ((name =in.nextLine(2000)) ==null)

If I rewrite the condition to, for example, compare to !="a" instead of null, the code just ignores the condition and always writes "Too slow!" no matter what. If it is =="a" it always says Hello, a. I completely don't understand why, it seems to ignore the logic. So the second question would have been, why does it ignore the logic when it is different? And how do I fix it?

public class TimedScanner
{
    public TimedScanner(InputStream input)
    {
        in = new Scanner(input);
    }

    private Scanner in;
    private ExecutorService ex = Executors.newSingleThreadExecutor(new ThreadFactory()
    {
        @Override
        public Thread newThread(Runnable r)
        {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    });

    public static void main(String[] args) {
        TimedScanner in = new TimedScanner(System.in);
        int playerHealth = 5;
        System.out.print("Enter your name: ");
        try {
            while (playerHealth > 0) {
                String name = null;
                if ((name = in.nextLine(3000)) ==null) {
                    System.out.println(name);
                    System.out.println("Too slow!");
                    playerHealth--;
                } else {
                    System.out.println(name);
                    System.out.println("Hello, " + name);
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            //TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String nextLine(int timeout) throws InterruptedException, ExecutionException
    {
        Future<String> result = ex.submit(new Worker());
        try
        {
            return result.get(timeout, TimeUnit.MILLISECONDS);
        }
        catch (TimeoutException e)
        {
            return null;
        }
    }

    private class Worker implements Callable<String>
    {
        @Override
        public String call() throws Exception
        {
            return in.nextLine();
        }
    }
}

This is very barebones idea of what it should do. In the while I plan to put in a randomly picked string, that will be compared with the console input and wrong input = playerHealth--; correct input something else.

Pavel L
  • 27
  • 6

1 Answers1

1

2) why does it ignore the logic when it is different? And how do I fix it?

You've stated:

If I rewrite the condition to, for example, compare to !="a" instead of null, the code just ignores the condition and always writes "Too slow!" no matter what.

In Java, NEVER (or almost never) compare two strings using == or !=. A String is an Object so comparing them using == means comparing them by address and not by value. So

if ((name = in.nextLine(3000)) != "a")

will always (or almost always) return true because any string returned from in#nextLine, be it "a" or something different, will be allocated on the heap at a different address than your hardcoded "a" string. The reason I'm saying "almost" is because Java uses a concept of String Pool: when creating a new reference to a literal it checks whether a string is already present in the pool in order to reuse it. But you should never rely on ==. Instead, use Object.Equals().

More discusion about Java String Pool here.

1) Is there a simple way to combine timer and scanner?

Well, console UI it's not really friendly with multi-threading when it comes to reading user input, but it can be done...

Your code has an issue: whenever the player loses a life, it has to press Enter twice - when it loses 2 life consecutively, it has to press Enter 3 times in order to receive a positive feedback from "AI". This is because you're not killing the preceding thread / cancelling the preceding task. So I suggest the following code:

private static Scanner in;

public String nextLine(int timeout) throws InterruptedException, ExecutionException
{
    //keep a reference to the current worker
    Worker worker = new Worker();
    Future<String> result = ex.submit(worker);
    try
    {
        return result.get(timeout, TimeUnit.MILLISECONDS);
    }
    catch (TimeoutException e)
    {
        //ask the worker thread to stop
        worker.interrupt();
        return null;
    }
}

private class Worker implements Callable<String>
{
    //you want the most up-to-date value of the flag, so 'volatile', though it's not really necessary
    private volatile boolean interrupt;

    @Override
    public String call() throws Exception
    {
        //check whether there's something in the buffer;
        while (System.in.available() == 0){
            Thread.sleep(20);
            //check for the interrupt flag
            if(interrupt){
                throw new InterruptedException();
            }
        }
        //once this method is called there's no friendly way back - that's why we checked for nr of available bytes previously
        return in.nextLine();
    }

    public void interrupt(){
        this.interrupt = true;
    }
}
Norberth Csorba
  • 312
  • 1
  • 9
  • Oh man, this is embarassing because I even used Objective.equals before and I completely forgot about it. Jesus. I will try the coder at morning, thanks a lot for the answare to the second asnware thus far tho. Just embarassing. – Pavel L Apr 19 '19 at 22:53
  • to the second question* sorry it is very late here. – Pavel L Apr 19 '19 at 23:00
  • Alright, back. Thanks a lot, the code works perfectly, sadly I dont exactly understand what it does for each line, although I will try to figure it out as best as possible. – Pavel L Apr 20 '19 at 08:43
  • you're welcome! If you have further questions you can ask me anytime – Norberth Csorba Apr 21 '19 at 09:18