25
\$\begingroup\$

sheesh, is this really the 4th? For all you oldcomers, the core challenge is the same, but we are using Java instead of a custom language. Here are the past three CodeBot challenges, in case you are interested.

The goal of CodeBots is to make your bot as viral as possible. Each bot carries a Flag, and you need to make sure that your Flag is everywhere.

API

Bots will follow a standardized "protocol", and bots progress through the following steps synchronously:

  1. IPAddress selectMessageRecipient() allows a bot to choose who he wants to send a "friendly" message to.
  2. Message sendMessage() allows a bot to choose the contents of the message he will send out.
  3. processMessage(IPAddress, Message) is called for each message a bot receives.
  4. FunctionType selectFunctionToBlock() blocks a function from being overwritten for the current turn. See step 7.
  5. IPAddress selectAttackTarget() allows a bot to choose who he wants to DDOS. A DDOS attack is successful if the bot is targeted by 3 bots at the same time. If an attack is successful, then each of the attackers can perform step 6 and 7.
  6. readData(ReadonlyBot) allows a bot to read data stored on the vulnerable bot.
  7. FunctionType selectFunctionToReplace() is the crux of this challenge. You are allowed to select 1 function (of the 8 listed here) to copy over from your bot to their bot. Then, your function will be called instead of theirs. If multiple bots select the same function, a random one will be successful.
  8. String getFlag() is called at the end of the game, and should return a string unique to your submission. Your function should always return the same string. The submission with the most flags at the end of the game wins.

Storage

You have 3 forms of storage, an AddressBook, a Log, and a Variables. These forms of storage are local to the bot your function is running on (so if your function gets copied, the contents of each of these objects will be different). All of these objects can be manually modified or cleared. To obtain these objects, you have a getter in your class (e.g. getLog()).

The AddressBook stores a list of IPAddress, each with a AddressType, which allows you to classify the different types of addresses. AddressBook will always contain at least 1 address (if it is cleared, a random one will be added). Clearing your AddressBook to obtain multiple IPAddresses is not allowed.

The Log stores a list of all actions taken, as well as data about the action. It also includes a history of all successful attacks (though you don't know which functions they overwrote)

The Variables object allows you to store string variables attached to a string name. On game start, Variables will contain a single variable, ID, which contains a randomly generated ID that is unique to your bot type.

You also have other access functions:

  • int getTurnNumber() returns an integer with the current turn
  • bool functionsMatch(ReadonlyBot, FunctionType) tests to see if the ReadonlyBot's function matches yours
  • IPAddress personalAddress() returns your IPAddress

How to implement

  • You can obtain the code from Github
  • Add your bot to the \bots folder, and then add a reference to your bot in controller\CodeBotFactory.java
  • Your bot must extend codebots.bot.CodeBot or codebots.bots.DefaultCodeBot
  • You need Java 8 if you want to run the controller.
  • You can run the code (assuming you are in the /src folder) using javac codebots\*.java to compile, then java codebots.Main to run.
  • You may not have any non-constant member variables in your class
  • Reflection is not allowed.
  • Forms of communication between bots (of the same or different types) outside of the methods listed above is not allowed.
  • Dumb and/or Suicidal bots are allowed, but all bots should be functionally different than existing entries.
  • If you want randomness in your bot, use getRandom()
  • Please try to keep your code efficient. I've spent a good deal of time profiling and making the controller fast.

Scores

105.2501 Expelliarmus!
104.5803 I'm Helping!
104.2746 Who Am I?
103.8529 Dumb Bot
103.2028 Replacer
102.7045 Chaos
102.4046 Hermit Bot
102.2849 Swarmer
100.5598 Random bot loves you
99.966 Trust in Trust!
99.0185 codebots.bots.DefaultCodeBot
91.2942 codebots.bots.MarkedBot
91.1423 Just your friendly neighborhood mail delivering robot.
89.4645 null

\$\endgroup\$
25
  • \$\begingroup\$ You should probably make Log.LogEntry final, with it not final I can create logs with any information I want ... that only my bot function would be able to read or create. \$\endgroup\$ Dec 11, 2015 at 14:09
  • \$\begingroup\$ Can readData access the IpAddress of the bot it is reading? \$\endgroup\$ Dec 11, 2015 at 18:13
  • \$\begingroup\$ @TheNumberOne not currently, but I don't see why not. I don't have the code on me right now, but I'll update the code to change that. \$\endgroup\$ Dec 11, 2015 at 19:02
  • 3
    \$\begingroup\$ I find it interesting that Chaos makes DisarmBot and MarkedBot trade places on the leader board. \$\endgroup\$ Dec 11, 2015 at 20:09
  • 1
    \$\begingroup\$ Currently on round 7850 out of 10000, getting more accurate scores... \$\endgroup\$ Dec 12, 2015 at 22:50

14 Answers 14

4
\$\begingroup\$

TrustBot

If you send him a message, he will do what it says. If he reads a bot, he will copy the addresses directly into his book. He attacks the bots that the address book says to attack.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.*;
import java.util.*;

public class TrustBot extends CodeBot {
    @Override
    public IPAddress selectMessageRecipient() {
        AddressBook book = getAddressBook();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public Message sendMessage() {
        AddressBook book = getAddressBook();
        return new Message(Message.MessageType.INFORM, book.getAddress(getRandom().nextInt(book.size())));
    }

    @Override
    public void processMessage(IPAddress s, Message m) {
        AddressBook book = getAddressBook();
        if(m.getAddress() != null){
            if(m.getType() == Message.MessageType.ATTACK){
                book.add(m.getAddress(), AddressBook.AddressType.TO_ATTACK);
            }
            else if(m.getType() == Message.MessageType.HELP){
                book.add(m.getAddress(), AddressBook.AddressType.TO_DEFEND);
            }
            else if(m.getType() == Message.MessageType.CONFIRM){
                book.add(m.getAddress(), AddressBook.AddressType.TRUSTED);
            }
            else if(m.getType() == Message.MessageType.REJECT){
                book.add(m.getAddress(), AddressBook.AddressType.UNTRUSTED);
            }
            else if(m.getType() == Message.MessageType.AVOID){
                book.remove(m.getAddress());
            }
            else{
                book.add(m.getAddress());
            }
        }else{
            Message msg = new Message(m.getType(), s);
            processMessage(s, msg);
        }
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return FunctionType.SELECT_FUNCTION_TO_BLOCK;
    }

    @Override
    public IPAddress selectAttackTarget() {
        AddressBook book = getAddressBook();
        List<IPAddress> l;
        l = book.getAddressesOfType(AddressBook.AddressType.TO_ATTACK);
        Iterator<IPAddress> I = l.iterator();
        if(!I.hasNext())
            return book.getAddress(getRandom().nextInt(book.size()));
        return I.next();
    }

    @Override
    public void readData(ReadonlyBot bot) {
        AddressBook myBook = getAddressBook();
        ReadonlyAddressBook hisBook = bot.getAddressBook();
        AddressBook.AddressType[] values = AddressBook.AddressType.values();
        for(int i=0;i<values.length;i++){
            myBook.addAll(hisBook.getAddressesOfType(values[i]), values[i]);
        }
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        return getRandom().nextInt(2)==1?FunctionType.GET_FLAG:FunctionType.SELECT_FUNCTION_TO_BLOCK;
    }

    @Override
    public String getFlag() {
        return "Trust in Trust!";
    }
}
\$\endgroup\$
4
\$\begingroup\$

AmnesiaBot

A random bot that injects other bots with memory loss code. Each function starts with code to clear the log, Address Book and Variables. This code will make smart bots lose memory, in an attempt to do away with logic.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.*;

public class AmnesiaBot extends CodeBot {

    private void clear(){
        getAddressBook().clear();
        getAddressBook().add(getAddressBook().getAddress(0), AddressBook.AddressType.TRUSTED);
        getVariables().clear();
        getLog().clear();
    }

    @Override
    public IPAddress selectMessageRecipient() {
        clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public Message sendMessage() {
        clear();
        Message.MessageType[] values = Message.MessageType.values();
        return new Message(values[getRandom().nextInt(values.length)], getAddressBook().getAddress(0));
    }

    @Override
    public void processMessage(IPAddress source, Message message) {
        clear();
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        clear();
        return getTurnNumber() % 2 == 0 ?
             FunctionType.GET_FLAG: FunctionType.SELECT_FUNCTION_TO_BLOCK;
    }

    @Override
    public IPAddress selectAttackTarget() {
        clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public void readData(ReadonlyBot bot) {
        clear();
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        clear();
        FunctionType[] values =  FunctionType.values();
        return values[getRandom().nextInt(values.length)];
        //random gives a 7/8 chance of successes. 
    }

    @Override
    public String getFlag() {
        return "Who Am I?";
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ I think this one wins only due to the inability of a smarter script to be capable of remembering anything. i.e. clearing the Variables object is really damn powerful. \$\endgroup\$ Dec 12, 2015 at 17:48
  • \$\begingroup\$ @draco18s this was not really ment to be a serious answer... \$\endgroup\$
    – MegaTom
    Dec 12, 2015 at 17:55
  • \$\begingroup\$ I know! That's why I'm so confused by it. XD \$\endgroup\$ Dec 12, 2015 at 17:55
3
\$\begingroup\$

NullBot

His flag is very ... characteristic ...

package codebots.bots;
import codebots.gameobjects.*;
public class NullBot extends DefaultCodeBot {
    public IPAddress selectMessageRecipient() {
        return null;
    }
    public Message sendMessage() {
        return null;
    }
    public IPAddress selectAttackTarget() {
        return null;
    }
    public FunctionType selectFunctionToReplace() {
        return null;
    }
    public FunctionType selectFunctionToBlock() {
        return null;
    }
    public String getFlag(){
        return null;
    }
}

This is also meant to test the controller and the limits of the "dumb bots are allowed" rule.

\$\endgroup\$
4
  • \$\begingroup\$ Technically he doesn't fit the specification, because he doesn't exactly return a String for his flag. \$\endgroup\$ Dec 9, 2015 at 18:44
  • 3
    \$\begingroup\$ null is a string. ;) Just a fancy string. \$\endgroup\$ Dec 9, 2015 at 18:45
  • \$\begingroup\$ This made me recognize a flaw in my spec, which has been specified: "all bots should be functionally different than existing entries" \$\endgroup\$ Dec 9, 2015 at 18:46
  • \$\begingroup\$ @NathanMerrill Fixed to more closely follow the spec. \$\endgroup\$ Dec 9, 2015 at 18:49
3
\$\begingroup\$

RandomCodeBot

Obligatory random KoTH entry

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.AddressBook;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class RandomCodeBot extends CodeBot {

    @Override
    public IPAddress selectMessageRecipient() {
        AddressBook book = getAddressBook();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public Message sendMessage() {
        Message.MessageType[] values = Message.MessageType.values();
        return new Message(values[getRandom().nextInt(values.length)]);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {

    }

    @Override
    public FunctionType selectFunctionToBlock() {
        FunctionType[] values =  FunctionType.values();
        return values[getRandom().nextInt(values.length)];
    }

    @Override
    public IPAddress selectAttackTarget() {
        AddressBook book = getAddressBook();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public void readData(ReadonlyBot bot) {

    }

    @Override
    public FunctionType selectFunctionToReplace() {
        FunctionType[] values =  FunctionType.values();
        return values[getRandom().nextInt(values.length)];
    }

    @Override
    public String getFlag() {
        return "Random bot loves you";
    }
}
\$\endgroup\$
3
\$\begingroup\$

DisarmerBot

DisarmerBot isn't too intelligent. If it receives attack instructions, it will choose a random attackee, otherwise, it will attack a random player. It just overrides their selectFunctionToBlock function to block selectFunctionToBlock.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.*;

import java.util.ArrayList;
import java.util.List;

public class DisarmerBot extends CodeBot {
    public IPAddress selectMessageRecipient() { return null; }
    public Message sendMessage() { return null; }

    public void processMessage(IPAddress source, Message message) {
        if (message != null && message.getAddress() != null && message.getType() == Message.MessageType.ATTACK)
            getAddressBook().add(message.getAddress());
    }

    public IPAddress selectAttackTarget() {
        AddressBook book = getAddressBook();
        List<IPAddress> attack = book.allAddresses();
        if (attack.size() > 0) {
            IPAddress bot = attack.get(getRandom().nextInt(attack.size()));
            book.clear();
            return bot;
        }
        //Umm...
        book.clear();
        return book.getAddress(0);
    }

    public void readData(ReadonlyBot bot) { getLog().clear(); /*Safety*/ }
    public FunctionType selectFunctionToReplace() { return FunctionType.SELECT_FUNCTION_TO_BLOCK; }
    public FunctionType selectFunctionToBlock() { return FunctionType.SELECT_FUNCTION_TO_BLOCK; }
    public String getFlag() { return "Expelliarmus!"; }
}
\$\endgroup\$
2
  • \$\begingroup\$ You can select the nth address without needing to do allAddresses(). If you look at my random bot, it is doing random address selection. I've updated your code on Github (for efficiency reasons), but if you feel like it doesn't work, I'm happy to revert it. \$\endgroup\$ Dec 10, 2015 at 15:10
  • \$\begingroup\$ Oh, my bad, fixed. \$\endgroup\$ Dec 10, 2015 at 23:05
3
\$\begingroup\$

MarkedBot

Marks itself on the first round, and uses that info in later rounds. That way, if another bot gets injected with its attack code, it will be ineffective.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.*;

public class MarkedBot extends CodeBot {

    @Override
    public IPAddress selectMessageRecipient() {
        Variables v = getVariables();
        AddressBook ab = getAddressBook();
        if(getTurnNumber()==0)
            v.add(v.get("ID"),"true");
        if("true".equals(v.get("hasOurFlag"))){
            ab.remove(ab.getAddress(0));
            v.remove("hasOurFlag");
        }
        return ab.getAddress(0);
    }

    @Override
    public Message sendMessage() {
        return new Message(Message.MessageType.STOP);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {
        if(message.getType() != Message.MessageType.STOP)
            getAddressBook().add(source, AddressBook.AddressType.TO_ATTACK);
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        Variables v = getVariables();
        if("true".equals(v.get(v.get("ID"))))
            return FunctionType.GET_FLAG;
        return FunctionType.SELECT_FUNCTION_TO_BLOCK;
    }

    @Override
    public IPAddress selectAttackTarget() {
        Variables v = getVariables();
        if("true".equals(v.get(v.get("ID"))))
            return getAddressBook().getAddress(0);
        else
            return null;
    }

    @Override
    public void readData(ReadonlyBot bot) {
        Variables v = getVariables();
        if(functionsMatch(bot, FunctionType.GET_FLAG))
            v.add("hasOurFlag", "true");
        else if("false".equals(v.get("hasOurFlag")))
            v.add("hasOurFlag", "false2");
        else
            v.add("hasOurFlag", "false");
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        Variables v = getVariables();
        if("true".equals(v.get(v.get("ID"))))
            if(!v.has("hasOurFlag") || "false".equals(v.get("hasOurFlag")))
                return FunctionType.GET_FLAG;
            else if("false2".equals(v.get("hasOurFlag")))
                return FunctionType.SELECT_FUNCTION_TO_BLOCK;
            else
                return FunctionType.SEND_MESSAGE;
        return FunctionType.SELECT_FUNCTION_TO_REPLACE;
    }

    @Override
    public String getFlag() {
        return this.getClass().getName();
    }
}
\$\endgroup\$
5
  • \$\begingroup\$ I found several bugs in this bot (typos, using == instead of equals) You also found a flaw in my system: you shouldn't be able to create new random IPAddresses. I've fixed that issue (and removed the code doing it). You can find the updated code on github \$\endgroup\$ Dec 10, 2015 at 17:21
  • \$\begingroup\$ Also, congrats on top spot! \$\endgroup\$ Dec 10, 2015 at 17:21
  • \$\begingroup\$ HelperBot isn't very smart. It only got top because all the other bots were dumb. :P This is probably the first effective bot. \$\endgroup\$ Dec 10, 2015 at 17:42
  • 1
    \$\begingroup\$ @NathanMerrill Does that mean we are no longer allowed to create forged IPAdresses for deception? (if thats the case i'm gonna need to redesign mine) \$\endgroup\$ Dec 10, 2015 at 23:23
  • \$\begingroup\$ A bot never has access to the round intentionally. You may use opponents IP addresses as forgeries, but creating one isn't allowed. Also, a constant is a variable set at static time or initialization time. \$\endgroup\$ Dec 11, 2015 at 0:12
2
\$\begingroup\$

SwarmBot

This not-well-named bot is rather complicated (and the most complicated submitted thus far), but I made an attempt at a bot that systematically replaces all methods of a target before moving on to a new target. It attempts to identify copies of itself and treat them as allies, periodically checking up on them to insure integrity. I haven't come up with a better name.

Linking to the github repo branch, as this bot is 340 lines long.

https://github.com/Draco18s/CodeBots4/blob/master/src/codebots/bots/SwarmBot.java

A few interesting points:

  • Lines 14-24 are simply an unmodifiable list that made it easy to tweak the order in which the bot replaces the methods of its target. It stores which index its on in Variables and increments each round. This should follow the "no non-constant variables" rule.
  • Lines 203-217 deal with ally verification. We actually don't care that another bot implements all eight of our instructions. Only four are essential, and if we're missing one on a "trusted" ally, we replace it with our own.
  • Lines 295-300 were an unexpected boost in effectiveness. By protecting our Flag on the first two turns of the game we avoid dumb bots replacing our Flag before we have a chance to spread very far. Waiting longer, however, gives other bots a chance to replace our BlockFunction and that causes performance to degrade (suspected due to RandomBot interfering with allies attempting to undo corruption).
  • For the longest time during the development of this bot, this bot caused HelperBot to surge ahead, at one point breaching the 130 mark, while this bot languished at the 81-98 range, but having dragged MarkedBot and DefaultBot's effectiveness down several points.
  • This bot was only possible with the added functionsMatch method. Without functionsMatch it was impossible to write a bot that could make meaningful decisions, as it was blind. It could read its targets variables and logs, but know nothing of its target's state.

There are likely still some improvements possible, but I cannot see them. Lines 198-205 are probably a performance hog, but until the IPAddress class allows addresses to be reconstituted from being stored in Variables, this is necessary (as bots have no means of validating an address, any storage of an invalid address causes the game to wrap a null target in a ReadOnlyBot, throwing NPE).

Edit: Updates 12/12/15

Tweaking some of the parameters on the getTurnNumber() logic allowed for some boosts in performance. The increase from 5% to 10% in end-game targeting was worth about 15 points, likewise increasing the early-game targeting from 5% to 8%. Combined this bot can now (almost) survive even when faced with AmnesiaaBot (reaching 2nd with a score of 110, where HelperBot gets to about 117).

Even with these tweaks, it can get unlucky, so for 10 rounds it's score range is approximately 170-185.

\$\endgroup\$
9
  • \$\begingroup\$ Awesome! It is intentional that you cannot create IPAddresses from strings. \$\endgroup\$ Dec 11, 2015 at 17:30
  • \$\begingroup\$ Well, ostensibly, yes! (Or bots will create arbitrary ones to find new bots). The problem is that if you do the simulation crashes. ;) \$\endgroup\$ Dec 11, 2015 at 17:32
  • \$\begingroup\$ Urm, on line 143 you use a non-existing constructor. \$\endgroup\$ Dec 11, 2015 at 17:55
  • \$\begingroup\$ @TheNumberOne it was valid when I wrote it. Nathan probably updated the base. \$\endgroup\$ Dec 11, 2015 at 18:45
  • \$\begingroup\$ @TheNumberOne update made. The new IPAddress call should have been a "look up from address book" as I'd done in readData. I extracted that lookup and fixed line 143. \$\endgroup\$ Dec 11, 2015 at 19:16
1
\$\begingroup\$

DefaultCodeBot

Tries to do reasonable things. (Override this class if you don't want to implement all of the functions)

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class DefaultCodeBot extends CodeBot {

    @Override
    public IPAddress selectMessageRecipient() {
        return getAddressBook().getAddress(0);
    }

    @Override
    public Message sendMessage() {
        return new Message(Message.MessageType.INFORM);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {

    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return FunctionType.GET_FLAG;
    }

    @Override
    public IPAddress selectAttackTarget() {
        return getAddressBook().getAddress(0);
    }

    @Override
    public void readData(ReadonlyBot bot) {

    }

    @Override
    public FunctionType selectFunctionToReplace() {
        return FunctionType.GET_FLAG;
    }

    @Override
    public String getFlag() {
        return this.getClass().getName();
    }
}
\$\endgroup\$
1
\$\begingroup\$

HelperBot

Helper bot does nothing but try to spread its own flag...or at least the flag it is currently carying...

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class HelperBot extends CodeBot {

    @Override
    public IPAddress selectMessageRecipient() {
        getAddressBook().clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public Message sendMessage() {
        return new Message(Message.MessageType.INFORM);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {

    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return FunctionType.GET_FLAG;
    }

    @Override
    public IPAddress selectAttackTarget() {
        getAddressBook().clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public void readData(ReadonlyBot bot) {

    }

    @Override
    public FunctionType selectFunctionToReplace() {
        return FunctionType.GET_FLAG;
    }

    @Override
    public String getFlag() {
        return "I'm Helping!";
    }
}

If HelperBot assumes that any method of its own that is overwritten (other than getFlag()) will be overwritten with something better.

\$\endgroup\$
1
\$\begingroup\$

Chaos

He frees all flags from the tyranny of being blocked.

package codebots.bots;

import codebots.bot.ReadonlyBot;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by thenumberone on 12/11/15.
 *
 * @author thenumberone
 */
public class Chaos extends DefaultCodeBot{

    private static final String NAME = "Chaos";
    private static final String BLOCK = NAME + ":BLOCK";
    private static final String ATTACK = NAME + ":ATTACK";
    private static final String REPLACE = NAME + ":REPLACE";
    private static final String READ = NAME + ":READ";
    private static final String FLAG = NAME + ":FLAG";
    private static final String SELECT = NAME + ":SELECT";
    private static final String SEND = NAME + ":SEND";

    private static final Map<String, FunctionType> commands;

    private static final String[] REPLACEMENT_ORDER = {
            BLOCK,
            FLAG,
            REPLACE,
            READ,
            ATTACK,
    };

    private static final String DEFAULT = BLOCK;
    private static final String BLOCK_FUNCTION = BLOCK;

    static {
        Map<String, FunctionType> c = new HashMap<>();
        c.put(BLOCK, FunctionType.SELECT_FUNCTION_TO_BLOCK);
        c.put(ATTACK, FunctionType.SELECT_ATTACK_TARGET);
        c.put(REPLACE, FunctionType.SELECT_FUNCTION_TO_REPLACE);
        c.put(READ, FunctionType.READ_DATA);
        c.put(FLAG, FunctionType.GET_FLAG);
        c.put(SELECT, FunctionType.SELECT_MESSAGE_RECIPIENTS);
        c.put(SEND, FunctionType.SEND_MESSAGE);
        commands = Collections.unmodifiableMap(c);
    }

    @Override
    public String getFlag() {
        return NAME;
    }

    @Override
    public void readData(ReadonlyBot bot) {
        for (String command : commands.keySet()){
            getVariables().remove(command);
        }
        for (String command : REPLACEMENT_ORDER){
            if (!functionsMatch(bot, commands.get(command))) {
                getVariables().add(command, "");
                return;
            }
        }
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return commands.get(BLOCK_FUNCTION);
    }

    @Override
    public IPAddress selectAttackTarget() {
        getAddressBook().clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        for (String command : REPLACEMENT_ORDER){
            if (getVariables().has(command)) {
                getVariables().remove(command);
                return commands.get(command);
            }
        }
        return commands.get(DEFAULT);
    }
}
\$\endgroup\$
1
\$\begingroup\$

Replacer

This entry replaces all selectFunctionToReplace functions with its own selectFunctionToReplace function.

package codebots.bots;

import codebots.bot.ReadonlyBot;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by thenumberone on 12/11/15.
 *
 * @author thenumberone
 */
public class Replacer extends DefaultCodeBot{

    private static final String NAME = "Replacer";
    private static final String BLOCK = NAME + ":BLOCK";
    private static final String ATTACK = NAME + ":ATTACK";
    private static final String REPLACE = NAME + ":REPLACE";
    private static final String READ = NAME + ":READ";
    private static final String FLAG = NAME + ":FLAG";
    private static final String SELECT = NAME + ":SELECT";
    private static final String SEND = NAME + ":SEND";

    private static final Map<String, FunctionType> commands;

    private static final String[] REPLACEMENT_ORDER = {
            REPLACE,
            FLAG,
            READ,
            ATTACK
    };

    private static final String DEFAULT = REPLACE;
    private static final String BLOCK_FUNCTION = FLAG;

    static {
        Map<String, FunctionType> c = new HashMap<>();
        c.put(BLOCK, FunctionType.SELECT_FUNCTION_TO_BLOCK);
        c.put(ATTACK, FunctionType.SELECT_ATTACK_TARGET);
        c.put(REPLACE, FunctionType.SELECT_FUNCTION_TO_REPLACE);
        c.put(READ, FunctionType.READ_DATA);
        c.put(FLAG, FunctionType.GET_FLAG);
        c.put(SELECT, FunctionType.SELECT_MESSAGE_RECIPIENTS);
        c.put(SEND, FunctionType.SEND_MESSAGE);
        commands = Collections.unmodifiableMap(c);
    }

    @Override
    public String getFlag() {
        return NAME;
    }

    @Override
    public void readData(ReadonlyBot bot) {
        for (String command : commands.keySet()){
            getVariables().remove(command);
        }
        for (String command : REPLACEMENT_ORDER){
            if (!functionsMatch(bot, commands.get(command))) {
                getVariables().add(command, "");
                return;
            }
        }
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return commands.get(BLOCK_FUNCTION);
    }

    @Override
    public IPAddress selectAttackTarget() {
        getAddressBook().clear();
        return getAddressBook().getAddress(0);
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        for (String command : REPLACEMENT_ORDER){
            if (getVariables().has(command)) {
                getVariables().remove(command);
                return commands.get(command);
            }
        }
        return commands.get(DEFAULT);
    }
}
\$\endgroup\$
1
\$\begingroup\$

MailBot

Mailbot only handles messages. It is not successful at getting its own flag out into the world (average score ~50, slightly higher than nullbot at ~45), but send it a message and it'll forward your address off to someone else.

package codebots.bots;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import codebots.bot.ReadonlyBot;
import codebots.gameobjects.AddressBook;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class MailBot extends DefaultCodeBot {
    private final String TEAM = "Just your friendly neighborhood mail delivering robot.";
    private final String TEAMALT = "Mailmain";
    private final List<FunctionType> funcList;
    {
        List<FunctionType> list = new ArrayList<FunctionType>();
        list.add(FunctionType.SELECT_MESSAGE_RECIPIENTS);
        list.add(FunctionType.SEND_MESSAGE);
        list.add(FunctionType.PROCESS_MESSAGE);
        funcList = Collections.unmodifiableList(list);
    }

    @Override
    public IPAddress selectMessageRecipient() {
        AddressBook book = getAddressBook();
        IPAddress ip;
        List<IPAddress> l = book.getAddressesOfType(AddressBook.AddressType.TO_ATTACK);
        if(l.size() > 0) {
            ip = l.get(0);
            book.add(ip,AddressBook.AddressType.UNTRUSTED);
            return ip;
        }
        ip = book.getAddress(getRandom().nextInt(book.size()));
        book.add(ip,AddressBook.AddressType.UNTRUSTED);
        return ip;
    }

    @Override
    public Message sendMessage() {
        AddressBook book = getAddressBook();
        IPAddress ip;
        
        List<IPAddress> l = book.getAddressesOfType(AddressBook.AddressType.UNTRUSTED);
        if(l.size() > 0) {
            ip = l.get(0);
            book.add(ip,AddressBook.AddressType.TO_DEFEND);
            return new Message(Message.MessageType.INFORM,ip);
        }
        
        ip = book.getAddress(getRandom().nextInt(book.size()));
        book.add(ip,AddressBook.AddressType.UNTRUSTED);
        return new Message(Message.MessageType.INFORM,ip);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {
        AddressBook book = getAddressBook();
        book.add(source,AddressBook.AddressType.TO_ATTACK);
        if(message.getAddress() != null)
            book.add(message.getAddress(),AddressBook.AddressType.TO_ATTACK);
    }

    @Override
    public FunctionType selectFunctionToBlock() {
        return FunctionType.SEND_MESSAGE;
    }

    @Override
    public IPAddress selectAttackTarget() {
        //Mailbot doesn't attack
        return null;
    }

    @Override
    public void readData(ReadonlyBot bot) { }

    @Override
    public FunctionType selectFunctionToReplace() {
        //if our attack selection gets overwritten,
        //then attack a message-based function
        return funcList.get(getTurnNumber()%3);
    }

    @Override
    public String getFlag() {
        return TEAM;
        //if flag is too long, use:
        //return TEAMALT;
    }
}

I considered saving details so it would forward the whole messsage on to a new bot (content and flag) rather than just the sender's IP, but that would have involved heavy use of Variables without any functional gain, especially considering AmnesiaBot being in play.

\$\endgroup\$
1
\$\begingroup\$

DumbBot

Ugh, this feels dirty. This is likely the only thing that beats AmnesiaBot. In reality, its just a specialized RandomBot: it gets a random bot in the simulation (via getAddressBook().clear()) and randomly replaces either the Block function or the Flag function. That's it. By picking only those two, its flag-spread rate is higher than either AmnesiaBot or HelperBot, but only slightly after 3000 rounds:

Round 2999
105.50666666666666  Dumb Bot
105.07266666666666  Who Am I?
103.541             I'm Helping!
102.94833333333334  Swarmer
102.82033333333334  Chaos
102.82033333333334  Replacer
101.55666666666667  Expelliarmus!
101.25833333333334  Trust in Trust!
100.347             Random bot loves you
99.22233333333334   codebots.bots.DefaultCodeBot
92.62733333333334   codebots.bots.MarkedBot
91.80966666666667   Just your friendly neighborhood mail delivering robot.
90.46933333333334   null

I fiddled around with the replacement function a bit, but in the end, this is the version that was most successful.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.AddressBook;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class DumbBot extends CodeBot {


    @Override
    public FunctionType selectFunctionToBlock() {
        return getRandom().nextBoolean()?FunctionType.SELECT_FUNCTION_TO_BLOCK:FunctionType.GET_FLAG;
    }

    @Override
    public IPAddress selectAttackTarget() {
        AddressBook book = getAddressBook();
        book.clear();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        return getRandom().nextBoolean()?FunctionType.SELECT_FUNCTION_TO_BLOCK:FunctionType.GET_FLAG;
    }

    @Override
    public void readData(ReadonlyBot bot) {

    }

    @Override
    public IPAddress selectMessageRecipient() {
        AddressBook book = getAddressBook();
        book.clear();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public Message sendMessage() {
        Message.MessageType[] values = Message.MessageType.values();
        return new Message(values[getRandom().nextInt(values.length)]);
    }

    @Override
    public void processMessage(IPAddress source, Message message) {

    }

    @Override
    public String getFlag() {
        return "Dumb Bot";
    }
}
\$\endgroup\$
0
\$\begingroup\$

Hermit Bot

He lives alone and talks only to himself. If fewer people know who he is, then he will be bothered less. If someone does bother him, he will attack them until someone else bothers him.

package codebots.bots;

import codebots.bot.CodeBot;
import codebots.bot.ReadonlyBot;
import codebots.gameobjects.AddressBook;
import codebots.gameobjects.FunctionType;
import codebots.gameobjects.IPAddress;
import codebots.gameobjects.Message;

public class HermitBot extends CodeBot {

    @Override
    public IPAddress selectMessageRecipient() {
        return personalAddress();//Talks to himself.
    }

    @Override
    public Message sendMessage() {
        Message.MessageType[] values = Message.MessageType.values();
        return new Message(values[getRandom().nextInt(values.length)], personalAddress());
    }

    @Override
    public void processMessage(IPAddress source, Message message) {
        AddressBook book = getAddressBook();
        if(source != personalAddress()){
            //if someone talks to you, put them in your addres book and remove everyone else
            book.clear();
            book.add(source);
            book.remove(0);
        }
    }


    @Override
    public FunctionType selectFunctionToBlock() {
        return getTurnNumber() % 3 == 0 ?
                FunctionType.SELECT_FUNCTION_TO_BLOCK: FunctionType.GET_FLAG;
    }

    @Override
    public IPAddress selectAttackTarget() {
        AddressBook book = getAddressBook();
        return book.getAddress(getRandom().nextInt(book.size()));
    }

    @Override
    public void readData(ReadonlyBot bot) {
        Variables v = getVariables();
        if(functionsMatch(bot, FunctionType.SELECT_FUNCTION_TO_BLOCK))
            v.add("Block Dif","A");
        if(functionsMatch(bot, FunctionType.GET_FLAG))
            v.add("Flag Dif","B");
        if(functionsMatch(bot, FunctionType.SELECT_MESSAGE_RECIPIENTS))
            v.add("Targ Dif","C");
            
    }

    @Override
    public FunctionType selectFunctionToReplace() {
        Variables v = getVariables();
        FunctionType r = getRandom().nextBoolean()?FunctionType.SELECT_FUNCTION_TO_REPLACE: FunctionType.READ_DATA;

        if(v.has("Targ Dif"))
            r = FunctionType.SELECT_MESSAGE_RECIPIENTS;
        if(v.has("Flag Dif") && getTurnNumber() % 3 == 0)
            r = FunctionType.GET_FLAG;
        if(v.has("Block Dif"))
            r = FunctionType.SELECT_FUNCTION_TO_BLOCK;
        v.clear();
        return r;
    }

    @Override
    public String getFlag() {
        return "Hermit Bot";
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.