Lelang Tagihan Dolar


32

Ini adalah tantangan KOTH untuk permainan lelang tagihan dolar dalam teori permainan. Di dalamnya, satu dolar dijual kepada penawar tertinggi. Tawaran naik sebesar 5 ¢, dan yang kalah juga membayar tawaran mereka. Idenya adalah bahwa kedua pemain meningkatkan perang penawaran jauh di luar nilai dolar untuk memotong kerugian mereka.

Mari berharap bot Anda lebih pintar dari itu.

Anda akan membuat bot untuk memainkan game ini dengan memperluas net.ramenchef.dollarauction.DollarBidderkelas. Anda harus menerapkan nextBidmetode yang mengembalikan tawaran bot Anda berikutnya mengingat tawaran bot lain sebelumnya. Jika perlu, Anda juga dapat menggunakan newAuctionmetode ini untuk mengatur ulang untuk setiap lelang dengan kelas bot lawan.

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);
}

Penawaran berlangsung hingga salah satu dari yang berikut terjadi:

  • nextBidmelempar pengecualian. Jika ini terjadi, bot yang melempar pengecualian membayar tawaran mereka sebelumnya, dan bot lain mendapatkan dolar secara gratis.
  • Bot mana pun tidak cukup membayar untuk melampaui tawaran sebelumnya. Jika ini terjadi, kedua bot membayar tawaran mereka (yang kalah membayar tawaran sebelumnya), dan pemenang mendapat satu dolar.
  • Kedua bot menawar lebih dari $ 100. Jika ini terjadi, kedua bot membayar $ 100, dan bot tidak mendapatkan dolar.

2 lelang diadakan untuk setiap kombinasi bot. Bot dinilai dengan total laba yang mereka hasilkan di seluruh pelelangan itu. Skor tertinggi menang.

Contohnya

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

Jangan gunakan ini sebagai templat untuk bot yang berpikiran analitik; gunakan ImprovedAnalystBotsaja.

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;
    }
}

Aturan tambahan

  • Celah standar dilarang.
  • Menyabot bot lain diperbolehkan, tetapi upaya untuk mengubah visibilitas bidang / metode akan menghasilkan misterius SecurityException s yang . Pengecualian menyebabkan bot lain melanggar batas 500ms.
  • Bot tidak dapat mengakses paket pelari kecuali untuk memperpanjang DollarBidder kelas.
  • Semua metode harus dikembalikan dalam 500 ms atau kurang.
  • Bot tidak perlu bersifat deterministik.
  • Tawaran Anda tidak perlu kelipatan 5 ¢.
  • $ 1 = 100 ¢
  • Hasil akan diposting pada 24 April 2018.

Pelari di GitHub

Hasil

Lihat putaran individual di sini.

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

Selamat MTargetedBotdengan untung $ 14.30!


11
Tantangan ini pada dasarnya rentan terhadap One-Upping. Karena saya tahu kelas lawan saya, mudah untuk memilih strategi terbaik melawannya. (Lalu seseorang datang, dan dapat membuka bot saya , dll.)
Nathan Merrill

2
" Tawaran naik selisih 5 ¢ ". Anda tidak memiliki apa pun dalam kode Anda untuk memvalidasi ini, meskipun .. LuckyDiceBotmisalnya tawaran dalam penambahan 2-12secara acak ..
Kevin Cruijssen

4
Juga: bagaimana jika bot saya menyebabkan bot lain melebihi batasan 500 ms?
Nathan Merrill

4
@RamenChef Kita berbicara tentang kode berbahaya di sini. Bagaimana jika saya mendeteksi ketika bot lain memanggil saya, dan panggil Thread.sleep (1000)?
Nathan Merrill

3
Saya VTC ini karena tidak jelas sabotase apa yang diizinkan dan apa yang tidak. OP telah menolak pengiriman yang "menyerang pelari" (yang tidak jelas), dan tidak ada garis yang jelas antara kode berbahaya yang diizinkan, dan kode berbahaya yang tidak (Bagaimana Anda menentukan bot mana yang menyebabkan bot terlalu lama) ?)
Nathan Merrill

Jawaban:


2

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);
    }
}
  • Berdasarkan MBot yang diperbarui
  • Menggunakan metode yang mirip seperti CounterBot, tetapi dengan beberapa metode yang disempurnakan agar lebih sulit mengenai beberapa lawannya, juga harus lebih mudah dibaca
  • Pada default lawan yang tidak dikenal ke MBot strat

1
Ini tidak adil.
Joshua

@ Yosua Apa yang khususnya tidak adil tentang solusi ini menurut Anda?
mleko

Mengetahui nama-nama lawan Anda.
Joshua

@Joshua setengah dari solusi menggunakan informasi itu. Kami bahkan menulis kepada penulis bahwa ini harus diubah atau One-Upping akan terjadi, ia menolak untuk mengubah tantangan - jadi ini dia
mleko

1
Sudah melakukannya ....
Yosua

15

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;
        }
    }
}

Astaga. Saya berharap ini mudah ditulis, lalu menghabiskan 3 jam untuk itu.

Intinya, MimicBotsimpan daftar bot yang tersedia. Ketika ia pergi ke pelelangan baru, ia berjalan melalui daftar untuk mencari yang paling efektif melawan lawan saat ini. Kemudian menggunakan bot itu sebagai "referensi" dalam pelelangan.

Untuk tujuan pengujian, akan lebih baik menggunakan subset kiriman secara acak atau set lengkap. Dimulai dengan GreedyBot,, MimicBotdan satu bot lagi yang hanya menawar 5 ¢.


11

InsiderTradingBot

Dalam semangat jawaban @ StephenLeppik, InsiderTradingBot tahu semua lawannya dan memahami strategi mereka. Langkahmu, 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;
  }

};

1
Nah, perdagangan orang dalam adalah jika RichJerkbot membuat pengecualian spesifik untuk bot Anda dan menawar $ 0 untuk itu.
Nissa

Masih terlalu dini untuk mengoptimalkan jawaban lain. Juga AnalystBot, tidak Analyst.
RamenChef

8
Mungkin perlu ada aturan "nama kelas akan diacak".
user202729

1
@ user202729 Bagaimana kalau "tidak ada referensi langsung ke kelas"?
RamenChef

1
Saya ingin melihat ini menangani MimicBot.
Nissa

8

MirrorBot

Membuat musuh bermain melawan dirinya sendiri.

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);
        }
    }
}

6
Anda nuked secara Analystspektakuler.
Silvio Mayolo

@ SilvioMayolo Bagaimana?
dkudriavtsev

Mirror mencoba untuk meniru Analyst yang bermain melawan dirinya sendiri, menghasilkan stack overflow.
Silvio Mayolo

8

Sunting : Perubahan yang ditargetkan pada kelas DollarBidder telah merusak bot ini.

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;
  }
}

Setelah 1 lelang, nilainya akan menjadi -2147483645 tetapi waktu berikutnya ia akan kehilangan 5 ¢ atau 105 ¢ membuat skor positif dan sangat besar. Semua kerugian lainnya akan diabaikan.

Pada lelang pertama, itu juga akan membuat taruhan GreedyBot -2147483646 yang tidak habis dibagi 5.


scoredilindungi paket. Bot Anda tidak dapat mengaksesnya.
RamenChef

@RamenChef Ups, menghapus CheatingBot
Winter

Tidak ada aturan yang melarang "menyerang pelari", hanya "mengakses" itu, yang tidak berhasil. Saya merekomendasikan untuk memperbaiki bug, yang memecahkan masalah :)
Nathan Merrill

7

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;
        }
    }
}

Tidak dapat menguji ini saat ini, jadi tolong beri tahu saya jika itu rusak.

Pada dasarnya, pilih nilai untuk dolar, dan mengalahkan lawan sampai kita melebihi nilai itu.


7

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;
    }
}

Sangat sederhana, ia mencoba menentukan apakah lawan akan memperebutkan tawaran minimal dan, jika tidak, menempatkannya.

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;
    }
}

Versi baru, lebih pintar dari MarginalBot yang memeriksa untuk melihat apakah itu dapat membuat langkah menghasilkan uang tanpa kontes, daripada hanya berharap untuk menang dengan minimum.

Karena itu di keluarga yang sama dengan bot saya sebelumnya, tetapi strategi menghindari mencoba untuk mengalahkannya, saya pikir entri baru di pos yang sama adalah cara paling masuk akal untuk menyajikannya.

Sunting 1: Buat perubahan kecil ke metode Lelang baru untuk mengoptimalkan terhadap bot jenis penganalisis lainnya.

Sunting 2: Melakukan perubahan ke MarginalerBot untuk meminimalkan kerugian terhadap strategi licik atau non-deterministik.


Selamat datang di PPCG!
Martin Ender

1
Sederhana, tetapi mengalahkan semua bot lainnya dengan margin yang cukup besar!
RamenChef

6

BorkBorkBot

import net.ramenchef.dollarauction.DollarBidder;

public class BorkBorkBot extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
  }
}

Menyerah jika tidak bisa impas.


6

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;
    }
}

Itu harus dilakukan.


" Tawaran naik selisih 5 ¢ ". Bot Anda saat ini tidak melakukannya.
Kevin Cruijssen

1
@KevinCruijssen Cukup adil. Saya juga mengubah batas atas sehingga bisa menawar seluruh $ 1, untuk berjaga-jaga,
Neil

6

DeterrentBot

import net.ramenchef.dollarauction.DollarBidder;

public class DeterrentBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return opponentsBid > 5 ? 100 : opponentsBid + 5;
    }
}

Mencoba untuk meyakinkan bot yang berpikiran analitis bahwa satu-satunya langkah yang menang bukanlah bermain.


1
Saya perhatikan bahwa komentar saya yang agak samar, "Joshua? Apakah itu Anda?" sudah dihapus. Jadi hanya untuk memperjelas, itu adalah referensi ke kutipan terkenal dari film WarGames: "satu-satunya langkah yang menang bukanlah bermain" . (Joshua menjadi nama panggilan WOPR .)
Arnauld

5

LuckyDiceBot

LuckyDiceBot hanya mempercayai dadu. Dia melempar dua dadu, menambahkan jumlah ke nilai penawar saat ini, dan menawar sebanyak itu. Jika itu tidak cukup untuk mengatasi tawaran lawan, ia memotong kekalahannya dan melanjutkan.

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;
  }

};

2
How does this cut its losses or stop losses? If it always adds its dice roll to the opponents bid then you'll always bid more. The randomness might confuse a sufficiently analytical bot, I like the concept.
Freiheit

If the roll is 4 or less (statistically unlikely, but will eventually happen), then the bid is insufficient to beat the opponent and the auction ends.
Silvio Mayolo

Two things: 1. @Freiheit is right and this bot will keep bidding until it has won no matter how high. opponentsBid in nextBid(int opponentsBid) holds the total bid your opponent has bid thus far, not its next bid. A better term for the method would be raise (as the Poker term) imho. 2. Your bot doesn't bit in increments of 5 so is therefore validating one of the rules. If these problems are fixed I still like the concept though, because analytic bots won't be able to counter and so you'll most likely win quite often.
Kevin Cruijssen

5

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.


4

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.


This would be excellent against MirrorBot, MarginalerBot, and probably also MimicBot.
Nissa

@StephenLeppik That's what I was thinking when I made it. Still plenty of weaknesses, though.

+1, I like the idea. Hmm, is it intended that your bot bids 0 and breaks even if it starts a round (when opponentsBid is still 0)?
Kevin Cruijssen

@KevinCruijssen Yes. That can only happen against the very first opponent. Any of the bots that copy it will start at 0, so this won't waste more than 5c on them.

4

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.


4

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.

Wow that is a bug. It said I was commenting on the question, but it ended up here. Gotta find a way to reproduce it
Stan Strum

@RamenChef Typo.. But I've modified the entire bot. It had some bugs anyway..
Kevin Cruijssen

4
This can absolutely lose money. If you bid 100, then your opponent bids 105, you end up losing 100, and they only lose 5.

@Mnemonic Ah of course.. Hadn't thought about that part.. Hmm.. that makes things more interesting but also harder. Will edit the description for now, but leave the bot as is.
Kevin Cruijssen

1
I think you mean "lose" not "loose". Lose is the opposite of win. Loose is the opposite of tight.
Kat

3

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.


3

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.


1
Welcome to PPCG!
Alion

3

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.


Watch out for the AnalystKiller.
RamenChef

@RamenChef AFAIK the analyst killer just throws an exception if it sees itself being analyzed. I can catch that
dkudriavtsev

1
You probably should catch it.
RamenChef

@RamenChef No idea if that will work, I can't Java
dkudriavtsev

3

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.


1
MirrorBot calls newAuction with your own class, so that's a problem. Also, glad to know the 3 hours I spent on MimicBot weren't in vain.
Nissa

@StephenLeppik Removed the code in the newAuction because it would fail more often than not.. I can't counter MirrorBot nor can it counter me. Whoever begins of the two wins 95 cents and the other loses 5 cents.
Kevin Cruijssen

3
Holy ternary chaining, Batman!
Skyler

1
Also, when playing BorkBorkBot, shouldn't you raise to 95 when they hit 85? Otherwise you're both bidding 95 if they start.
Skyler

1
@Freiheit I know. I just used an additional case to return 0 in case I wanted to change the default for whatever reason. But I've put them under the default now (by commenting them out). And I know I can golf everything by quite a bit, but this isn't about making the shortest code. I just made it a ternary to make it a bit more compact, but that's about it. Will just leave it like this for now.
Kevin Cruijssen

3

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.


3

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.


2

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;
  }
}

2

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.


Mind if I port isPeeking into AbstractAnalystCounterBot?
Nissa

1
@StephenLeppik, well, I stole it from MBot...
Winston Ewert

1
Well, MBot probably stole it from me…
Nissa

2

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.


He can't bid 6 since all bids must be multiples of 5...
Neil

@Neil it's probably a typo...
Stan Strum

@Neil the rules specifically state: "Your bid does not need to be a multiple of 5¢"
MegaTom

@MegaTom Huh, well that was added since I last read the rules...
Neil

@Neil It was part of the original rules, but I added it there because it wasn't very obvious.
RamenChef

2

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.


Note that I've since updated my stack trace check to handle this.
Nissa

1

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.


2
The security manager would stop this.
Nissa

2
And this wouldn't work anyway since nowhere in the runner does it box its integers.
Nissa

Even if this wouldn't be stopped, this is a jerk move, although valid. "Sabotaging other bots is allowed, but attempting to alter field/method visibility will result in mysterious SecurityExceptions."
NoOneIsHere

1
@StephenLeppik The point of this is to break things like return opponentsBid <= 195 ? opponentsBid + 5 : 0 and make it return opponentsBid <= 100001 ? opponentsBid + 100001 : 100001.
NoOneIsHere

1
Fails to compile because of unchecked exceptions.
Nissa

1

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;
        }
    }
}

Why not just edit your challenge?
Nathan Merrill

@NathanMerrill How would I edit it?
RamenChef

By clicking the edit button, and replacing AnalystBot with this code?
Nathan Merrill

@NathanMerrill AnalystBot is deliberately bad code so that it can demonstrate the AnalystKiller sabotaging it.
RamenChef

1
The AnalystKiller still works with the improved one :) The issue with making it a post is that the challenge is far more visible than an answer.
Nathan Merrill

1

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

You can't declare nextBid to throw ClassCastException.
RamenChef

@RamenChef ok, swapped it to RuntimeException which don't require declaration :)
mleko

Your code for stack trace checking looks suspiciously similar to mine.
Nissa

@StephenLeppik probably it's copy of it
mleko

@mleko why though? The class it's copied from is an abstract superclass that's free to use.
Nissa

1

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


The stated rules forbid deliberately causing another bot to timeout.
Winston Ewert

@WinstonEwert can you quote? I can't find rule disallowing this
mleko

"Sabotaging other bots is allowed, but attempting to alter field/method visibility will result in mysterious SecurityExceptions. An exception is causing another bot to break the 500ms limit." Also, I'm defending against timeout.
RamenChef

@RamenChef but this don't alter visibility of other elements. I'm not sure if I understand you correctly. Is provoking timeout allowed?
mleko

"An exception is causing another bot to break the 500ms limit." Specifically, this is an exception to the rule about sabotage.
RamenChef
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.