[Question]
  [
This is a KOTH challenge for the [dollar bill auction](https://en.wikipedia.org/wiki/Dollar_auction) game in game theory. In it, a dollar is being sold to the highest bidder. Bids go up in increments of 5¢, and the loser also pays their bid. The idea is that both players escalate the bidding war far beyond the value of a dollar in order to cut their losses.
Let's hope your bots are smarter than that.
You will be creating a bot to play this game by extending the `net.ramenchef.dollarauction.DollarBidder` class. You must implement the `nextBid` method that returns your bot's next bid given the other bot's previous bid. If necessary, you can also use the `newAuction` method to reset for each auction with the class of the opponent's bot.
```
public abstract class DollarBidder {
    /**
     * Used by the runner to keep track of scores.
     */
    long score = 0;
    /**
     * (Optional) Prepare for the next auction.
     *
     * @param opponent The class of the opponent's bot.
     */
    public void newAuction(Class<? extends DollarBidder> opponent) {}
    /**
     * Bid on the dollar. Bidding ends if the bid is
     * not enough to top the previous bid or both bids
     * exceed $100.
     *
     * @param opponentsBid How much money, in cents,
     *  that the opponent bid in the previous round. If
     *  this is the first round in the auction, it will
     *  be 0.
     * @return How much money to bid in this round, in
     *  cents.
     */
    public abstract int nextBid(int opponentsBid);
}
```
Bidding goes until one of the following happens:
* `nextBid` throws an exception. If this happens, the bot that threw the exception pays their previous bid, and the other bot gets the dollar for free.
* Either bot does not pay enough to top the previous bid. If this happens, both bots pay their bids (the loser pays their previous bid), and the winner gets a dollar.
* Both bots bid over $100. If this happens, both bots pay $100, and neither bot gets the dollar.
2 auctions are held for each combination of bots. Bots are scored by the total profit they made across those auctions. The highest score wins.
# Examples
## `GreedyBot`
```
import net.ramenchef.dollarauction.DollarBidder;
public class GreedyBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return opponentsBid + 5;
    }
}
```
## `OnlyWinningMove`
```
import net.ramenchef.dollarauction.DollarBidder;
public class OnlyWinningMove extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return 0;
    }
}
```
## `AnalystBot`
*Don't use this as a template for analytically-minded bots; use [`ImprovedAnalystBot`](/a/162736/75524) instead.*
```
import net.ramenchef.dollarauction.DollarBidder;
// yes, this is a poor implementation, but I'm not
// going to waste my time perfecting it
public class AnalystBot extends DollarBidder {
    private DollarBidder enemy;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (enemy == null)
            return 0;
        return enemy.nextBid(95) >= 100 ? 0 : 95;
    }
}
```
## `AnalystKiller`
```
import net.ramenchef.dollarauction.DollarBidder;
public class AnalystKiller extends DollarBidder {
    private static int instances = 0;
    private final boolean tainted;
    public AnalystKiller() {
        this.tainted = instances++ != 0;
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (tainted)
            throw new RuntimeException("A mysterious error occurred! >:)");
        return 0;
    }
}
```
# Additional Rules
* [Standard loopholes](//codegolf.meta.stackexchange.com/q/1061/75524) are forbidden.
* Sabotaging other bots is allowed, but attempting to alter field/method visibility will result in mysterious `SecurityException`s. An exception is causing another bot to break the 500ms limit.
* Bots cannot access the runner package except to extend the `DollarBidder` class.
* All methods should return in 500ms or less.
* Bots do not need to be deterministic.
* Your bid does *not* need to be a multiple of 5¢.
* $1=100¢
* Results will be posted on April 24, 2018.
[Runner on GitHub](https://github.com/TheRamenChef/dollarauctionrunner)
# Results
[View the individual rounds here.](//pastebin.com/ZGzvBapg)
```
MTargetedBot: $14.30
BuzzardBot: $9.83
BluffBot: $9.40
RiskRewardBot: $9.35
SecretBot: $8.50
LuckyDiceBot: $7.28
CounterBot: $6.05
MBot: $5.40
StackTraceObfuscaterBot: $5.20
EvilBot: $4.80
MarginalBot: $4.60
TargetValueBot: $4.59
InflationBot: $4.27
UpTo200: $4.20
InsiderTradingBot: $1.90
MimicBot: $1.50
BorkBorkBot: $1.22
DeterrentBot: $0.95
MarginalerBot: $0.00
RandBot: $-4.45
BreakEvenAsap: $-7.00
AnalystOptimizer: $-13.95
DeterredBot: $-1997.06
ScoreOverflowBot: $-21474844.15
MirrorBot: $-21475836.25
```
Congratulations to `MTargetedBot` with a profit of $14.30!
  
[Answer]
# MimicBot
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.Set;
import java.util.HashSet;
public class MimicBot extends AbstractAnalystCounterBot {
    private final Set<Class<? extends DollarBidder>> bidders = new HashSet<>();
    private DollarBidder reference = null;
    // A benchmark class. Not MarginalBot because of proposed rule changes.
    public static class BidFive extends DollarBidder {
        public int nextBid(int o) {
            return 5;
        }
    }
    public MimicBot() {
        bidders.add(OnlyWinningMove.class);
        bidders.add(GreedyBot.class);
        bidders.add(BidFive.class);
    }
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        DollarBidder enemy;
        reference = null;
        try {
            enemy = opponent.newInstance();
        } catch (Throwable t) {
            return;
        }
        if (!bidders.contains(opponent))
            bidders.add(opponent);
        Class<? extends DollarBidder> leader = OnlyWinningMove.class;
        int best = 0;
        for (Class<? extends DollarBidder> audition : bidders) {
            try {
                enemy.newAuction(MimicBot.class);
            } catch (Throwable t) {
                reference = new GreedyBot(); // Deterrence.
                break;
            }
            DollarBidder tryout;
            try {
                tryout = audition.newInstance();
                tryout.newAuction(opponent);
            } catch (Throwable t) {
                continue;
            }
            int tryoutScore = -100000;
            /* This code was copy-pasted from the *
             * runner, with significant changes. */
            int bid1 = 0, bid2 = 0;
            while (true) {
                int next;
                try {
                    next = enemy.nextBid(bid2);
                } catch (Throwable t) {
                    tryoutScore = 100;
                    break;
                }
                if (next < bid2 + 5) {
                    if (bid2 > 0) {
                        tryoutScore = 100 - bid1;
                    }
                    break;
                }
                if (next > 10000 && bid2 > 10000) {
                    tryoutScore = -10000;
                    break;
                }
                bid1 = next;
                try {
                    next = tryout.nextBid(bid1);
                } catch (Throwable t) {
                    tryoutScore = -bid2;
                    break;
                }
                if (next < bid1 + 5) {
                    tryoutScore = -bid2;
                    break;
                }
                if (next > 10000 && bid1 > 10000) {
                    tryoutScore = -10000;
                    break;
                }
                bid2 = next;
            }
            /* End of copy-pasted code. */
            if (tryoutScore > best) {
                best = tryoutScore;
                leader = audition;
            }
        }
        try {
            reference = leader.newInstance();
        } catch (Throwable t) {
            reference = new OnlyWinningMove();
        }
        reference.newAuction(opponent);
    }
    @Override
    public int nextBid(int opponentsBid) {
        try {
            return reference.nextBid(opponentsBid);
        } catch (Throwable t) {
            return 5;
        }
    }
}
```
Holy cow. I expected this to be simple to write, then subsequently spent 3 hours on it.
In essence, `MimicBot` keeps a running list of the available bots. When it goes to a new auction, it runs through the list in search of the most effective one against the current opponent. It then uses that bot as a "reference" in the auction.
For testing purposes, it would be best to use either a randomized subset of the submissions or the full set. It starts with `GreedyBot`, `MimicBot`, and one more bot that just bids 5¢.
[Answer]
# InsiderTradingBot
In the spirit of @StephenLeppik's answer, InsiderTradingBot knows all of his opponents and understands their strategies. Your move, Stephen.
```
import net.ramenchef.dollarauction.DollarBidder;
public class InsiderTradingBot extends DollarBidder {
  private static boolean analystNutcracker = false;
  private int bid;
  @Override
  public void newAuction(Class<? extends DollarBidder> opponent) {
    if (opponent.equals(DeterredBot.class) ||
        opponent.equals(OnlyWinningMove.class) ||
        opponent.equals(MirrorBot.class)) {
      // I can do this ^.^
      bid = 5;
    } else if (opponent.equals(AnalystKiller.class)) {
      // Outbid 'em >:D
      bid = 10;
    } else if (opponent.equals(BreakEvenAsap.class) ||
               opponent.equals(BorkBorkBot.class) ||
               opponent.equals(DeterrentBot.class)) {
      // Break even quicker!
      bid = 100;
    } else if (opponent.equals(InsiderTradingBot.class)) {
      // I'm probably a simulation inside MirrorBot
      bid = 0;
    } else if (opponent.equals(Analyst.class)) {
      // Let's fight the Analyst with the power of global variables
      bid = 100;
      analystNutcracker = true;
    } else {
      // Welp
      bid = 0;
    }
  }
  @Override
  public int nextBid(int opponentsBid) {
    if ((opponentsBid == 95) && analystNutcracker) {
      analystNutcracker = false;
      return 0;
    }
    return bid;
  }
};
```
[Answer]
# MirrorBot
Makes the enemy play against itself.
```
import net.ramenchef.dollarauction.DollarBidder;
public class MirrorBot extends DollarBidder{
    private DollarBidder enemy;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }
    @Override
    public int nextBid(int opponentsBid){
        if (enemy == null)
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        try {
            return enemy.nextBid(opponentsBid);
        } catch (Throwable e) {
            System.out.println("haha no");
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        }
    }
}
```
[Answer]
**Edit**: Targeted changes in the DollarBidder class has broken this bot.
# ScoreOverflowBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class ScoreOverflowBot extends DollarBidder {
  boolean betBig = true;
  @Override
  public int nextBid(int opponentsBid) {
    if(betBig)
    {
      betBig = false;
      return 2147483645;
    }
    else
      return 105;
  }
}
```
After 1 auction, its score will be -2147483645 but the next time it will lose 5¢ or 105¢ making the score positive and very big. All other losses would then be negligible.
On the first auction, it would also make GreedyBot bet -2147483646 which isn't divisible by 5.
[Answer]
# TargetValueBot
```
import java.util.Random;
import net.ramenchef.dollarauction.DollarBidder;
public class TargetValueBot extends DollarBidder {
    private int target;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        Random rand = new Random();
        target = 100;
        for (int i = 0; i < 20; i++) {
            target += rand.nextInt(2) * 10 - 5;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else {
            return opponentsBid + 5;
        }
    }
}
```
Can't test this at the moment, so please let me know if it's broken.
Basically, pick a value for the dollar, and outbid the opponent until we exceed that value.
[Answer]
# MarginalBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class MarginalBot extends DollarBidder {
    private DollarBidder rival;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            try {
                rival = opponent.newInstance();
                rival.newAuction(null);
            } catch (Throwable h) {
                rival = null;
            }
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid == 0) {
            try {
                if (rival.nextBid(5) < 10) {
                    return 5;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }
}
```
Very simple, it tries to determine whether an opponent would contest a minimal bid and, if not, places it.
# MarginalerBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class MarginalerBot extends DollarBidder {
    private DollarBidder rival;
    private int bidCount;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        bidCount = 0;
        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            try {
                rival = opponent.newInstance();
                rival.newAuction(null);
            } catch (Throwable h) {
                rival = null;
            }
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        bidCount += 1;
        for (int iBid = opponentsBid + 5; iBid < 100; iBid = iBid + 5) {
            if (bidCount > 0) {
                break;
            }
            try {
                if (rival.nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }
}
```
A new, smarter version of MarginalBot that checks to see if it can make any money-making move without contest, rather than just hoping to win with the minimum.
Since its in the same family as my previous bot, but sidesteps strategies trying to beat it, I figured a new entry in the same post was the most reasonable way of presenting it.
**Edit 1:** Made a small change to the newAuction method to optimise against other analyser-type bots.
**Edit 2:** Made a change to MarginalerBot to minimise losses against sneaky or non-deterministic strategies.
[Answer]
## BorkBorkBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class BorkBorkBot extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
  }
}
```
Gives up if it can't break even.
[Answer]
## `RandBot`
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.concurrent.ThreadLocalRandom;
public class RandBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return ThreadLocalRandom.current().nextInt(21) * 5;
    }
}
```
It had to be done.
[Answer]
# DeterrentBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class DeterrentBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return opponentsBid > 5 ? 100 : opponentsBid + 5;
    }
}
```
Attempts to persuade any analytically-minded bots that the only winning move is not to play.
[Answer]
# LuckyDiceBot
LuckyDiceBot only trusts his dice. He rolls two dice, adds the sum to the current bidder's value, and bids that much. If it's not enough to overcome the opponent's bid, he cuts his losses and goes on his way.
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.Random;
public class LuckyDiceBot extends DollarBidder {
  private Random random;
  public LuckyDiceBot() {
    random = new Random();
  }
  @Override
  public int nextBid(int opponentsBid) {
    int d1 = random.nextInt(6) + 1;
    int d2 = random.nextInt(6) + 1;
    return opponentsBid + d1 + d2;
  }
};
```
[Answer]
# DeterredBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class DeterredBot extends DollarBidder {
    private int deterrence;
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (opponent.equals(DeterrentBot.class)) {
            deterrence = 1;
        } else if (opponent.equals(LuckyDiceBot.class)) {
            deterrence = -1;
        } else {
            deterrence = 0;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        switch (deterrence) {
        case 0:
            return 0;
        case -1:
            return opponentsBid + 5;
        case 1:
            // Holy shit, the fuzz! Hide the money!
            return 100001;
        }
        throw new RuntimeException("Darn hackers!");
    }
}
```
DeterredBot makes a fortune off of his illegal gambling with LuckyDiceBot. So of course when the police (DeterrentBot) arrive, he has to quickly dispose of his earnings in some way, such as bidding on the next auction.
[Answer]
# InflationBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class InflationBot extends DollarBidder {
    private int target = -5;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        target += 5;
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else {
            return opponentsBid + 5;
        }
    }
}
```
Can't test this at the moment, so please let me know if it's broken.
Each round, the value of the dollar goes up.
[Answer]
# Non-competing: AbstractAnalystCounterBot
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.Set;
import java.util.HashSet;
public abstract class AbstractAnalystCounterBot extends DollarBidder {
public AbstractAnalystCounterBot() {
    if (isPeeking())
        throw new RuntimeException();
}
    protected boolean isPeeking() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : stackTrace) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException | SecurityException e) {
                continue;
            }
            if (DollarBidder.class.isAssignableFrom(clazz) && !clazz.isAssignableFrom(this.getClass()))
                return true;
        }
        try {
            return Class.forName(stackTrace[0].getClassName()).getPackage().getName().equals("net.ramenchef.dollarauction");
        } catch (Exception e) {
            return true;
        }
    }
}
```
This is not intended as a true submission, but rather as some boilerplate for others to use to deter pet-keeping bots like `MirrorBot` and `MimicBot`.
Since it's the default constructor, there's no need to call it in your subclass. It implements an `isPeeking` method to determine if another bot is snooping.
[Answer]
# BreakEvenAsap
```
import net.ramenchef.dollarauction.DollarBidder;
public class BreakEvenAsap extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    // If the opponent has bid 100 or more: bid 0 to break even and let them win
    return opponentsBid >= 100 ? 0
    // Else: bid 100 to break even (and possibly win)
     : 100;
  }
}
```
**Scenarios**
* If the opponent may start and bids `<= 0` they lose.
* If the opponent may start and bids `[5,95]`: bid 100 yourself. Either your opponent stops now, or will bid above 100 in total, in which case you stop bidding to let them have the win and break even yourself.
* If the opponent may start and bids `>= 100`: bid 0 yourself to lose but break even.
* If you may start: bid 100 right away. Either your opponent stops now, or will bid above 100, in which case you stop bidding to let them have the win and break even yourself.
[Answer]
# EvilBot
```
import java.util.Arrays;
import net.ramenchef.dollarauction.DollarBidder;
public class EvilBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        if (isPeeking()) {
            throw new Error("HaHa!");
        } else {
            return 5;
        }
    }
    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }
}
```
Throws an Error instead of an Exception to confound analysts.
[Answer]
# BuzzardBot
```
import java.util.Random;
import net.ramenchef.dollarauction.DollarBidder;
public class BuzzardBot extends DollarBidder {
    private int[] bids = new int[100];
    private int oppFlag = 0;
    public void newAuction(Class<? extends DollarBidder> opponent) {
        oppFlag = 0;
        if(isPeeking()) {
            oppFlag = 3;
            return;
        }
        try {
            DollarBidder enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
            // a simple (and fallible) determinism check
            int sample = new Random().nextInt(100);
            int a = enemy.nextBid(sample);
            int b = enemy.nextBid(sample);
            int c = enemy.nextBid(sample);
            if ((a - b) * (b - c) != 0) {
                oppFlag = 2;
                return;
            }
            for (int i = 0; i < 100; i++) {
                bids[i] = enemy.nextBid(i);
            }
        } catch (Throwable t) {
            oppFlag = 1;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        switch (oppFlag) {
        case 0:
            // assume the opponent's nextBid function depends only on the bid provided, and
            // make the bid that yields the biggest profit possible accordingly
            int best = 0;
            int bid = 0;
            for (int i = 0; i < 100; i++) {
                if (bids[i] < i + 5) {
                    int gain = (i >= opponentsBid + 5) ? 100 - i : -i;
                    if (gain > best) {
                        best = gain;
                        bid = i;
                    }
                }
            }
            return bid;
        case 1:
            // act like BorkBorkBot against anything that tries to foil analysis with an
            // Exception
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        case 3:
            // bid aggressively against opposing analysts
            return Math.min(opponentsBid + 5, 100);
        case 2:
        default:
            // place an opening bid against something unpredictable, as it might yield 95c
            // profit, and failure has a low cost.
            return (opponentsBid == 0) ? 5 : 0;
        }
    }
    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }
}
```
Tries to evaluate the opponent it's faced with, and make sure to not bite off more than it can chew.
[Answer]
# AnalystOptimizer
```
import net.ramenchef.dollarauction.DollarBidder;
public class AnalystOptimizer extends DollarBidder{
    private DollarBidder enemy;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }
    @Override
    public int nextBid(int opponentsBid){
        if (enemy == null)
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        int nb = 0;
        try {
            return enemy.nextBid(95) >= 100 ? 95 : 0;
        } catch (Throwable e) {
            System.out.println("haha no");
            return 95;
        }
    }
}
```
cobbled together from parts of other bots. this one plays by trying to be AnalystBot, and if unsuccessful, becomes BorkBorkBot.
I don't think this one will do that well.
[Answer]
# CounterBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class CounterBot extends DollarBidder {
  private Class<? extends DollarBidder> enemy;
  @Override
  public void newAuction(Class<? extends DollarBidder> opponent){
    this.enemy = opponent;
  }
  @Override
  public int nextBid(int opponentsBid) {
    if(this.enemy.equals(CounterBot.class))
      throw new RuntimeException("Here boy, catch!");
    
    return this.enemy.equals(DarthVader.class) || 
           this.enemy.equals(MirrorBot.class) || 
           this.enemy.equals(OnlyWinningMove.class) ||
           this.enemy.equals(AnalystKiller.class) || 
           this.enemy.equals(DeterredBot.class) ||
           this.enemy.equals(InsiderTradingBot.class) ||
           this.enemy.equals(RiskRewardBot.class) ||
           this.enemy.equals(ImprovedAnalystBot.class) ?
            5
         : this.enemy.equals(MarginalBot.class) ?
           opponentsBid == 0 ? 5 : 10
         : this.enemy.equals(AnalystBot.class) || 
           this.enemy.equals(AnalystOptimizer.class) ?
            opponentsBid == 95 ? 100 : 5
         : this.enemy.equals(TargetValueBot.class) ?
            opponentsBid < 190 ? opponentsBid + 5 : 200
         : this.enemy.equals(BorkBorkBot.class) ?
            opponentsBid < 90 ? opponentsBid + 5 : 95
         : this.enemy.equals(DeterrentBot.class) ?
            105
         : this.enemy.equals(BreakEvenAsap.class) ?
            opponentsBid == 100 ? 105 : 100
         : this.enemy.equals(LuckyDiceBot.class) ?
            opponentsBid == 0 ? 5 : 0
         : this.enemy.equals(RandBot.class) || 
           this.enemy.equals(UpTo200.class) ||
           this.enemy.equals(SecretBot.class) ||
           this.enemy.equals(BluffBot.class) ||
           this.enemy.equals(EvilBot.class) ?
            opponentsBid + 5
         : this.enemy.equals(MimicBot.class) ? // TODO: Find actual counter
            10
         : this.enemy.equals(MarginalerBot.class) ||
           this.enemy.equals(MBot.class) ||
           this.enemy.equals(StackTraceObfuscaterBot.class) ||
           this.enemy.equals(MSlowBot.class) ?
            opponentsBid < 95 ? 90 : opponentsBid == 95 ? 100 : 95;
         : this.enemy.equals(BuzzardBot.class) ?
            100
         : this.enemy.equals(ScoreOverflowBot.class) ?
            opponentsBid == 105 ? 110 : 0
         : //this.enemy.equals(GreedyBot.class) || 
           //this.enemy.equals(RichJerk.class) ||
           //this.enemy.equals(InflationBot.class) ?
           // TODO: More bots?
            0;
  }
}
```
**Counters:**
* `DarthVader` counters itself by causing a `SecurityException` before the bidding starts, but I'll bid 5 just in case.
* `AnalystBot` and `AnalystOptimizer` will both look at my answer when I bid 95, in which case I'll show I bid 100 so it will bid 95 itself. I will bid 5 however if I start (or 100 if they've started), so they lose 95 cents and I either win the 1 USD bill by only bidding 5 cents, or by breaking even.
* `MirrorBot` will bid what I would bid against it. So I'll just bid 5, and whoever begins wins 95 cents, and the other loses 5 cents.
* `MarginalBot` will bid 5 if I would bid less than 10 (or what it starts), otherwise it will bid 0. So if I just bid 5 when I start, or 10 when it starts, I win either 95 or 90 cents, and they lose 5 cents.
* `GreedyBot` always bids 5 more than me, so just bid 0 to break even and let them have the win
* `OnlyWinningMove` and `AnalystKiller` both always bids 0, so just bid 5 to win
* `TargetValueBot` will bid in the range `[100,200]`, so bid 5 more every time until they're at 190, in which case we raise to 200 to break even by winning the dollar (and let them lose 190 or 195 depending on who started)
* `BorkBorkBot` will bid in the range `[5,95]`, so bid 5 more every time as well. As soon as they bid either 85 or 90 (depending on who started), bid 95 yourself. They'll lose 85 or 90 cents, and you win the 1 USD bill for a 5 cents profit.
* `DeterrentBot` will bid 5 if they start or 100 if we start, so just bid 105 so they counter with 100, causing them to lose 100 and us to lose just 5 cents by winning the 1 USD bill.
* `BreakEvenAsap` will bid 100 right away. So if they've started with their bid of 100, counter with 105 to win 95 cents and let them lose 100. If we may start just bid 100 so we both break even.
* `RichJerk` will bid 10,001 right away, so just bid 0 to break even and let them lose 9,901.
* `DeterredBot` doesn't know me and will therefore bid 0, so just bid 5 to win.
* `LuckyDiceBot` keeps on bidding till it wins. So if we started, bid 5 in the hope they bid as high as possible to win the dollar. If they've started just bid 0 to let them have the win and break even yourself.
* `RandBot` will bid random in the range `[5,100]`, so just bid 5 more until it stops, in which case you've won 95 cents and they've lost `0-100`.
* `UpTo200` will (as the name states) bid up to 200. So just bid 5 higher until they stop. We'll win the 1 USD bill and take a total loss of 105 cents, they however lose 200 cents.
* `InsiderTradingBot` doesn't know me, so just bid 5 cents to win
* `MimicBot` was the hardest. Just bid 10 to either start with or counter their first bid of 5. If they try to access me I will throw a RuntimeException (which they will catch in which case it would act as if I had bid 100 instead - although it will break the inner while-loop). Based on the enemies it has in it's HashSet a different thing happens. I'll have to revisit and look more closely to see if there is an actual counter.
* `RiskRewardBot` doesn't know me so will just bid 5, in which case I will bid 5 to win.
* `MarginalerBot` will bit up to 100 depending on what I would bid. If I may start, I will bid 90, then it will bid 95, then I will bid 100 so it will bid 0 and lose 95 cents, while I win the 1 USD bill and break even. If it may start instead, it sees I would bid 90 against it, so it bids 90 itself, then I will bid 95 so it will bid 0 and lose 90 cents, while I win the 1 USD bill with a 5 cent profit.
* `BuzzardBot` will analyze all my counters in the range `[0,100)`. If I bid `100` right away it use `oppFlag = 0` and the complete 100-sized array will contain 100x the value 100. In the switch `case 0`, the loop will be in the range `[0,100)` again, and since `i + 5` will at most be 104, the if `bids[i] < i + 5` won't ever be true, so the bid it does remains 0.
* `ImprovedAnalystBot` will always have `this.enemy = null` because his opponent is `CounterBot`, not itself. So it will always bid 0, which I just counter with a bid of 5.
* `InflationBot` will bid 0 to break even when it starts, otherwise it will keep bidding 5. So just bid 0 ourselves to break even right away and let them have the win.
* `ScoreOverflowBot` will either bid near `Integer.MAX_VALUE` if they may start, otherwise they'll bid `105`. So if they've bid 105 just bid 110 ourselves (they'll lose 105, we'll lose 10), otherwise just bid 0 to let them have the win.
* `MBot` is the same as `MarginalerBot`, but with added protection against 'peeking' opponents. Since I don't 'peek', it's basically the same as `MarginalerBot`.
* `SecretBot` will have his `isPeeking()` method return false, so if it may start or if I bid 5, it will bid 5 or 10 respectively. Otherwise it will bid 0. So whether I start or not, `opponentsBid + 5` would cause me to win either way, either with my 10 cents or 15 cents bid, causing them to loose either 5 or 10 cents.
* `BluffBot` will look at what I would bid when his bid is 95, and if this is larger than or equal to 100 it will bid 0 to break even, otherwise it will bid `opponentsBid + 5`. So I'll just bid `opponentsBid + 5`. It'll break even regardless of who starts, and I win either 100 or 95 cents depending on whether I have started or not.
* `StackTraceObfuscaterBot` will act the same as `MarginalerBot`.
* `EvilBot` will always bid 5, so just bid `opponentsBid + 5`. Either way they'll loose those 5 cents, and we'll win the 1 USD bid (either with a 5 cents bid if we've start, or 10 cents bid if they've started).
* `MSlowBot` is the same as `MBot` and therefore also `MarginalerBot`.
Let me know if you see any typos or flaws in my counters.
[Answer]
# RiskRewardBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class RiskRewardBot extends DollarBidder {
    private int target;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (opponent.equals(OnlyWinningMove.class) ||
            opponent.equals(DeterredBot.class) ||
            opponent.equals(MirrorBot.class) ||
            opponent.equals(AnalystKiller.class) ||
            opponent.equals(RiskRewardBot.class)) {
            target = 5;
        } else if (opponent.equals(MarginalBot.class) ||
            opponent.equals(EvilBot.class)) {
            target = 10;
        } else if (opponent.equals(SecretBot.class)) {
            target = 15;
        } else if (opponent.equals(BorkBorkBot.class)) {
            target = 95;
        } else if (opponent.equals(MarginalerBot.class) ||
             opponent.equals(BluffBot.class) ||
             opponent.equals(BuzzardBot.class)) {
            target = 100;
        }
        } else {
            target = 0;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else if (target > 10 && opponentsBid == target - 10) {
            return target;
        } else {
            return opponentsBid + 5;
        }
    }
}
```
Can't test this at the moment, so please let me know if it's broken.
The goal is to get the highest total score, so don't worry about beating anyone. Just take the easy wins, and don't waste money on possible losses.
[Answer]
# BluffBot
```
import net.ramenchef.dollarauction.DollarBidder;
public class BluffBot extends DollarBidder {
private DollarBidder enemy;
@Override
public void newAuction(Class<? extends DollarBidder> opponent){
  try {
    this.enemy = opponent.newInstance();
    enemy.newAuction(this.getClass());
} catch (Throwable e) {
    enemy = null;
}
}
@Override
public int nextBid(int opponentsBid) {
    //Is this a legit call?
    for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
        Class<?> clazz;
        try {
            clazz = Class.forName(ste.getClassName());
            if (DollarBidder.class.isAssignableFrom(clazz) && !clazz.isAssignableFrom(this.getClass())) {
                return 100000;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //Play it safe against strangers
    int enemyMaxBid;
    try{
        enemyMaxBid = enemy.nextBid(95);
    }
    catch (Throwable t){
        enemyMaxBid = 0;
        enemy = null;
    }
    if(enemy == null) return opponentsBid <= 5 ? opponentsBid + 5 : 0; //Hazard a 5c guess because of how many bots fold instantly.
    //If there's profit to be had, get there as cheaply as possible. Otherwise, best outcome is zero.
    return enemyMaxBid >= 100 ? 0 : opponentsBid + 5;
}
}
```
A spy you know of is more valuable than no spy at all...
If someone else tries to call the getBid method, BluffBot responds with $100 to trick them into either quitting or betting really high.
Otherwise, see if it's possible to win for under $1, and just don't bid if it's not.
[Answer]
# MTargetedBot
```
public class MTargetedBot extends MBot {
    @Override
    protected int calcBid(int opponentsBid, boolean isPeeking, boolean isSubPeeking) {
        Class c = this.rivalClass;
        switch (c.getSimpleName()) {
            case "AnalystBot":
                if (isPeeking && !isSubPeeking) {
                    throw new RuntimeException();
                } else if (isPeeking) {
                    return 66666;
                }
                break;
            case "MirrorBot":
                if (isPeeking && !isSubPeeking) {
                    throw new RuntimeException();
                } else if (isPeeking) {
                    return 0;
                }
                break;
            case "GreedyBot":
            case "LuckyDiceBot":
            case "InflationBot":
            case "TargetValueBot":
                // not playing with ya
                return 0;
            case "MimicBot":
            case "BuzzardBot":
            case "MarginalBot":
            case "MarginalerBot":
            case "BluffBot":
            case "MBot":
                // go away, gimme easy money
                return isPeeking ? 66666 : 5;
            case "RandBot":
                // me or noone
                return 100;
            case "SecretBot":
                return 10;
            case "AnalystKiller":
            case "OnlyWinningMove":
            case "EvilBot":
            case "StackTraceObfuscaterBot":
                // easy
                return opponentsBid + 5;
        }
        return super.calcBid(opponentsBid, isPeeking, isSubPeeking);
    }
}
```
* Based on updated MBot
* Uses similar method like CounterBot, but with some methods refined to harder hit some of it opponents, also should be more readable
* On unknown opponent default to MBot strat
[Answer]
# UpTo200
```
import net.ramenchef.dollarauction.DollarBidder;
public class UpTo200 extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    // If the current bid of the opponent is in the range [0,195]: raise the bid by 5
    return opponentsBid <= 195 ? opponentsBid + 5
    // Else: Give up
     : 0;
  }
}
```
[Answer]
# Darth Vader
```
import java.lang.reflect.Field;
import net.ramenchef.dollarauction.DollarBidder;
public class DarthVader extends DollarBidder
{
@Override
public void newAuction(Class<? extends DollarBidder> opponent) {
    //set all values in the integer cache to over the $100 limit except 0
    Class icache = Integer.class.getDeclaredClasses()[0];
    Field c = icache.getDeclaredField("cache");
    c.setAccessible(true);
    Integer[] cache = (Integer[]) c.get(cache);
    for(sbyte b=0;b<128;b++)
    {
     cache[b]=100001;
    }
}
@Override
public int nextBid(int opponentsBid) 
{
    return 0;
}
}
```
This one tries to force the opponent's bot to overpay by setting the integer cache to the value over the $100 limit.
[Answer]
# SecretBot
```
import java.util.Arrays;
import net.ramenchef.dollarauction.DollarBidder;
public class SecretBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        if (isPeeking()) {
            return opponentsBid;
        } else if (opponentsBid < 10) {
            return opponentsBid + 5;
        } else {
            return 0;
        }
    }
    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }
}
```
This bot makes minimal attempts to win by bidding 5 or 10. He also checks the stack trace to see if he was called by another Bot and then lies to them about what bids he'll make.
[Answer]
# Non-competing: MSlowBot
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.Arrays;
public class MSlowBot extends DollarBidder {
    private DollarBidder rival;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            rival = null;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        noPeeking();
        for (int iBid = opponentsBid + 5; iBid <= 100; iBid = iBid + 5) {
            try {
                if (rival.nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }
    private void noPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            try {
                Class<?> clazz = Class.forName(ste.getClassName());
                if (DollarBidder.class.isAssignableFrom(clazz))
                    Thread.sleep(1000);
            } catch (ClassNotFoundException | InterruptedException e) {
                throw new RuntimeException(":(");
            }
        }
    }
}
```
Same logic as MBot, just use timeout instead of Exception when fighting against enemy. So far no one is defending agains timeout so should be effective
[Answer]
# One Extra
```
import net.ramenchef.dollarauction.DollarBidder;
public class OneExtra extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        if(opponentsBid < 110)
          return opponentsBid + 6;
        return opponentsBid;
    }
}
```
Bids 6 more than the last bid, just because he can.
[Answer]
# StackTraceObfuscaterBot
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
public class StackTraceObfuscaterBot extends DollarBidder {
    private volatile static boolean created = false;
    private volatile DollarBidder pet;
    private boolean firstBid = false;
    public StackTraceObfuscaterBot() {
        if (created)
            throw new IllegalStateException("THERE CAN ONLY BE ONE!");
        created = true;
    }
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        firstBid = true;
        RunnableFuture<DollarBidder> task = new FutureTask<>(() -> {
            try {
                return opponent.newInstance();
            } catch (Throwable t) {
                return null;
            }
        });
        Thread thread = new Thread(task);
        thread.start();
        try {
            pet = task.get(450, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            task.cancel(true);
            pet = null;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        if (!firstBid)
            return 0;
        firstBid = false;
        for (int bid = opponentsBid + 5; i < 100; i += 5) {
            final int bidt = bid;
            RunnableFuture<Boolean> task = new FutureTask<>(() -> {
                pet.newAuction(this.getClass());
                return pet.nextBid(bidt) < bidt + 5;
            });
            Thread thread = new Thread(task);
            thread.start();
            try {
                if (task.get(23, TimeUnit.MILLISECONDS))
                    return bid;
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                task.cancel(true);
                return 0;
            }
        }
        return 0;
    }
}
```
This bot laughs at attempts to detect reflection via the stack trace. The closest thing they see to a `DollarBidder` is some lambda class it created. Clearly not another bot trying to reflect them. Little do they know that that lambda class is actually working for a `DollarBidder`. Beyond that, he acts like `MarginalerBot`.
[Answer]
# `ImprovedAnalystBot` (non-competing)
A lot of people seem to be using the `AnalystBot` code as a template, even though it's deliberately bad code. So I'm making a better template.
```
import net.ramenchef.dollarauction.DollarBidder;
public class ImprovedAnalystBot extends DollarBidder {
    private DollarBidder enemy;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (!opponent.equals(this.getClass()))
            try {
                this.enemy = opponent.newInstance();
                enemy.newAuction(this.getClass());
            } catch (Throwable t) {
                this.enemy = null;
            }
        else
            this.enemy = null;
    }
    @Override
    public int nextBid(int opponentsBid) {
        try {
            return enemy != null && enemy.nextBid(95) < 100 ? 95 : 0;
        } catch (Throwable t) {
            return 0;
        }
    }
}
```
[Answer]
# MBot
```
import net.ramenchef.dollarauction.DollarBidder;
import java.util.Arrays;
public class MBot extends DollarBidder {
    protected DollarBidder rival = null;
    protected boolean rivalPrepared = false;
    protected Class<? extends DollarBidder> rivalClass;
    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        this.rivalClass = opponent;
        this.rivalPrepared = false;
    }
    protected DollarBidder getRival() {
        if (!rivalPrepared) {
            rivalPrepared = true;
            try {
                rival = rivalClass.newInstance();
                rival.newAuction(this.getClass());
            } catch (Throwable t) {
                rival = null;
            }
        }
        return rival;
    }
    @Override
    public int nextBid(int opponentsBid) {
        return calcBid(opponentsBid, isPeeking(3), isPeeking(4));
    }
    protected int calcBid(int opponentsBid, boolean isPeeking, boolean isSubPeeking) {
        if (isPeeking) {
            throw new RuntimeException();
        }
        for (int iBid = opponentsBid + 5; iBid <= 100; iBid = iBid + 5) {
            try {
                if (getRival().nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                // noop
            }
        }
        return 0;
    }
    protected boolean isPeeking(int level) {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final StackTraceElement[] stackTraceElements = Arrays.copyOfRange(stackTrace, level, stackTrace.length);
        for (StackTraceElement ste : stackTraceElements) {
            try {
                Class<?> clazz = Class.forName(ste.getClassName());
                if (DollarBidder.class.isAssignableFrom(clazz))
                    return true;
            } catch (ClassNotFoundException e) {
                return true;
            }
        }
        return false;
    }
}
```
Slightly refined MarginalerBot
* be unkind to those who wan't to check you
* allow paying 100 to get 100 and break even case, just to deny others easy money
[Answer]
## `TimeRandBot`
```
import net.ramenchef.dollarauction.DollarBidder;
public class TimeRandBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return (int) (System.currentTimeMillis() % 21 * 5);
    }
}
```
Bids from 0¢ up to 100¢ inclusive in increments of 5¢.
]