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 thecurrentBid
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.