Code Bots 4: Functional Programming

25

7

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

Nathan Merrill

Posted 2015-12-09T17:50:08.273

Reputation: 13 591

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. – TheNumberOne – 2015-12-11T14:09:55.910

Can readData access the IpAddress of the bot it is reading? – TheNumberOne – 2015-12-11T18:13:26.557

@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. – Nathan Merrill – 2015-12-11T19:02:12.120

That would simplify SwarmBot slightly. I have to do a variable-lookup and then address-lookup in order to perform address book modification. – Draco18s no longer trusts SE – 2015-12-11T19:23:42.797

3I find it interesting that Chaos makes DisarmBot and MarkedBot trade places on the leader board. – TheNumberOne – 2015-12-11T20:09:44.453

I think there may be some problems with the AddressBook. You put IPAddress objects in a HashMap but they don't implement hashCode() and equals(). This might work, but only if each IPAddress object exists only once. Apart from that the IPAddress objects are sorted by the hash code in the map. This means that simple bots calling getAddressBook().getAddress(0) will get the IPAddress with the smallest hash code, which seems unfair to me, because bots whose IPAddress has a small hash code will be attacked more often than others. – Sleafar – 2015-12-11T23:31:45.710

@Sleafar IPAddresses do only exist once, which is very intentional. The Bot with the smallest IPAddress will be randomized each round, so it should even out. I've used other methods for random access, but they all cause code slowdown, so I'm ok with that. – Nathan Merrill – 2015-12-12T00:20:00.903

@NathanMerrill Should probably override hashCode and equals anyway. Its good practice. – Draco18s no longer trusts SE – 2015-12-12T02:38:45.333

Except that the default behavior is the one I want. The IP address isn't defined by the string, it's simply a nice visual representation. – Nathan Merrill – 2015-12-12T04:13:26.503

1Currently on round 7850 out of 10000, getting more accurate scores... – LegionMammal978 – 2015-12-12T22:50:23.710

Sorry, for bugging you ... but when are you going to add the getIPAddress function to ReadOnlyBot ? – TheNumberOne – 2015-12-12T23:20:11.473

I can't do it today, but if you code a bot that uses that function, I'll implement it. – Nathan Merrill – 2015-12-13T01:30:48.960

Ok :) I'm actually working on one right now. btw, you should probably add hasAttacked to ReadOnlyLogEntry. – TheNumberOne – 2015-12-13T01:58:52.023

Some results after 10000 rounds – LegionMammal978 – 2015-12-13T13:22:28.150

@LegionMammal978 its good to see that all of the high rankings match mine exactly. – Nathan Merrill – 2015-12-13T13:32:45.623

@TheNumberOne done. – Nathan Merrill – 2015-12-13T14:02:09.747

Very interesting challenge, even though I'm quite late to the party. Just an observation: for newcomers, the "Scores" section is rather hard to correlate to the answers at first glance. Having the answer title match or include what getFlag() returns would make that easier (since I'm guessing the scores are generated and the code has no way of knowing what the answer's title is), e.g. instead of just "AmnesiaBot", if it could say "AmnesiaBot - Who Am I?", that would help. – Amos M. Carpenter – 2016-04-04T08:07:47.550

@AmosM.Carpenter yeah, that was fairly annoying, which is why I ended up linking to the questions. – Nathan Merrill – 2016-04-04T14:03:18.960

I'm sad to see that my last entry was never run officially, despite out-scoring the current leader at the time. It came in only a day after the leaderboard was last updated in the question. Additionally, there is one other entry that was never run, having been posted 10 days later.

– Draco18s no longer trusts SE – 2018-03-13T19:14:57.340

Oh, I didn't realize that that happened! I don't have time today, but I'll definitely run it tomorrow! – Nathan Merrill – 2018-03-13T21:26:58.213

@Draco18s ...This is going to take longer than I thought. I'm running 10K rounds: I started 5 hours ago, and I'm on round 3000. I'll have the scores up by tomorrow hopefully :) – Nathan Merrill – 2018-03-14T19:23:31.383

@NathanMerrill Dedication! Rivaled only by trichoplax and Formic Functions. ;) Glad its being done, I'd forgotten about it until I mentioned this challenge and noticed. – Draco18s no longer trusts SE – 2018-03-14T20:01:45.303

@Draco18s well, unfortunately, your bot didn't win. I'm not convinced that this ended up being a good KoTH: there were significant place changes, and I don't know whether it was the two new bots, or simply randomness. – Nathan Merrill – 2018-03-15T14:51:54.937

@NathanMerrill Yeah, a sufficiently long-calculation for KOTHs is always a problem. Formic Functs takes 2+ weeks to resolve first place. And that's after Dave went and made his KOTH framework which makes some speed optimizations (such as caching and webworkers). The official Formic Funct controller implemented the caching, speeding things up from "1-3 months" to "1-3 weeks" (before the addition of a new 500 ant bot, which slowed it down again). Trichoplax made a meta post about it not that long ago.

– Draco18s no longer trusts SE – 2018-03-15T17:24:33.723

Per this one and Dumb Bot: I never ran it with Hermit in the mix, so that might've changed things, or it was the randomness, or who knows. But thanks for giving it a bash! – Draco18s no longer trusts SE – 2018-03-15T17:25:02.723

Answers

4

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

MegaTom

Posted 2015-12-09T17:50:08.273

Reputation: 3 787

4

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

MegaTom

Posted 2015-12-09T17:50:08.273

Reputation: 3 787

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. – Draco18s no longer trusts SE – 2015-12-12T17:48:44.700

@draco18s this was not really ment to be a serious answer... – MegaTom – 2015-12-12T17:55:02.363

I know! That's why I'm so confused by it. XD – Draco18s no longer trusts SE – 2015-12-12T17:55:37.990

3

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

Nathan Merrill

Posted 2015-12-09T17:50:08.273

Reputation: 13 591

3

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.

TheNumberOne

Posted 2015-12-09T17:50:08.273

Reputation: 10 855

Technically he doesn't fit the specification, because he doesn't exactly return a String for his flag. – TheNumberOne – 2015-12-09T18:44:51.697

3null is a string. ;) Just a fancy string. – Addison Crump – 2015-12-09T18:45:49.377

This made me recognize a flaw in my spec, which has been specified: "all bots should be functionally different than existing entries" – Nathan Merrill – 2015-12-09T18:46:45.430

@NathanMerrill Fixed to more closely follow the spec. – TheNumberOne – 2015-12-09T18:49:26.967

3

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

LegionMammal978

Posted 2015-12-09T17:50:08.273

Reputation: 15 731

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. – Nathan Merrill – 2015-12-10T15:10:32.970

Oh, my bad, fixed. – Nathan Merrill – 2015-12-10T23:05:45.930

3

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

MegaTom

Posted 2015-12-09T17:50:08.273

Reputation: 3 787

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

– Nathan Merrill – 2015-12-10T17:21:31.973

Also, congrats on top spot! – Nathan Merrill – 2015-12-10T17:21:52.897

HelperBot isn't very smart. It only got top because all the other bots were dumb. :P This is probably the first effective bot. – Draco18s no longer trusts SE – 2015-12-10T17:42:21.473

1@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) – Nic Robertson – 2015-12-10T23:23:49.813

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. – Nathan Merrill – 2015-12-11T00:12:14.177

2

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.

Draco18s no longer trusts SE

Posted 2015-12-09T17:50:08.273

Reputation: 3 053

Awesome! It is intentional that you cannot create IPAddresses from strings. – Nathan Merrill – 2015-12-11T17:30:56.643

Well, ostensibly, yes! (Or bots will create arbitrary ones to find new bots). The problem is that if you do the simulation crashes. ;) – Draco18s no longer trusts SE – 2015-12-11T17:32:17.667

Urm, on line 143 you use a non-existing constructor. – TheNumberOne – 2015-12-11T17:55:33.927

@TheNumberOne it was valid when I wrote it. Nathan probably updated the base. – Draco18s no longer trusts SE – 2015-12-11T18:45:10.407

@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. – Draco18s no longer trusts SE – 2015-12-11T19:16:27.123

I now get compile errors on lines 143, 199, 347, 349, and 351. I would suggest specifying a return value for getIPAddress and adding a default return value in case trg == null or the string is not in your address book. – TheNumberOne – 2015-12-11T19:45:10.640

@TheNumberOne I'll have to fix it later, I have been doing editing in a raw text editor as I am away from Eclipse. – Draco18s no longer trusts SE – 2015-12-11T19:51:31.837

@TheNumberOne OK code correctly compiles now. – Draco18s no longer trusts SE – 2015-12-11T23:55:44.980

@NathanMerrill I fixed a logic error that was inadvertently doing the reverse of intended, drastically killing efficiency. Against any bot other than Amnesia, it reigns supreme now, up at 160-170 efficiency. – Draco18s no longer trusts SE – 2015-12-11T23:56:19.317

1

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

Nathan Merrill

Posted 2015-12-09T17:50:08.273

Reputation: 13 591

1

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.

Draco18s no longer trusts SE

Posted 2015-12-09T17:50:08.273

Reputation: 3 053

1

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

TheNumberOne

Posted 2015-12-09T17:50:08.273

Reputation: 10 855

1

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

TheNumberOne

Posted 2015-12-09T17:50:08.273

Reputation: 10 855

1

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.

Draco18s no longer trusts SE

Posted 2015-12-09T17:50:08.273

Reputation: 3 053

1

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

Draco18s no longer trusts SE

Posted 2015-12-09T17:50:08.273

Reputation: 3 053

0

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

MegaTom

Posted 2015-12-09T17:50:08.273

Reputation: 3 787