0

Environment: Windows 10.0.19044.0 - Java 13.0.1+9

I'm trying to create a small game which deals with players bartering and attending auctions for various items. This particular code section handles auctions amongst players when a new item is up for grabs. The code works, in the sense that auctions are handled as they should, according to the following rules:

  • If no players make a bid, the auction ends (the item is sold to the highestBidder, provided there is one)
  • If a player takes more than 10 seconds to make a valid bid, they are added to the banList, preventing them from bidding
  • A bid is accepted as a valid input when it can be processed as a positive integer greater than the currentBid and does not exceed the bidding player's balance
  • Only players who are not on the banList and whose balance is greater than the currentBid are allowed to bid (rule-offending players still shown as options when selecting the player to bid as, with a preceding tag as a temporary method to mark non-selectable players)

The problem I am facing here is that I can't figure out a way for my input to be displayed while typed as a regular Scanner or BufferedReader object would. I've played around with various methods, but this is the only iteration in which I could get an InterruptedException thrown from the prompt*() methods (done in my case by using BufferedReader's ready() method). This is necessary since the input needs to be interrupted in case a TimeoutException is thrown from the handleAuction() method...Otherwise, the user input object keeps receiving input and would use it for the next method call, causing an IndexOutOfBoundsException.

I've tried creating a thread wrapper class that throws an Exception, in addition to several iterations revolving around scheduling a Callable from a ScheduledExecutorService inside each prompt method that throws an InterruptedException after 10 seconds...the problem in each case is that the scope of the exception never goes out far enough to reach the catch statements of my methods...I am fine using Scanner as well, provided the input displays when being entered :)

Aucthreadz.java:

import java.util.concurrent.Future;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Objects;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.io.IOException;

public class Aucthreadz {
    
    final static int[] playerBalances = new int[]{5,10,15};
    final static String[] playerNames = new String[]{"Inez", "Jackie", "Matt"};
    final static TimeUnit wunit = TimeUnit.SECONDS; // time unit to use when waiting
    final static long secs = 10; // seconds to wait
    
    // prompts for choice among given list, displaying the list with prepended numbers, 
    public static int revisedPromptNumberedInput(ArrayList<String> list) throws TimeoutException {
        BufferedReader buff = new BufferedReader(new InputStreamReader(System.in));
        Integer[] options = new Integer[list.size()];
        Integer choice = 0;
        System.out.println("Please enter the corresponding number of the option you want (-1 to quit):");
        while (true){
            try{
                for (int i = 0; i < list.size(); ++i) {
                // If it has a newline, print so the string can be entirely on its own lines so the formatting looks nice
                if (list.get(i).contains("\n")) {
                    System.out.println(i + 1 + ". ");
                    for (String line : list.get(i).split(("\n"))) {
                        System.out.println(line);
                    }
                // otherwise, just print it on the same line
                } else {
                    System.out.println((i + 1) + ". " + list.get(i));
                }
                options[i] = i + 1;
                }
                try {
                    while(!buff.ready()){
                        Thread.sleep(200);
                        //Thread.currentThread().sleep(200);
                    }
                    choice = Integer.parseInt(buff.readLine());
                    if (Arrays.asList(options).contains(choice)){
                        if (list.get(choice - 1).length() > 5 && (list.get(choice-1)).substring(0, 6).equals("BANNED"))
                            System.out.println("player is on banlist, can't bid...\n");
                        else if (list.get(choice - 1).length() > 7 && (list.get(choice-1)).substring(0, 8).equals("TOO POOR"))
                            System.out.println("player is too poor, can't bid...\n");
                        else
                            break;
                    }
                }
                // catch poorly formatted input
                catch(NumberFormatException nfe){
                    System.out.println("NumberFormatException!!!");
                }
                System.out.println("Please enter a valid integer among the provided options:");
            }
            catch(IOException ioe){
                System.out.println("IOException inside promptBid...parameters: list: "+list.toString());
                ioe.printStackTrace();
                System.out.println();
                break;
            }
            catch(InterruptedException ie){
                System.out.println("InterruptedException inside revisedPromptNumberedInput!!!");
                break;
            }
        }
        return choice;
    }

    // prompts for bid, only accepts number greater than highest bid and lower than player's current balance
    // used as callable in Future
    public static int promptBid(int choice, int currentBalance, int highestBid) throws TimeoutException {
        //Scanner scanner = new Scanner(System.in);
        BufferedReader buff = new BufferedReader(new InputStreamReader(System.in));
        int bid = 0;
        System.out.println("\nEnter the amount to bid for: ");
        while(true){
            try{
                try{
                    while(!buff.ready()){
                        Thread.sleep(200);
                    }
                    bid = Integer.parseInt(buff.readLine());
                    System.out.println("player attempted to bid $"+bid);
                    if (bid <= highestBid) {
                        System.out.println("\nPlease enter a higher amount than the current bid ($" + highestBid + "):\n");
                    }
                    else if (bid > currentBalance) {
                        System.out.println("\nPlease enter an amount less than or equal to your current amount owned ($"+ currentBalance + ") :'[\n");
                    }
                    else {
                        System.out.println("New bid placed by " + playerNames[choice - 1] + " for $" + bid); 
                        break;
                    }
                }
                // catch poorly formatted input
                catch(NumberFormatException nfe){
                    System.out.println("NumberFormatException!!!");
                }
            }
            catch(IOException ioe){
                System.out.println("IOException inside promptBid...parameters: choice: "+choice+" currentBalance: "+currentBalance+" highestBid: "+highestBid);
                ioe.printStackTrace();
                break;
            }
            catch(InterruptedException ie){
                System.out.println("InterruptedException inside promptBid!!!");
                break;
            }
        }
        return bid;
    }

    // handler function to be called whenever an auction is required amongst players
    public static void handleAuction() {
        ArrayList<String> options = new ArrayList<String>();
        ArrayList<Integer> banList = new ArrayList<Integer>();
        boolean endAuction = false;
        int highestBid = 0;
        String highestBidder = "";
        while(!endAuction){
            options.clear();
            for (int i = 0; i < playerNames.length; i++){ 
                if (banList.contains(i)) 
                    options.add("BANNED--"+playerNames[i]);
                else if (playerBalances[i] <= highestBid)
                    options.add("TOO POOR--"+playerNames[i]);
                else
                    options.add(playerNames[i]);
            }
            ExecutorService es = Executors.newSingleThreadExecutor(r -> {final Thread t = new Thread(r); t.setDaemon(true); return t; });
            System.out.println("Enter player number or wait 10 secs to end auction altogether!");
            try {
                int choice = 0, bid = 0;
                final ArrayList<String> foptions = options;
                Future<Integer> f1 = es.submit(() -> {
                    int ret = revisedPromptNumberedInput(foptions);
                    return ret;
                });
                try{ choice = f1.get(secs, wunit); } 
                catch (TimeoutException te){
                    System.out.println( highestBid == 0 ? "\nTime's up! No takers for this item..." : "\nSOLD!!! "+highestBidder+" has won the item with a bid of $"+ highestBid );
                    endAuction = true;
                    es.shutdownNow();
                    break;
                }
                finally { f1.cancel(true); }
                // creating final versions of parameters since submitting tasks requires variables to be final
                final int fchoice = choice;
                final int fbalance = playerBalances[choice-1];
                final int fhighestBid = highestBid;
                Future<Integer> f2 = es.submit(() -> {
                    int ret = promptBid(fchoice, fbalance, fhighestBid);
                    return ret;
                });
                try { highestBid = f2.get(secs, wunit); highestBidder = options.get(choice-1);}
                catch (TimeoutException te){
                    banList.add(choice-1);
                    System.out.println(options.get(choice-1)+" got added to the banlist for timewasting!");
                    if (options.size()-1 == 0){
                        System.out.println("No players left to bid! Ending auction...");
                        if (highestBid > 0) System.out.println("\nSOLD!!! "+highestBidder+" has won the item with a bid of $"+ highestBid);
                        endAuction = true;
                    }
                }
                finally { f2.cancel(true); }
            }
            catch (InterruptedException ie){
                System.out.println("InterruptedException inside handleAuction!!!");
            }
            catch (final ExecutionException ee) {
                ee.printStackTrace();
                endAuction = true;
            }
            finally {
                System.out.println("ExecutorService state: "+es.shutdownNow().toString()+"\n");
            }
        }
    }
    
    public static void main(final String[] args) {
        handleAuction();
    }
}

P.S. The eventual plan is to have input handled by a server-socket connection, but I first want to make sure that I can get this part working offline.

0 Answers0