Survival Game - Create Your Wolf

229

126

The Board

The Board is a two dimensional array of cells. Cells are populated by Animals. Every day, all Animals on the Board simultaneously make one move. If two or more Animals move to the same cell, they fight until one remains. The possible moves and attacks are as follows:

  • Moves - { Move.UP, Move.RIGHT, Move.DOWN, Move.LEFT, Move.HOLD }
  • Attacks - { Attack.ROCK, Attack.PAPER, Attack.SCISSORS, Attack.SUICIDE }

Animals fight by playing Rock-Paper-Scissors. Standard rules apply but with two modifications. First, you may commit suicide at any time. Second, a tie is broken pseudorandomly. If more than two Animals collide, two are pseudorandomly selected to fight until one remains.

The Players

Animal behavior and appearance is as follows.

  • Lion
    • Represented by the character L. Moves DOWN, RIGHT, then repeats. Pseudorandomly attacks with PAPER or SCISSORS.
  • Bear
    • Represented by the character B. Moves DOWN x 4, RIGHT x 4, UP x 4, LEFT x 4, then repeats. Attacks with PAPER.
  • Stone
    • Represented by the character S. Moves HOLD. Attacks with ROCK.
  • Wolf
    • Only wolves that are submitted as an answer will be included. Represented by 'W'. Moves with any Move. Attacks with any Attack

You will implement the Wolf by filling in the blanks in the following template. All submissions must be in Java and must be contained in a single file. Alternatively, @ProgrammerDan has written a wrapper class that extends the competition to non-java submissions.

// Optional code here
public class Wolf extends Animal {
    // Optional code here
    public Wolf() { super('W'); /* Optional code here */ }
    public Attack fight(char opponent) { /* Required code here. Must return an Attack. */ }
    public Move move() { /* Required code here. Must return a Move. */ }
    // Optional code here
}

The submission with the highest average number of living wolves after five trials with 1,000 iterations wins. I will update the winner each time a new answer is posted (but not within the first 24 hours).

Tools

  • You are provided with a small map of your immediate surroundings in the following form.
    • char[][] surroundings A zero indexed, 3 by 3 matrix of characters that represent nearby animals. Empty tiles are represented by a space character (' '). You are at surroundings[1][1]. For example, to your right would be surroundings[1][2], and above you is surroundings[0][1]. Your surroundings are updated just before being asked to move, but may be out of date when asked to fight.
  • You may persist data between invocations of your Wolf, Move requests, and Attack requests. You may neither read from nor modify files created by another Wolf class.
  • You are provided with the size of the map in the following form
    • int MAP_SIZE

Assumptions

  • All submission will compete on the same board against all other submissions as well as Lions, Bears, and Stones
  • The board is a square with sides of length sqrt(n+3)*20 where n is the number of submissions. All sides wrap, so you can safely move in any direction infinitely.
  • Simulation begins at ~25% board capacity with 100 of each Animal pseudorandomly distributed across the board.
  • If an Animal throws an exception when asked to move, that Animal will HOLD
  • If an Animal throws an exception when asked to fight, that Animal will SUICIDE
  • If an Animal has no letter when the controller checks, that Animal will immediately die

Getting Started and Testing Instructions

The tools you need to test your submission can be found here. The following files should be available at that link.

  • ExampleRun.gif - A 5-10 second gif of the program running.
  • Scoreboard - The latest results of the competition.
  • Wild.jar - An executable that you can run to watch the premade Animals run around.
  • Wild.zip - The NetBeans project that houses the controller program with a few premade Animals added. Use this to develop your submission.
  • WildPopulated.zip - Same as above, but with and about 40 submissions added for you to test against. The GUI has also been removed because of performance issues. The results will simply appear a few moments after you run. The non-Java submissions are commented out because they require extra downloads and effort. Use this to test your submission against the field.
  • Wolf.txt - A naive implementation of the Wolf class in Java. You may use and expand that implementation without crediting me.

To test your class:

  • Download Wild.zip
  • Rename your Wolf.java and Wolf class to something unique
  • Add your UniquelyNamedWolf.java file to Wild\src\animals\
  • In the Wild class, add your class to classes array, like this.
    • Class[] classes = { UniquelyNamedWolf.class, Bear.class, Lion.class, Stone.class, Wolf.class };
  • Rebuild and run

I also included another project containing all of the submissions, for testing purposes. I creatively named it "WildPopulated". The three or four non-Java wolves were commented out, because getting them to run requires a lot of extra work and downloads. The one I posted should run with zero extra work. The GUI was also commented out for the sake of speed.

Scoreboard April 22, 2014

The Scoreboard has been moved to Google Drive. View it here. It will be updated on a casual basis (i.e., when I get to it) as new submissions come in.

Side Challenge - NoHOLD

Removed because no (as in zero) participation. It wasn't part of the original challenge anyway.

Rainbolt

Posted 2014-04-04T18:47:50.283

Reputation: 3 500

Am I allowed to name my wolf "Wolf"? – TheNumberOne – 2015-01-08T18:30:38.287

2@TheBestOne Of course. Everyone can name their wolf "Wolf" if they want to. – Rainbolt – 2015-01-08T18:55:04.130

Does this run on my computer? So can I use my gpu-accelerated simulator for my wolf to foresee actions quicker? – huseyin tugrul buyukisik – 2015-09-05T20:00:35.523

1@huseyintugrulbuyukisik Read the section labelled "Getting Started and Testing Instructions" and try to follow the instructions. If you get stuck, then I will be more than happy to help you get the project running on your computer. – Rainbolt – 2015-09-08T13:00:11.843

How mannered animals they are! They don't believe in violence. :D – Arjun – 2016-05-23T05:13:29.357

So what happens if move() returns null? – RamenChef – 2017-10-26T14:09:14.423

@RamenChef You get disqualified for not following instructions. See the requirement "Must return a Move." Unfortunately, null is not a Move. Here's proof.

– Rainbolt – 2017-10-26T15:50:26.947

48+1, great challenge. Too bad it's limited to Java. :-/ – Martin Ender – 2014-04-04T19:00:20.347

When you say "simulation begins ... with 100 of each animal"... does that mean 100 per "breed" of wolf? – Martin Ender – 2014-04-04T19:03:10.000

@m.buettner Correct. Simulation starts with 100 of each Wolf submission (breed), 100 Stones, 100 Lions, and 100 Bears. – Rainbolt – 2014-04-04T19:04:43.893

1@m.buettner I'm tempted to write a wrapper that would allow non-java submissions, if Rusher doesn't forbid me from doing so :D – ProgrammerDan – 2014-04-04T19:13:43.553

@ProgrammerDan If it does not cripple the controller or force the tester (me) to download a compiler/interpreter for every submission, I will add your wrapper, and then community wiki the challenge so that I gain no more rep from it. I would be happy to see it work. – Rainbolt – 2014-04-04T19:25:21.710

5@Rusher That sounds terrible. I could just community wiki my answer that contains the Wrapper. As for the compiler/interpreter issue, I suppose it'd be up to the submittee to provide you with clear instructions on usage, and if the instructions where unclear or too complex, you could reject the submission :) – ProgrammerDan – 2014-04-04T19:27:43.300

4"* You are provided with the size of the map in the following form: int MAP_SIZE" I'm having trouble figuring out how to use this. Netbeans says there's no instance of the string MAP_SIZE in any of the files in the project. – undergroundmonorail – 2014-04-04T21:53:50.200

1

To help testing, I modified your Game.toString() to use a StringBuilder instead of countless concats. It speeds things up dramatically(I also took out the sleep() in my copy). The mod is here on pastebin.

– Geobits – 2014-04-05T04:16:00.010

6how about trees showing paper? – Mukul Kumar – 2014-04-05T05:32:38.710

2@m.buettner wrapper posted, ping me with questions on how to use, but if your language/interpreter of choice supports piped inputs and outputs, you are good to go. – ProgrammerDan – 2014-04-05T06:27:54.063

@ProgrammerDan awesome! I think if I have to set up javac on my machine anyway, I might just write an answer in Java right away, though. But it's certainly helpful for people who don't know any Java at all. – Martin Ender – 2014-04-05T09:34:28.623

3@Rusher, I think some people are doing this already, but is inter-Wolf communication allowed via static members (within your own breed)? – Martin Ender – 2014-04-05T09:35:55.687

10@m.buettner Allowed. Go forth and build your hive-minded wolf. – Rainbolt – 2014-04-05T15:02:51.333

@ProgrammerDan MAP_SIZE bug fixed. All wolves should be able to access it now. It is inherited from Animal. Download the new controller from the same link in the question. – Rainbolt – 2014-04-05T17:15:40.383

Ah, the nostalgia of intro programming courses – geoff – 2014-04-05T22:46:06.170

When is the deadline for entering? – Kevin – 2014-04-06T04:34:02.517

1One more thing, just to clarify: if animals move through each other (i.e. swap places) there is no fight? – Martin Ender – 2014-04-06T08:18:56.137

3@Kevin No foreseeable deadline. I will update this post with results as I learn how to compile them (I'm currently learning how to run an R program). If, in a few months, I get tired of updating it, I'll just slap a timer on it. – Rainbolt – 2014-04-06T15:49:26.750

@m.buettner If they swap places, there is no fight. In other words, you don't "collide" halfway between cells. – Rainbolt – 2014-04-06T15:51:11.097

2Wow this is awesome. I sense a whole new breed of computer gaming here haha. – Andrew Gies – 2014-04-06T20:04:49.023

If you want players to be able to run the program themselves to see the winner, you could ensure that everyone is using the same random number generator (in a deterministic order) and promise to seed it with a future random number.

– Jeremy Stein – 2014-04-07T20:01:13.630

Does this need JDK 8? – user3188175 – 2014-04-07T20:44:54.377

What is the syntax for the surrounding char[][] array? By that I mean, assuming my character is in position 1,1, does Move.UP imply that I will move to position 0,1 or 1,0 or 1,2 or 2,1? It might be nice if this was explicitly stated. – Sheph – 2014-04-07T20:58:23.893

@Sheph Good question. I edited the "Tools" section to reflect your orientation on the board. – Rainbolt – 2014-04-07T21:13:50.277

@user3188175 Yes. I messed up and included one line of code that requires Java 8. I blame the ever changing ArrayList syntax! – Rainbolt – 2014-04-07T21:16:43.220

Is there a good graphical representation of the playing field that I'm just not seeing, or do I have to write one myself? (e.g. johnchen902's display) – apnorton – 2014-04-07T23:50:42.357

2@anorton The default testing program includes a graphical output when run. It doesn't look exactly like johnchen902's, but it shows the position/species of each creature. Get it from the link in the OP's "Testing Instructions" section. – Geobits – 2014-04-08T01:23:49.757

@anorton It looks like johnchen902 modified the display to show his wolves in another color. Mine doesn't do that, but it should be a pretty easy tweak. – Rainbolt – 2014-04-08T03:35:43.187

Sort of expected this, installed NetBeans on Debian, downloaded the project, at first NetBeans refused to open it, I deleted nbproject directory like some people advised, now running the project is impossible. Ideas? – Llamageddon – 2014-04-08T09:06:35.727

1

@Asmageddon First, do you have JDK 8? If not, download it. If so, follow the instructions at this link to point NetBeans to the right Java platform. http://stackoverflow.com/a/6951067/3224483

– Rainbolt – 2014-04-08T13:01:13.253

@Asmageddon Second, if nothing else works, just create a new project and add the .java source files yourself to \yourproject\src\. I have mine separated into two folders -> \Wild\src\animals and \Wild\src\wild. You could even copy and paste the code from the .java files. – Rainbolt – 2014-04-08T13:07:12.337

"Simulation begins at ~25% board capacity with 100 of each Animal pseudorandomly distributed across the board." Is the "~25%" part of that an artifact of old design? Although it may be a close estimate for certain numbers of species, it isn't for others, and just seems to add unnecessary (mis?)information. – Runer112 – 2014-04-08T13:07:57.823

@Runer112 Please provide a number of submissions where you think the math does not work out, and then I'll be able to help you. – Rainbolt – 2014-04-08T13:18:41.377

I notice that most of the wolfs fights ended to be psadorandom as there is more chances that it will be even, than victory. I suggest to edit the rules to include 3 breaking even attempts before using psadorandom victory, how ever it will make the lion much weaker so apply this rule only on wolfs fights. – Ilya_Gazman – 2014-04-08T13:33:06.813

@Ilya_Gazman I appreciate the suggestion, but I generally can't edit the rules after posting the challenge in a way that would affect answers already submitted. I can only add clarifications to the already existing rules (and hope I don't piss anyone off). – Rainbolt – 2014-04-08T13:40:23.617

@Rusher It seems you probably won't have to worry about it with the number of submissions coming in, but for numbers of submissions less than about 15, it would't be a great estimate, getting progressively worse with fewer submissions. – Runer112 – 2014-04-08T16:09:34.790

@Runer112 Since you say it gets progressively worse close as it gets lower, we'll take n=1 submissions. sqrt(1+3)20 gives us a 4040 board size. That's 1600 cells. Fill 1600 cells with 100 each Wolves, Bears, Lions, and Stones, and that makes 400 Animals. 400 is exactly 25% of 1600. Where exactly did the math go wrong? – Rainbolt – 2014-04-08T16:21:30.687

@Rusher Well, that was a math failure on my part. Ignore that completely, then. :) – Runer112 – 2014-04-08T16:24:27.673

1@Rusher Also, a slight concern. Right now, animal surroundings are undated immediately before that animal moves, meaning that the surroundings reported will reflect the new positions of animals that have already moved that tick. However, this behavior means that there may be multiple animals on one spot, and (I think?) you resolve this by choosing to show the animal that was there first. Is this ideal? If not, one way to possibly improve it might be to modify the choosing logic to choose the "most dangerous" animal. Another way might be to update all surroundings, and only then move animals. – Runer112 – 2014-04-08T16:25:06.353

1@Runer112 Surroundings reported reflect the board as it was before ANYONE moved. I make a brand new game board every iteration, but I pull surroundings from the old board. After every move has been made, I just replace the old board with the new one, and then collapse the cells. I even took care of the cases where 3 or 4 Animals land on the same cell. It wouldn't be fair if the first Animal had to fight everyone, so fighters are selected randomly until only one remains. – Rainbolt – 2014-04-08T16:27:15.843

1@Rusher It sounds like I'm just being really stupid today... All my doubts about your design have been ill-founded. Looks like your design has been great all along! And I've been having a lot of fun trying to make a decent wolf for myself. – Runer112 – 2014-04-08T16:30:30.793

1

@Runer112 As long as you are pointing out flaws in good faith, don't be discouraged from challenging the design further. It obviously isn't perfect. The particular concern you came up with is even related to a bug of one of the biggest Java games of all time - http://minecraft.gamepedia.com/South-East_rule

– Rainbolt – 2014-04-08T16:37:23.357

3@Rusher I've got you this time! If both animals in a fight choose to commit suicide, I would think both shouhd die. But currently it's classified as a tie and only one randomly chosen one dies. :P – Runer112 – 2014-04-08T18:54:53.807

1@Runer112 I had to dig into my code before finding out that you are correct! I'll fix it (although I doubt it will change much for the suicidal wolves), add the newest submissions, upload the updated project files, and post new results sometime in the next couple of days. – Rainbolt – 2014-04-08T19:04:36.623

1As a helper to those confused. If given an enum Cell with the following members (in order){TOP_LEFT,TOP,TOP_RIGHT,LEFT,MID,RIGHT,BOT_LEFT,BOT,_BOT_RIGHT;}, you can derive the character of a given position using the enum with the function: char get(Cell c) {int x = c.ordinal()%3; int y = c.ordinal()/3; return surroundings[y][x];} – Sheph – 2014-04-08T19:57:50.203

@Rusher Updated Stone Eating Wolf. Please use updated version :) – Averroes – 2014-04-08T21:36:39.327

@Rusher Updated MimicWolf, I should be able to double my max with this update ^^ – Sahar Rabinoviz – 2014-04-09T00:31:00.837

3It seems that the surviving wolves are the ones that avoid encounters with other wolves and lions. I think it would be great to do some other king of the hill challenge similar to this but adding sheeps (that can breed if the encounter instead of kill themselves) and wolves that actual have to hunt them down to survive. Wolves that don't hunt starve and die. – Averroes – 2014-04-09T10:48:43.263

@Rusher So, you're not including CamoWolf? -_- It's not against the rules, and you can't just say "I don't like this one so I'm not including it"... – Doorknob – 2014-04-09T12:58:40.213

2

@Averroes I'm working on something like that, but will be waiting until the current competitions die down some before posting.

– Geobits – 2014-04-09T13:04:03.093

2I am so starting on my wolf once I get home. I'm thinking of a Wolf that instinctually runs away from everything, unless someone's already done that. – Zibbobz – 2014-04-09T14:46:25.607

@Geobits I would suggest keeping the rules very simple like this competition as that sparks creativity. However one change I would like that would have made the wolves behaviour more interesting is to have a concept of a "health" per animal so that attacking can be strategic and not a thing to avoid as it is in this competition. – Moogie – 2014-04-09T20:54:31.110

2@Moogie The competition is what it is, and changing it invalidate the 32 submissions already posted. Let's just say we learned how to make an even better challenge next time. Also, welcome to Code Golf! – Rainbolt – 2014-04-09T20:57:46.003

@Rusher Thanks for the welcome :) Please do not take offence at my comment. I have had lots of fun! – Moogie – 2014-04-09T21:21:57.880

Here are evaluations of the Java wolves when we increase the number of lions: http://chat.stackexchange.com/transcript/message/14837457#14837457

– Justin – 2014-04-10T07:45:59.097

@Rusher StoneEatingWolf updated – Averroes – 2014-04-10T16:28:42.180

Why are there two "MimicWolf" in the Google Drive scoreboard? – justhalf – 2014-04-11T04:47:25.237

Unnoficial codegolf score: Surviving rate / Bytes of code – Averroes – 2014-04-11T07:23:27.353

Your explanation of the surroundings matrix doesn't make any sense. If it is a zero indexed 3x3 matrix, how can it have indices larger than 2? – Tim Seguine – 2014-04-11T14:10:16.747

3@TimSeguine The way Stack Exchange identifies links is with constructs like [1], [2], [3], etc. When I edited the links in my post, it also changed my indices. I think it's a bug. Anyway, I fixed it. – Rainbolt – 2014-04-11T14:15:15.733

Would it be possible to put the version you are using to generate the scoreboard on the google drive (i.e. the one with all of the current wolf classes)? – Tim Seguine – 2014-04-11T15:25:20.780

@TimSeguine Sure, when I get around to it. I have a MTG tournament this weekend so possibly Sunday or Monday evening? Anyway, to even run it you'll have to download Mono, Rscript, Node.js, compile some stuff by hand, and then cross your fingers and hope it works. I might also post a version that only includes Java wolves that you can simply download and run, and a version that has colors if the author allows me to steal his code. I'm also going to increase the number of trials and include some actual statistics in the spreadsheet. – Rainbolt – 2014-04-11T15:49:11.557

1Okay, sounds good. I just figured since you already did the work of incorporating ~40 wolves into the project, it would make it easier for people to test without having to duplicate that work. – Tim Seguine – 2014-04-11T16:10:10.713

@Rusher, ETA for scoreboard update? Can you make a new sheet with the date so we can track changes over time? Still packaging those wolves and game update? – MatthewMMorrow – 2014-04-22T20:27:46.697

1@MatthewMMorrow Tonight. Will package the java only wolves (the rest can be added manually and all require additional downloads anyway). Will create a new sheet as requested. – Rainbolt – 2014-04-22T22:35:02.847

2@Rusher Since the most successful wolves rely on HOLD, I think it would be pretty interesting to take it out, maybe as a side challenge? – Trent – 2014-04-26T09:00:28.267

@FizzBuzz I added your suggestion for a side challenge to the bottom of the post. – Rainbolt – 2014-04-28T13:59:03.940

@Rusher Surprisingly harder than expected. I thought my algorithm was decent enough that I could just remove HOLD and be good to go...no such luck, it performs terribly! – Trent – 2014-04-29T07:53:11.690

I'd really like to participate and build an epic collaborative wolf, but Java file io intimidates me ;_; – cjfaure – 2014-05-07T09:14:03.040

@Trimsty It's intimidating at first, but then you realize that you can hook a Scanner to a File, a String, a Stream, or to STDIN and it becomes easy. And you construct a File like File f = new File(path);. Easy right? – Rainbolt – 2014-05-07T13:05:38.000

@Rusher Oooohhhh. I like Python's more though xD – cjfaure – 2014-05-07T13:38:29.620

Hey, my wolf is no-hold capable. You only need to comment hold line in movemap. – mleko – 2014-05-13T17:15:46.470

Answers

45

HerjanWolf

Updated 10-4-2014 at 15:00

Averages of 100 rounds, 1000 iterations:

Standard mobs:

class animals.Bear - 2.2600002
class animals.Lion - 41.21
class animals.Stone - 20.159998
class animals.HerjanWolf - 99.99 <-- kind of flawless

20+ Species (Keep in mind, don't trust these scores since we keep calculating avgs until our wolves score best!)

class animals.Bear - 0.1
class animals.Lion - 0.0
class animals.Stone - 1.5
class animals.AlphaWolf - 75.5
class animals.HerjanWolf - 86.4 <-- #1
class animals.GatheringWolf - 39.5
class animals.OmegaWolf - 85.4 <-- #2
class animals.ShadowWolf - 71.1
class animals.MOSHPITFRENZYWolf - 8.8
class animals.WolfWithoutFear - 11.5
class animals.MimicWolf - 0.5
class animals.LazyWolf - 52.8
class animals.Sheep - 38.3
class animals.HonorWolf - 80.7
class animals.CamperWolf - 52.8
class animals.GamblerWolf - 14.7
class animals.WolfRunningWithScissors - 0.0
class animals.LionHunterWolf - 27.6
class animals.StoneEatingWolf - 70.8
class animals.Wion - 0.1
class animals.ProAlpha - 79.3
class animals.HybridWolf - 83.2

My Wolf:

package animals;

public class HerjanWolf extends Animal {

    private boolean lionTopLeft = false, lionTopLeftReady = false;
    private boolean lionRight = false, lionRightReady = false;
    private boolean lionBot = false, lionBotReady = false;
    private boolean lionDanger = false, careful = true, firstmove = true;
    private final int hold = 0, down = 1, right = 2, left = 3, up = 4;

    public HerjanWolf() {
        super('W');
    }

    public Attack fight(char c){
        switch (c) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                int rand = (int) (Math.random()*3);
                if(rand < 1)
                    return Attack.PAPER;
                else if(rand < 2)
                    return Attack.SCISSORS;
                else
                    return Attack.ROCK;
        } 

    }
    public Move move() { //surroundings[y][x]

        checkLions();

        if(firstmove){
            if(surroundings[2][0] == 'L')
                lionBotReady = true;
            if(surroundings[0][2] == 'L')
                lionRightReady = true;
            firstmove = false;
        }

        int[] dang = new int[4]; // 0 is left side, 1 is top side, 2 is right side, 3 is down side

        for(int y = 0; y < 3; y++){
            for(int x = 0; x < 3; x++){
                if(surroundings[y][x] == 'W'){
                    if(y == 0){
                        dang[1]++;
                        if(x == 1)
                            dang[1]+=2;
                    }if(y == 2){
                        dang[3]++;
                        if(x == 1)
                            dang[3]+=2;
                    }if(x == 0){
                        dang[0]++;
                        if(y == 1)
                            dang[0]+=2;
                    }if(x == 2){
                        dang[2]++;
                        if(y == 1)
                            dang[2]+=2;
                    }
                }
            }
        }

        int maxIndex = 0, minIndex = 0, minIndex2 = 0;
        for(int i = 1; i < dang.length; i++){
            if(dang[i] > dang[maxIndex])
                maxIndex = i;
            if(dang[i] <= dang[minIndex]){
                minIndex2 = minIndex;
                minIndex = i;
            }
        }

        if(lionDanger || surroundings[1][0] == 'L' && lionTopLeftReady || surroundings[0][1] == 'L' && lionTopLeftReady || dang[maxIndex] >= 3){

            switch(minIndex){
            case 0:
                if (isSafe(1, 0)){
                    newMove(left);
                    return Move.LEFT;
                }
            case 1:
                if (isSafe(0, 1)){
                    newMove(up);
                    return Move.UP;
                }
            case 2:
                if(isSafe(1,2)){
                    newMove(right);
                    return Move.RIGHT;
                }

            case 3:
                if (isSafe(2, 1)){
                    newMove(down);
                    return Move.DOWN;
                } 
            }

            switch(minIndex2){
            case 0:
                if (isSafe(1, 0)){
                    newMove(left);
                    return Move.LEFT;
                }
            case 1:
                if (isSafe(0, 1)){
                    newMove(up);
                    return Move.UP;
                }
            case 2:
                if(isSafe(1,2)){
                    newMove(right);
                    return Move.RIGHT;
                }

            case 3:
                if (isSafe(2, 1)){
                    newMove(down);
                    return Move.DOWN;
                } 
            }

            if(dang[maxIndex]<3){ //if that was not the reason its really obligated (because of lions)
                if (isSafe(2, 1)){
                    newMove(down);
                    return Move.DOWN;
                }else if(isSafe(1,2)){
                    newMove(right);
                    return Move.RIGHT;
                }else if (isSafe(0, 1)){
                    newMove(up);
                    return Move.UP;
                }else{
                    newMove(left);
                    return Move.LEFT;
                }
            }
        }

        return Move.HOLD;
    }

    boolean isSafe(int y, int x){
        if(y <= 1){
            if(x <= 1){
                if(surroundings[y][x] != 'W' && !lionTopLeft)
                    return true;
            }else if(surroundings[1][2] != 'W' && !lionRightReady)
                    return true;
        }else if(surroundings[2][1] != 'W' && !lionBotReady)
            return true;

        return false;
    }

    public void checkLions(){
        int y = 0, x = 0;

        if(lionTopLeft)
            lionTopLeftReady = true;
        else
            lionTopLeftReady = false;

        if(surroundings[y][x] == 'L')
            lionTopLeft = true;
        else
            lionTopLeft = false;

        if(lionRight)
            lionRightReady = true;
        else
            lionRightReady = false;

        if(surroundings[y][x+1] == 'L') // && !lionTopLeftReady
            lionRight = true;
        else
            lionRight = false;

        if(lionBot)
            lionBotReady = true;
        else
            lionBotReady = false;

        if(surroundings[y+1][x] == 'L' && !lionTopLeftReady)
            lionBot = true;
        else
            lionBot = false;

        if(careful){
            if(surroundings[y+1][x] == 'L'){
                lionDanger = true;
            }else if(surroundings[y][x+1] == 'L'){
                lionDanger = true;
            }

            careful = false;
        }
    }

    public void newMove(int move){
        lionTopLeft = false;
        lionRight = false;
        lionBot = false;

        lionTopLeftReady = false;
        lionRightReady = false;
        lionBotReady = false;

        lionDanger = false;

        if(move == down){
            if(surroundings[1][0] == 'L')
                lionTopLeft = true;
            if(surroundings[2][0] == 'L')
                lionBot = true;

        }else if(move == right){
            if(surroundings[0][1] == 'L')
                lionTopLeft = true;
            if(surroundings[0][2] == 'L')
                lionRight = true;

        }else
            careful = true;
    }
}

Herjan

Posted 2014-04-04T18:47:50.283

Reputation: 1 127

@Herjan I didn't initially understand for whatever reason that it was a wolf free for all. I honestly don't know how I thought the scoring worked under that assumption. You made the same key observation that there is always at least one lion safe move. – Tim Seguine – 2017-12-17T16:03:34.980

Including only the "top" wolves in test runs normally skews the numbers. With 30+ species on the field, the numbers can change dramatically. If you haven't yet, I'd recommend testing that also. – Geobits – 2014-04-09T18:30:22.280

With nearly all the wolves and 1000 iterations, this wolf averages ~55. – user3188175 – 2014-04-09T19:02:38.573

@user3188175 You're right, I just edited it with more wolves, as Geobits suggested. Sad results :( It was fun though. – Herjan – 2014-04-09T19:04:21.597

1@user20220 It is pretty good in theory(and in practice with relatively less wolves) though. – user3188175 – 2014-04-09T19:07:05.857

@user3188175 Fixed a problem works lots and lots better in large groups now – Herjan – 2014-04-09T20:07:07.060

@user20220 It does seem to be much better, running a simulation right now. – user3188175 – 2014-04-09T20:10:14.827

@user20220 Nice! It shares the 1st place with HonorWolf(88 survival rate). – user3188175 – 2014-04-09T20:29:19.453

@user3188175 Yeah I tweaked it again lol, now the avg is 2 units better. It's so much fun! – Herjan – 2014-04-09T20:29:28.737

Could you explain what your wolf does? – plannapus – 2014-04-10T06:09:40.843

1

This wolf has the best anti-lion strategy: http://chat.stackexchange.com/transcript/message/14837616#14837616

– Justin – 2014-04-10T07:35:10.597

@Quincunx Ah, that feels good :) I originally designed it to make it lionproof and when that was 100% done I programmed interaction with other wolves, but that was at cost of my anti-lion strategy. But I'm honored ;) – Herjan – 2014-04-10T12:19:52.350

@plannapus Yes, what I do is: check the topleft corner [0][0] so I know there is a lion coming next turn. What I also do is check [1][0] so if there is a lion (if it was not in the topleft corner) I know it is going to [2][0] (down) and after that to [2][1] (right) (and with the right side I do the same [0][1] to [0][2] to [1][2]). So if the lion is going [2][1] HerjanWolf will be sure to not go down. – Herjan – 2014-04-10T12:31:00.617

2@user20220 Wolf-collision happens much more frequently, so you should add another wolf handling that. Also, can you please describe how this wolf works? – Justin – 2014-04-10T13:20:02.267

@Quincunx Yes I know I programmed interaction with other wolves later as I stated above, but since I first programmed a 100% lionproof wolf it works good with lots of lions. I just answered how the anti-lion strategy works, the wolf handling isn't that spectacular, if there is a wolf next to you it tries to move to the direction with the least amount of wolves. – Herjan – 2014-04-10T13:45:12.687

@Quincunx: I think the best anti-lion strategy per se is Wion, since it will never encounter any lion. Although it can't be adapted to anything else, unlike this one, which can be adapted to avoid wolves also. – justhalf – 2014-04-15T06:19:49.197

@justhalf If you take a look at my data, you can see that HerjanWolf does better with more lions. This is because Wion runs into the other Wolves that are avoiding lions. – Justin – 2014-04-15T06:24:01.767

Yap, if you consider other wolves, of course, Wion is bad, like I said. My point is, if the only aim is to avoid lion, Wion has the best strategy. – justhalf – 2014-04-15T06:29:10.223

@justhalf Certainly not, my wolf dodges lions better than the Wion, my wolf does way more than only avoiding lions, but if it has no choice it moves automagically the same way lions do, but better than Wion, since my wolves keep track of all lions in sight and so knows which moves they are going to make. My previous wolf, which had not any wolf-dodging built in at all was 100% lionproof. But ofcourse you can test and see it with your own eyes, btw here is still the source of my old wolf: http://www.java-gaming.org/topics/ai-challenge-on-stackexchange-right-now-create-your-wolf/32708/view.html

– Herjan – 2014-04-16T16:57:42.737

@Herjan: How can one be better in avoiding lions compared to wolves that do not collide with lions at all? Yours is not better in terms of avoiding lions, it is better since it also has the wolf-dodging capability. The lion-avoiding capability is the same, which is the maximum (i.e., doesn't collide with lions at all) – justhalf – 2014-04-17T01:38:24.340

3@justhalf Okay, it's not better, it's just as good then. My wolf dodges all lions as well, I first made my wolf 100% lionproof before I built the wolf-dodging. The difference is that my wolf tries to escape lions (the less movement, the better). So it's just as good at lion-dodging as the Wion, both 100% lionproof, but since my wolf tries to escape lions, and not boringly moves the same as a lion I can still build wolf-dodging in as well, it's just way more complex. – Herjan – 2014-04-17T08:43:28.980

About to post the new Scoreboard in minutes. You won this week. – Rainbolt – 2014-04-23T03:57:24.843

136

EmoWolf

EmoWolf hates Java, and would rather kill itself than participate. EmoWolf has starved itself, but still weighs 177 bytes.

package animals;public class EmoWolf extends Animal{public EmoWolf(){super('W');}public Attack fight(char opponent){return Attack.SUICIDE;}public Move move(){return Move.HOLD;}}

boothby

Posted 2014-04-04T18:47:50.283

Reputation: 7 640

26lol You can shave off some bytes: "If an Animal has no letter when the controller checks, that Animal will immediately die". – Geobits – 2014-04-04T21:26:40.407

3YES. Thank you for this, I was hoping for a suicide Wolf. – ProgrammerDan – 2014-04-05T00:26:03.393

2You can shave off more bytes by renaming parameter opponent. – johnchen902 – 2014-04-05T10:29:36.870

98The best thing about this is that it's consistently not the worst. It normally beats at least 2-3 others. – Geobits – 2014-04-07T13:43:33.963

46"The only winning move is to not play." – tadman – 2014-04-08T04:02:39.360

33+1 For being small and suicidal and still beating 4 other wolves. – puggsoy – 2014-04-09T01:32:48.507

24Thanks for the suggestions, folks. If this was golf, I would bother to edit the post. As it stands, I will let EmoWolf sulk. Alone. – boothby – 2014-04-09T03:49:53.143

13@awashburn EmoWolf waits until it has a spectator before it suicides. I thought about making it mope around in a circle... but that is way too much effort. – boothby – 2014-04-13T18:35:18.960

6

And apparently this is on track to become a meme.

– Isiah Meadows – 2014-09-02T00:17:56.617

51

LazyWolf

Aptly named, this guy does the bare minimum to survive. The only non-wolf threat is a lion, so he will move if one of those is about to step on him. Other than that, he just sleeps.

There's not much you can do against wolves that will be better than 50/50, so he just doesn't do anything. If a wolf attacks him, he chooses an attack in an evenly distributed manner.

That's it. I expect it to do pretty well, despite the simplicity.

package animals;    
public class LazyWolf extends Animal{    
    static int last = 0;
    static final Attack[] attacks = Attack.values();

    public LazyWolf() {super('W');}

    @Override
    public Attack fight(char other) {
        switch(other){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.ROCK; // faker!
        default:
            return attacks[last++%3];
        }
    }

    @Override
    public Move move() {
        if(surroundings[0][1] == 'L')
            return Move.LEFT;
        if(surroundings[1][0] == 'L')
            return Move.UP;
        return Move.HOLD;
    }

}

Update:

CamoWolf was beating me handily. Since my wolf is so lazy, he'll never normally run into a real stone. Therefore, if a stone attacks, it's obviously a fake and needs a rock thrown in its face.

Geobits

Posted 2014-04-04T18:47:50.283

Reputation: 16 001

Right now you have the possibility of moving into a second lion when you try to avoid one. You could remove this by tracking how many moves there have been, so you know if the lion is actually coming to your square. If it is, move the same direction as the lion, and no lion can get to you because they are also moving the same direction. – ughoavgfhw – 2014-04-05T02:17:15.417

13I thought about the two lion situation, but decided that my wolf is just too lazy to care. The odds of it happening often enough with a well-populated board are pretty slim anyway. – Geobits – 2014-04-05T02:52:58.897

I've updated CamoWolf to try not to stomp on wolves too. Now your update is useless! :D – Doorknob – 2014-04-06T17:34:40.870

1Why do you think this wolf never runs into real stones? I’d think it can happen whenever it runs away from a lion. – Christopher Creutzig – 2014-04-07T15:48:42.170

6@ChristopherCreutzig It runs from lions by moving to where the lion was. If a lion was there, a stone can't be there now. – Geobits – 2014-04-07T15:50:02.840

True, I had looked at the 0 and 1 the wring way round. – Christopher Creutzig – 2014-04-07T16:38:04.450

1Even if it could, there's no "infinite loop". If an attack is a tie, one or the other dies(picked by coin toss) per the spec. – Geobits – 2014-04-09T17:47:14.970

48

Wrapper for Non-Java Submissions

NOTE MAP_SIZE support has been added. If you care, please update your submission accordingly.

This is community wiki entry for a wrapper, usable by those who want to play but don't like/don't know Java. Please use it, have fun, and I'm happy to help you get things set up.

It's pretty late here as I'm finishing up, so other Java coders, please look this over and suggest improvements. If you can, do so via my github repository by filing an issue or submitting a patch. Thanks!

This entire is being distributed with the UNLICENSE, please follow/fork it from its github repository. Submit patches there if you find issues and I'll update this post.

Current Examples of Wrapper in Use

plannapus: WolfCollectiveMemory in R

user3188175: SmartWolf in C#

toothbrush: Toothbrush in ECMAScript

How to use

What follows are instructions on the protocol for-inter process communication via PIPES I have defined for remote Wolves. Note I've skipped MAP_SIZE as this doesn't appear to exist, in spite of its presence in OP's problem statement. If it does appear, I'll update this post.

IMPORTANT NOTES:

  • Only a single invocation of your external process will be made (so wrap your processing logic in an infinite loop. This also lets you keep any processing in-memory, instead of using disk)
  • All communication is to this single external process via STDIN and STDOUT
  • You must explicitly flush all output sent to STDOUT, and make sure it is newline-terminated

Specification

Remote scripts are supported by a simple protocol via STDIN and STDOUT hooks, and is split into initialization, Move, and Attack. In each case communication with your process will be via STDIN, and a reply is necessary from STDOUT. If a reply is not received in 1 second, your process will be assumed to be dead and an exception will be thrown. All characters will be encoded in UTF-8, for consistency. Every input will terminate with a newline character, and your process should terminate every output reply with a newline as well. WARNING Be sure to flush your output buffer after every write, to ensure the Java wrapper sees your output. Failure to flush may cause your remote Wolf to fail.

Note that only a single process will be created, all Wolves must be managed within that one process. Read on for how this spec will help.

Initialization

STDIN: S<id><mapsize>\n

STDOUT: K<id>\n

<id>: 00 or 01 or ... or 99

Explanation:

The character S will be sent followed by two numeric characters 00, 01, ..., 99 indicating which of the 100 wolves is being initialized. In all future communication with that specific wolf, the same <id> will be used.

Following the ID, a variable length sequence of numeric characters will be be sent. This is the size of the map. You'll know the sequence of numeric characters is over when you reach the newline (\n).

To ensure your process is alive, you must reply with the character K followed by the same <id> you received. Any other reply will result in an exception, killing your wolves.

Movement

STDIN: M<id><C0><C1>...<C7><C8>\n

STDOUT: <mv><id>\n

<Cn>: W or or B or S or L

W: Wolf

: Empty Space

B: Bear

S: Stone

L: Lion

<mv>: H or U or L or R or D

H: Move.HOLD

U: Move.UP

L: Move.LEFT

R: Move.RIGHT

D: Move.DOWN

Explanation:

The character M will be sent followed by the two character <id> to indicate which Wolf needs to choose a move. Following that, 9 characters will be sent representing that Wolf's surroundings, in row order (top row, middle row, bottom row from leftmost to rightmost).

Reply with one of the valid movement characters <mv>, followed by the Wolf's two digit <id> for confirmation.

Attack

STDIN: A<id><C>\n

STDOUT: <atk><id>\n

<C>: W or B or S or L

<atk>: R or P or S or D

R: Attack.ROCK

P: Attack.PAPER

S: Attack.SCISSORS

D: Attack.SUICIDE

Explanation:

The character A will be sent followed by the two character <id> to indicate which Wolf is participating in an attack. This is followed by a single character <C> indicating which type of thing is attacking, either a Wolf, Bear, Stone, or Lion.

Reply with one of the <atk> characters listed above, indicating what your response to the attack is, following by the two digit <id> for confirmation.

And that's it. There's no more to it. If you lose an attack, that <id> will never be sent to your process again, that's how you will know your Wolf has died -- if a complete Movement round has passed without that <id> ever being sent.

Conclusion

Note that any exceptions will kill all the Wolves of your remote type, as only a single "Process" is constructed of your remote wolf, for all wolves of your type that get created.

In this repository you'll find the Wolf.java file. Search and replace the following strings to set up your bot:

  • Replace <invocation> with the command line argument that will properly execute your process.

  • Replace <custom-name> with a unique name for your Wolf.

  • For an example look at the repository, where I have WolfRandomPython.java that invokes my example remote, the PythonWolf.py (a Python 3+ Wolf).

  • Rename the file to be Wolf<custom-name>.java, where <custom-name> is replaced with the name you chose above.

To test your Wolf, compile the Java program (javac Wolf<custom-name>.java), and follow Rusher's instructions to include it in the simulation program.

Important: Be sure to provide clear, concise instructions on how to compile/execute your actual Wolf, which follows the scheme I've outlined above.

Good luck, and may nature be ever in your favor.

The Wrapper Code

Remember, you MUST do the searches and replaces outlined about for this to work. If your invocation is particularly hairy, please contact me for assistance.

Note there is a main method in this wrapper, to allow rudimentary "pass/fail" testing on your local box. To do so, download the Animal.java class from the project, and remove the package animals; line from both files. Replace the MAP_SIZE line in Animal.java with some constant (like 100). Compile them using javac Wolf<custom-name>.java an execute via java Wolf<custom-name>.

package animals;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Remote Wolf<custom-name> wrapper class. 
 */
public class Wolf<custom-name> extends Animal {
    /**
     * Simple test script that sends some typical commands to the
     * remote process.
     */
    public static void main(String[]args){
        Wolf<custom-name>[] wolves = new Wolf<custom-name>[100];
        for(int i=0; i<10; i++) {
            wolves[i] = new Wolf<custom-name>();
        }
        char map[][] = new char[3][3];
        for (int i=0;i<9;i++)
            map[i/3][i%3]=' ';
        map[1][1] = 'W';
        for(int i=0; i<10; i++) {
            wolves[i].surroundings=map;
            System.out.println(wolves[i].move());
        }
        for(int i=0; i<10; i++) {
            System.out.println(wolves[i].fight('S'));
            System.out.println(wolves[i].fight('B'));
            System.out.println(wolves[i].fight('L'));
            System.out.println(wolves[i].fight('W'));
        }
        wolfProcess.endProcess();
    }
    private static WolfProcess wolfProcess = null;

    private static Wolf<custom-name>[] wolves = new Wolf<custom-name>[100];
    private static int nWolves = 0;

    private boolean isDead;
    private int id;

    /**
     * Sets up a remote process wolf. Note the static components. Only
     * a single process is generated for all Wolves of this type, new
     * wolves are "initialized" within the remote process, which is
     * maintained alongside the primary process.
     * Note this implementation makes heavy use of threads.
     */
    public Wolf<custom-name>() {
        super('W');
        if (Wolf<custom-name>.wolfProcess == null) {
            Wolf<custom-name>.wolfProcess = new WolfProcess();
            Wolf<custom-name>.wolfProcess.start();
        }

        if (Wolf<custom-name>.wolfProcess.initWolf(Wolf<custom-name>.nWolves, MAP_SIZE)) {
            this.id = Wolf<custom-name>.nWolves;
            this.isDead = false;
            Wolf<custom-name>.wolves[id] = this;
        } else {
            Wolf<custom-name>.wolfProcess.endProcess();
            this.isDead = true;
        }
        Wolf<custom-name>.nWolves++;
    }

    /**
     * If the wolf is dead, or all the wolves of this type are dead, SUICIDE.
     * Otherwise, communicate an attack to the remote process and return
     * its attack choice.
     */
    @Override
    public Attack fight(char opponent) {
        if (!Wolf<custom-name>.wolfProcess.getRunning() || isDead) {
            return Attack.SUICIDE;
        }
        try {
            Attack atk = Wolf<custom-name>.wolfProcess.fight(id, opponent);

            if (atk == Attack.SUICIDE) {
                this.isDead = true;
            }

            return atk;
        } catch (Exception e) {
            System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
            isDead = true;
            return Attack.SUICIDE;
        }
    }

    /**
     * If the wolf is dead, or all the wolves of this type are dead, HOLD.
     * Otherwise, get a move from the remote process and return that.
     */
    @Override
    public Move move() {
        if (!Wolf<custom-name>.wolfProcess.getRunning() || isDead) {
            return Move.HOLD;
        }
        try {
            Move mv = Wolf<custom-name>.wolfProcess.move(id, surroundings);

            return mv;
        } catch (Exception e) {
            System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
            isDead = true;
            return Move.HOLD;
        }
    }

    /**
     * The shared static process manager, that synchronizes all communication
     * with the remote process.
     */
    static class WolfProcess extends Thread {
        private Process process;
        private BufferedReader reader;
        private PrintWriter writer;
        private ExecutorService executor;
        private boolean running;

        public boolean getRunning() {
            return running;
        }

        public WolfProcess() {
            process = null;
            reader = null;
            writer = null;
            running = true;
            executor = Executors.newFixedThreadPool(1);
        }

        public void endProcess() {
            running = false;
        }

        /**
         * WolfProcess thread body. Keeps the remote connection alive.
         */
        public void run() {
            try {
                System.out.println("Starting Wolf<custom-name> remote process");
                ProcessBuilder pb = new ProcessBuilder("<invocation>".split(" "));
                pb.redirectErrorStream(true);
                process = pb.start();
                System.out.println("Wolf<custom-name> process begun");
                // STDOUT of the process.
                reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); 
                System.out.println("Wolf<custom-name> reader stream grabbed");
                // STDIN of the process.
                writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
                System.out.println("Wolf<custom-name> writer stream grabbed");
                while(running){
                    this.sleep(0);
                }
                reader.close();
                writer.close();
                process.destroy(); // kill it with fire.
                executor.shutdownNow();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Wolf<custom-name> ended catastrophically.");
            }
        }

        /**
         * Helper that invokes a read with a timeout
         */
        private String getReply(long timeout) throws TimeoutException, ExecutionException, InterruptedException{
            Callable<String> readTask = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return reader.readLine();
                }
            };

            Future<String> future = executor.submit(readTask);
            return future.get(timeout, TimeUnit.MILLISECONDS);
        }

        /**
         * Sends an initialization command to the remote process
         */
        public synchronized boolean initWolf(int wolf, int map_sz) {
            while(writer == null){
                try {
                this.sleep(0);
                }catch(Exception e){}
            }
            boolean success = false;
            try{
                writer.printf("S%02d%d\n", wolf, map_sz);
                writer.flush();
                String reply = getReply(5000l);
                if (reply != null && reply.length() >= 3 && reply.charAt(0) == 'K') {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        success = true;
                    }
                }
                if (reply == null) {
                    System.out.println("did not get reply");
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to initialize, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to initialize, %s\n", wolf, e.getMessage());
            }
            return success;
        }

        /**
         * Send an ATTACK command to the remote process.
         */
        public synchronized Attack fight(int wolf, char opponent) {
            Attack atk = Attack.SUICIDE;
            try{
                writer.printf("A%02d%c\n", wolf, opponent);
                writer.flush();
                String reply = getReply(1000l);
                if (reply.length() >= 3) {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        switch(reply.charAt(0)) {
                            case 'R':
                                atk = Attack.ROCK;
                                break;
                            case 'P':
                                atk = Attack.PAPER;
                                break;
                            case 'S':
                                atk = Attack.SCISSORS;
                                break;
                            case 'D':
                                atk = Attack.SUICIDE;
                                break;
                        }
                    }
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to attack, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to attack, %s\n", wolf, e.getMessage());
            }
            return atk;
        }

        /**
         * Send a MOVE command to the remote process.
         */
        public synchronized Move move(int wolf, char[][] map) {
            Move move = Move.HOLD;
            try{
                writer.printf("M%02d", wolf);
                for (int row=0; row<map.length; row++) {
                    for (int col=0; col<map[row].length; col++) {
                        writer.printf("%c", map[row][col]);
                    }
                }
                writer.print("\n");
                writer.flush();
                String reply = getReply(1000l);
                if (reply.length() >= 3) {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        switch(reply.charAt(0)) {
                            case 'H':
                                move = Move.HOLD;
                                break;
                            case 'U':
                                move = Move.UP;
                                break;
                            case 'L':
                                move = Move.LEFT;
                                break;
                            case 'R':
                                move = Move.RIGHT;
                                break;
                            case 'D':
                                move = Move.DOWN;
                                break;
                        }
                    }
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to move, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("Wolf<custom-name> %d failed to move, %s\n", wolf, e.getMessage());
            }
            return move;
        }
    }
}

ProgrammerDan

Posted 2014-04-04T18:47:50.283

Reputation: 1 119

1I'm torn on this post. It's not an answer, but it is very useful for the challenge. It should probably go in the challenge body, though. – Mego – 2016-08-21T23:32:02.400

Either there or here, provided folks can find it and its useful to them, I will be content :) – ProgrammerDan – 2016-08-22T17:22:43.673

40

CamoWolf

Abusing the required code format.

// Optional code here
public class Wolf extends Animal {
    // Optional code here
    public Wolf() { super('W'); // Optional code here }
    public Attack fight(char opponent) { // Required code here. Must return an Attack. }
    public Move move() { // Required code here. Must return a Move. }
    // Optional code here
}

So, my wolf is really smart, and he camouflages as a stone instead! Blending in with the environment is always a good survival tactic!

public class Wolf extends Animal {
    private Move lastMove;
    public Wolf() { super('S'); lastMove = Move.RIGHT; } /*
    public Wolf() { super('W'); }
    public Attack fight(char opponent) { */ public Attack fight(char opponent) {
        switch(opponent) {
        case 'B': return Attack.SCISSORS;
        case 'S': return Attack.PAPER;
        case 'W': return Attack.SCISSORS; // Here's an explanation why:
                                          // the wolves will see me and think I'm a rock.
                                          // Therefore, they'll attack with paper.
                                          // So, I'll use scissors instead!
        case 'L': return Attack.SCISSORS;
        }
    }
    public Move move() {
        // First we run away from any lions that we see, since they are the only threat
        if (surroundings[0][1] == 'L') {
            if (isSafe(surroundings[2][1])) return lastMove = Move.DOWN;
            else if (isSafe(surroundings[1][0])) return lastMove = Move.LEFT;
            else return lastMove = Move.RIGHT;
        }
        if (surroundings[1][0] == 'L') {
            if (isSafe(surroundings[1][2])) return lastMove = Move.RIGHT;
            else if (isSafe(surroundings[0][1])) return lastMove = Move.UP;
            else return lastMove = Move.DOWN;
        }

        // If there's no (threatening) lions in sight, be lazy.
        return lastMove = Move.HOLD;
    }
    private boolean isSafe(char c) { return (c != 'L' && c != 'W') }
}

Update: Added new isSafe check to also be safe from LazyWolf! Ha!
Update 2: Supposedly, being lazy is also a good survival tactic, so that's what mine does now. Doesn't move unless threatened by a lion. lastMove isn't needed anymore, but I kept it anyway just in case I change the code again.

Doorknob

Posted 2014-04-04T18:47:50.283

Reputation: 49 044

Ha, nice! Was averaging high-90's survival until I fixed LazyWolf ^^ – Geobits – 2014-04-05T20:36:12.533

26When I added your submission to the project, I accidentally put the entire class in a comment block. – Rainbolt – 2014-04-05T21:46:58.050

1@Rusher does Java parse nested comment blocks? :P – Martin Ender – 2014-04-06T08:20:14.043

6In a world where LazyWolf can defend against this, it might make more sense to pretend to be a lion and to choose ROCK or randomly. It will lower the number of encounters from people since most will try to avoid lions, and it should win on average against people who think they are fighting a lion. – Tim Seguine – 2014-04-06T08:45:24.233

1How is this not cheating? – Radiodef – 2014-04-06T16:04:50.760

6@Radiodef It is. – Rainbolt – 2014-04-06T16:55:41.317

2@Radiodef When did I say it wasn't? ;-) – Doorknob – 2014-04-06T17:01:32.427

@Geobits I've un-fixed your fix. :D – Doorknob – 2014-04-06T17:32:10.027

35

GatheringWolf

My wolves live in a group. They gather, if the lions let them. They're not so good at surviving, though.

Update: If a lion force them away, they now try to regather!

Screenshot of the result of GatheringWolf

package animals;
import java.util.*;
public class GatheringWolf extends Animal {
    private static int iteration;
    private static Move preferredMove;
    private int localIteration;
    private int loneliness;
    private boolean dangerFlag;
    private Move lastMove;
    public GatheringWolf() {
        super('W');
    }
    @Override
    public Attack fight(char other) {
        switch (other) {
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S':
            return Attack.PAPER;
        default:
            return Attack.values()[(int) (Math.random() * 3)];
        }
    }
    @Override
    public Move move() {
        if (localIteration == iteration) {
            localIteration++;
            iteration++;
            preferredMove = Math.random() < 0.5 ? Move.DOWN : Move.RIGHT;
        } else
            localIteration = iteration;
        EnumSet<Move> moves = EnumSet.allOf(Move.class);
        if (surroundings[0][1] == 'W')
            moves.remove(Move.UP);
        if (surroundings[1][0] == 'W')
            moves.remove(Move.LEFT);
        if (surroundings[2][1] == 'W')
            moves.remove(Move.DOWN);
        if (surroundings[1][2] == 'W')
            moves.remove(Move.RIGHT);
        if (surroundings[0][0] == 'L') {
            moves.remove(Move.UP);
            moves.remove(Move.LEFT);
        }
        if (surroundings[0][1] == 'L')
            moves.remove(Move.UP);
        if (surroundings[1][0] == 'L')
            moves.remove(Move.LEFT);
        if (surroundings[0][2] == 'L')
            moves.remove(Move.RIGHT);
        if (surroundings[2][0] == 'L')
            moves.remove(Move.DOWN);
        if (surroundings[0][1] == 'L' || surroundings[1][0] == 'L')
            if (moves.size() > 1) {
                moves.remove(Move.HOLD);
                dangerFlag = true;
            }
        int wolfNear = -1;
        for (char[] a : surroundings)
            for (char c : a)
                if (c == 'W')
                    wolfNear++;
        boolean enoughWolfNear = wolfNear >= (Math.random() < 0.9 ? 1 : 2);
        if (moves.contains(Move.HOLD) && enoughWolfNear) {
            loneliness = 0;
            dangerFlag = false;
            return lastMove = Move.HOLD;
        } else
            loneliness++;
        if (loneliness > 10) {
            EnumSet<Move> preferred = EnumSet.copyOf(moves);
            preferred.retainAll(EnumSet.of(preferredMove, Move.HOLD));
            if (!preferred.isEmpty())
                moves = preferred;
        }
        if (loneliness == 2 && dangerFlag) {
            Move reverted = Move.values()[lastMove.ordinal() ^ 0b10];
            dangerFlag = false;
            if (moves.contains(reverted))
                return lastMove = reverted;
        }
        if (moves.contains(Move.HOLD))
            dangerFlag = false;
        if (moves.contains(preferredMove))
            moves.remove(preferredMove == Move.DOWN ? Move.RIGHT : Move.DOWN);
        int n = (int) (Math.random() * moves.size());
        Iterator<Move> ite = moves.iterator();
        while (n-- > 0)
            ite.next();
        return lastMove = ite.next();
    }
}

johnchen902

Posted 2014-04-04T18:47:50.283

Reputation: 1 032

40A couple of times, our wolves became friends because mine got trapped but wouldn't initiate an attack and yours just assumed mine was part of the pack. :) – undergroundmonorail – 2014-04-05T06:02:05.290

1Thank you for writing this, because otherwise I was going to have to do it myself. Clever use of mutual pseudorandomness. – Ben Jackson – 2014-04-06T04:54:31.540

30

The Sheep in Wolf's Clothing

Runs away.

It prioritizes running away from Wolves the most, since they're going to be the most dangerous. Next are Lions, since they're nondeterministic. Bears and Stones are both not at all a problem, but we still run away from them if we have nothing better to do because a Wolf killing a Bear or a Stone is, at that moment in time, not a Wolf killing a Sheep.

I haven't yet tested it against LazyWolf, but I have it on good authority that it kicks EmoWolf's ass. ;)

(Please forgive me if this code is terrible, I've haven't ever touched Java for much more than a Hello World program before.)

package animals;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class Sheep extends Animal {
    public Sheep() { super('W'); }

    private static final Map<Character, Integer> AnimalWeights;
    static{
        AnimalWeights = new HashMap<>();
        AnimalWeights.put('W', -3);
        AnimalWeights.put('S', -1);
        AnimalWeights.put(' ', 0);
        AnimalWeights.put('H', 1);
        AnimalWeights.put('L', -2);
        AnimalWeights.put('B', -1);
    }

    @Override
    public Attack fight(char c) { 
        switch (c) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                return Attack.PAPER;
        } 
    }

    @Override
    public Move move() {

        int xWeight = 0;
        int yWeight = 0;

        // Northwest
        xWeight += AnimalWeights.get(surroundings[0][0]);
        yWeight += AnimalWeights.get(surroundings[0][0]);

        // North
        yWeight += AnimalWeights.get(surroundings[0][1]);

        // Northeast
        xWeight -= AnimalWeights.get(surroundings[0][2]);
        yWeight += AnimalWeights.get(surroundings[0][2]);

        // West
        xWeight += AnimalWeights.get(surroundings[1][0]);

        // East
        xWeight -= AnimalWeights.get(surroundings[1][2]);

        // Southwest
        xWeight += AnimalWeights.get(surroundings[2][0]);
        yWeight -= AnimalWeights.get(surroundings[2][0]);

        // South
        yWeight -= AnimalWeights.get(surroundings[2][1]);

        // Southeast
        xWeight -= AnimalWeights.get(surroundings[2][2]);
        yWeight -= AnimalWeights.get(surroundings[2][2]);

        if (Math.abs(xWeight) < Math.abs(yWeight)) {
            if (yWeight > 0) {
                return Move.UP;
            } else {
                return Move.DOWN;
            }
        } else if (Math.abs(yWeight) < Math.abs(xWeight)) {
            if (xWeight > 0) {
                return Move.RIGHT;
            } else {
                return Move.LEFT;
            }
        }

        // Sit still if no one's around
        return Move.HOLD;
    }
}

undergroundmonorail

Posted 2014-04-04T18:47:50.283

Reputation: 5 199

Just curious, if xWeight and yWeight are both non-zero and their absolute values are the same, this wolf does not move, correct? And if so, was that intentional, and why? – Patrick Roberts – 2017-05-15T19:37:46.293

@PatrickRoberts oops – undergroundmonorail – 2017-05-15T20:13:18.757

Oops, I left something I used for testing in there by accident. It won't affect anything, it just means that I try to get close to anything that looks like an H. :P – undergroundmonorail – 2014-04-05T00:37:44.790

25

Not an entry, just want to contribute to the GUI by adding color code for each class =D

Result

Colored GUI

Wild.java

Change the code around game.populate(c,100) with:

String[] colors = generateColors(classes.length);
int idx = 0;
for(Class c : classes){
    Animal.setColor(c, colors[idx]);
    idx++;
    game.populate(c, 100);
}
stats.update();

with the generateColors defined as:

private static String[] generateColors(int n){
    String[] result = new String[n];
    double maxR = -1000;
    double minR = 1000;
    double maxG = -1000;
    double minG = 1000;
    double maxB = -1000;
    double minB = 1000;
    double[][] colors = new double[n][3];
    for(int i=0; i<n; i++){
        double cos = Math.cos(i * 2 * Math.PI / classes.length);
        double sin = Math.sin(i * 2 * Math.PI / classes.length);
        double bright = 1;
        colors[i][0] = bright + sin/0.88;
        colors[i][1] = bright - 0.38*cos - 0.58*sin;
        colors[i][2] = bright + cos/0.49;
        maxR = Math.max(maxR, colors[i][0]);
        minR = Math.min(minR, colors[i][0]);
        maxG = Math.max(maxG, colors[i][1]);
        minG = Math.min(minG, colors[i][1]);
        maxB = Math.max(maxB, colors[i][2]);
        minB = Math.min(minB, colors[i][2]);
    }
    double scaleR = 255/(maxR-minR);
    double scaleG = 255/(maxG-minG);
    double scaleB = 255/(maxB-minB);
    for(int i=0; i<n; i++){
        int R = (int)Math.round(scaleR*(colors[i][0]-minR));
        int G = (int)Math.round(scaleG*(colors[i][1]-minG));
        int B = (int)Math.round(scaleB*(colors[i][2]-minB));
        result[i] = "#"+String.format("%02x%02x%02x", R, G, B);
    }
    return result;
}

which algorithm is taken from this StackOverflow answer

With the color and setColor being defined in Animal.java

Animal.java

public static HashMap<Class, String> color = new HashMap<Class, String>();

public static void setColor(Class animalClass, String animalColor){
    color.put(animalClass, animalColor);
}

Then update the toString methods in Game.java and Statistics.java:

Game.java

public String toString() {
    String s = "<html>";
    for (ArrayList<ArrayList<Animal>> row : board) {
        for (ArrayList<Animal> cell : row) {
            if (cell.isEmpty())
                s += "&nbsp;&nbsp;";
            else
                s += "<span style='color:"+ Animal.color.get(cell.get(0).getClass()) +"'>" + cell.get(0).letter + "</span>&nbsp;";
        }
        s+="<br>";
    }
    return s + "</html>";
}

Statistics.java

public String toString() {
    String s = "<html>";
    for (int i = 0; i < classes.length; i++) {
        s += "<span style='color:" + Animal.color.get(classes[i]) + "'>" + classes[i] + "</span>&nbsp;-&nbsp;" + living[i] + "<br>";
    }
    return s + "</html>";
}

justhalf

Posted 2014-04-04T18:47:50.283

Reputation: 1 922

2Beautiful. I passed several minutes yesterday just looking the screen. Felt like Tank reading Matrix. – Averroes – 2014-04-09T10:51:21.170

Simulation.... is.... kind.... of..... slow....(5 iterations after 30 seconds) – user3188175 – 2014-04-09T19:37:07.253

Haha, yes. But it's inherent in the design of the simulation, which is printing HTML code for each iteration. – justhalf – 2014-04-10T00:36:09.387

Can I share this on my Google drive (linked in the Challenge) if I provide a link to this post? It works spectacularly well for unit testing a small number of wolves. – Rainbolt – 2014-04-11T16:20:22.593

1Yes you can. You can include some initial rendering skip also to speed up the rendering process for many wolves, as I wrote in my other post regarding earthquake. – justhalf – 2014-04-11T16:22:45.937

23

Gambler Wolf

Gambler wolf likes to take chances so he behaves erratically hoping lady luck is on his side! Fortunately for gambler wolf, lady luck never lets him down! By perpetual strokes of luck, gambler wolf clears the field of all non-wolf obstacles with unbelievable few causalities!

package animals;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

public class GamblerWolf extends Animal {
    private static int last = 0;

    public GamblerWolf() { super('W'); gamble(); }
    public Attack fight(char opponent) {
        switch (opponent) {
        case 'S': return Attack.ROCK; /* Camo Wolf? */
        case 'B': return Attack.SCISSORS;
        case 'L': return Attack.SCISSORS;
        default:  return attackWolf();
        }
    }
    public Move move() {
        ArrayList<Move> moves = (ArrayList<Move>) Arrays.asList(Move.values());
        Collections.shuffle(moves);
        for(Move move : moves)
          if(isThreatenedBy(move))
            return moveToEvade(move);
        return Move.HOLD;
    }

    /* Remember, Gamblers Don't Gamble */
    @SuppressWarnings("serial")
    private static void gamble() {
        try {
        Field field = Math.class.getDeclaredField("randomNumberGenerator"); 
        field.setAccessible(true);
        field.set(null, new Random() { 
              @Override
              public double nextDouble() {
                return 4; // chosen by fair dice roll
              }           // guaranteed to be random
            });           // proof: http://xkcd.com/221/
        }
        catch (SecurityException        e) {}
        catch (NoSuchFieldException     e) {}
        catch (IllegalArgumentException e) {}
        catch (IllegalAccessException   e) {}
    }

    private static Attack attackWolf() {
        return Attack.values()[last++ % 3];
    }
    private boolean isThreatenedBy(Move move) {
        return isWolf(move) 
            || isStone(move); 
    }

    private Move moveToEvade(Move move) {
        if(isSafeMove(getOpposite(move)))
            return getOpposite(move);

        ArrayList<Move> moves = (ArrayList<Move>) Arrays.asList(getOrthoginal(move));
        Collections.shuffle(moves);
        for(Move m : moves)
            if(isSafeMove(m))
                return m;
        return Move.HOLD;
    }

    private static Move[] getOrthoginal(Move move) {
        switch(move){
        case UP:
        case DOWN:  return new Move[] { Move.LEFT, Move.RIGHT };
        case LEFT:
        case RIGHT: return new Move[] { Move.UP,   Move.DOWN };
        default:    return null;
        }
    }

    private static Move getOpposite(Move move) {
        switch(move){
        case UP:    return Move.DOWN;
        case DOWN:  return Move.UP;
        case LEFT:  return Move.RIGHT;
        case RIGHT: return Move.LEFT;
        default:    return null;
        }
    }

    private boolean isSafeMove(Move move) {
        return !isWolf(move)
            && !isStone(move)
            && !couldAWolfMoveHere(move);
    }

    private boolean isWolf(Move move) {
        return isX(move,'W');
    }

    private boolean isStone(Move move) {
        return isX(move,'S');
    }

    private boolean isX(Move m, char c) {
        switch (m) {
        case UP:    return surroundings[0][1] == c;
        case LEFT:  return surroundings[1][0] == c;
        case RIGHT: return surroundings[1][2] == c;
        case DOWN:  return surroundings[2][1] == c;
        default:    return false;
        }
    }

    private boolean couldAWolfMoveHere(Move move) {
        switch (move) {
        case UP:    return surroundings[0][2] == 'W' || surroundings[0][0] == 'W';
        case LEFT:  return surroundings[2][0] == 'W' || surroundings[0][0] == 'W';
        case RIGHT: return surroundings[0][2] == 'W' || surroundings[2][2] == 'W';
        case DOWN:  return surroundings[2][0] == 'W' || surroundings[2][2] == 'W';
        default:    return false;
        }
    }
}

Edit: v1.1

  • Now avoids Stones (Camo-Wovles?)

  • Increased Randomness!

recursion.ninja

Posted 2014-04-04T18:47:50.283

Reputation: 548

GamblerWolf is preventing ForrestWolf from working, because now Math.random is no longer in the range [0, 1); ForrestWolf relies on that. -1 until you fix that. – HyperNeutrino – 2016-04-29T01:50:21.233

@AlexL. Sounds like a ForrestWolf problem... – recursion.ninja – 2016-04-29T18:50:41.950

@recursion.ninja Maybe; one could just as well use Random#randInt(...). If you edit the question I might revert the downvote and leave it undecided. – HyperNeutrino – 2016-04-29T21:35:37.460

5I think I'll add this to my wolves: static{System.setSecurityManager(new SecurityManager());} – johnchen902 – 2014-04-06T01:08:46.253

@johnchen902 This is quickly degenerating into reactionary, defensive programming... – recursion.ninja – 2014-04-06T01:13:15.650

13When you can't play fair, cheat +1 :D – ProgrammerDan – 2014-04-06T06:00:29.507

5Unfortulately can't use return 4; // chosen by fair dice roll. guaranteed to be random. here... – Vi. – 2014-04-08T00:08:35.733

1If you want to be more evil, break the contract of Math.random() even more; return values that are well out of the [0,1) range, maybe even infinite or NaN. Or, to be as evil as possible, just throw a runtime exception instead of returning anything (also achievable by just setting the generator to null). – Runer112 – 2014-04-10T22:15:49.553

@Runner112 Because runtime exceptions equate to suicide/hold! That's perfect! – recursion.ninja – 2014-04-11T12:10:35.813

@Rusher Updated GamberWolf – recursion.ninja – 2014-04-11T18:34:33.350

23

AlphaWolf

Time to shine! My other wolf CamperWolf was very thin, now comes AlphaWolf, who is more muscular!

Instead of dodging lions the standard way, it swaps the field with them. It also assigns dangers-values to each surrounding field.

package animals;

import java.util.Random;

public class AlphaWolf extends Animal{
    private Boolean lionMoveDown = true;

    public AlphaWolf() {
        super('W');
    }
    @Override
    public Attack fight(char opponent) {
        switch(opponent){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.PAPER;
        default:
            return randomAttack();
        }
    }

    @Override
    public Move move() {
        int[] danger = new int[4];
        final int wolfsDanger = 4;
        lionMoveDown = !lionMoveDown;
        if(surroundings[0][1] == 'L' && lionMoveDown) {
            return Move.UP;
        }
        if(surroundings[1][0] == 'L'&& !lionMoveDown) {
            return Move.LEFT;
        }
        if(surroundings[0][1] == 'W') {
            danger[0] += wolfsDanger;
        }
        if(surroundings[1][2] == 'W') {
            danger[1] += wolfsDanger;
        }
        if(surroundings[2][1] == 'W') {
            danger[2] += wolfsDanger;
        }
        if(surroundings[1][0] == 'W') {
            danger[3] += wolfsDanger;
        }
        if(surroundings[0][0] == 'W') {
            danger[0]++;
            danger[3]++;
        }
        if(surroundings[0][2] == 'W') {
            danger[0]++;
            danger[1]++;
        }
        if(surroundings[2][2] == 'W') {
            danger[1]++;
            danger[2]++;
        }
        if(surroundings[1][2] == 'W') {
            danger[2]++;
            danger[3]++;
        }
        Boolean shouldMove = false;
        Move bestMove = Move.HOLD;
        int leastDanger = 4;
        for(int i = 0; i < 4; i++) {
            if (danger[i] < leastDanger) {
                bestMove = Move.values()[i];
            }
            if(danger[i] > 3) {
                shouldMove = true;
            }
        }
        if(shouldMove) {
            return bestMove;
        } else {
            return Move.HOLD;
        }
    }

    public Attack randomAttack() {
        Random rand = new Random();
        switch (rand.nextInt(3)){
            case 1: return Attack.SCISSORS;
            case 2: return Attack.ROCK;
            default: return Attack.PAPER;
        }
    }

}

It is not very beautiful code, but in my tests it worked very well against all java wolves.

Manu

Posted 2014-04-04T18:47:50.283

Reputation: 3 642

1What does "swaps the field" mean? Just curious, since I can't run this at work. – Rainbolt – 2014-04-07T21:14:52.057

On all my simulations, you are winning with an average of 80-90 wolves after 1000 iterations. I only have the legal wolves written in java that were submitted so far on mine tho. Still that's like 10 wolves or so. – Sheph – 2014-04-07T23:29:50.780

@Rusher If a lion is above my wolf and will move down, my wolf will move up – Manu – 2014-04-08T05:07:21.617

you need to add some anti-wilfcamo code. very nice idea to use lion's know pattern :) – Lesto – 2014-04-08T10:13:20.713

Did you saw my pro-alpha wolf? It's completely original ;)

– Ilya_Gazman – 2014-04-08T11:00:11.840

9Now someone needs to invent a wolf that will tail any lion it finds, so you will run into it the moment you try to swap with the lion. – AJMansfield – 2014-04-08T16:21:23.033

@AJMansfield If a lion follows another lion, this is exactly whats happens :( – Manu – 2014-04-09T10:15:53.140

@AJMansfield Done

– Justin – 2014-04-10T03:08:10.603

This is strange. I get patterns (obvious like this, occurs with more wolves too. They all move left): http://i.stack.imgur.com/L7CvF.png

– Justin – 2014-04-10T05:08:53.233

22

CamperWolf

The goal is to survive. As many of the other wolves run away from wolves, mine just stays where it is and fights all animals (even rocks, if cheating would be allowed).

package animals;

public class CamperWolf extends Animal {
    public CamperWolf() { super('W'); }
    @Override
    public Attack fight(char opponent) {  
        switch(opponent){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.ROCK;
        default:
            return Attack.values()[(int) (Math.random() * 3)];
        }
    }
    @Override
    public Move move() { return Move.HOLD; }
}

I expect it to perform very good, as many other wolves run away from wolves and this type of wolve can only die against other wolves.

Manu

Posted 2014-04-04T18:47:50.283

Reputation: 3 642

lions can kill it too. If The lion rolls scissors, your wolf will die pseudorandomly. – Tim Seguine – 2014-04-06T21:23:14.417

2Congrats man. You and your 15 lines of code are currently winning. – Rainbolt – 2014-04-07T01:58:07.277

2@Rusher thanks :) Could the downvoter please explain why he downvoted? – Manu – 2014-04-07T11:01:14.963

@Manu not that it matters, since Stones can't move, but shouldn't your case 'S' return Attack.PAPER? – DavidJFelix – 2014-04-08T13:15:41.793

@DavidJFelix If CamoWolf would have been allowed, Attack.ROCK would beat him ;)

– Manu – 2014-04-08T13:28:29.767

3Yeah, I saw CamoWolf after asking. Tricky guy! – DavidJFelix – 2014-04-09T12:49:47.967

21

DeepWolf

I put way too much time into this... Anyways, this wolf uses "deep" analytics, collecting and using as much environmental data as I could think of for it to use. The analysis operates on a mixture of wolf-specific knowledge, like known locations of lions and wolves and predictions of future locations, and pack hivemind knowledge, like estimated populations, wolf battle history, and danger of colliding with an animal opponent when moving. It's also extra massive, because in addition to having lots of logic, I went overboard on object-oriented design and have lots of special-purpose classes and methods.

I ran 100 iterations of the game for 1000 steps each with many of the popular and best-performing wolves. I purposely left out GamblerWolf because I thought it was a bit cheaty, although it shouldn't affect my wolf, anyways. Here's the average, maximum, and minimum performance data:

Averages:
Bear 0.0
Lion 0.0
Stone 3.51
Wolf 1.56
AlphaWolf 77.05
CamperWolf 69.17
DeepWolf 90.48
EmoWolf 39.92
GatheringWolf 52.15
HerjanWolf 86.55
HonorWolf 86.76
HybridWolf 86.78
LazyWolf 71.11
LionHunterWolf 32.45
MimicWolf 0.4
MOSHPITFRENZYWolf 8.95
OmegaWolf 88.67
ProAlpha 83.28
Sheep 54.74
StoneEatingWolf 75.29
WolfWithoutFear 11.9

Maxes:
Bear 0
Lion 0
Stone 9
Wolf 4
AlphaWolf 89
CamperWolf 81
DeepWolf 96
EmoWolf 57
GatheringWolf 65
HerjanWolf 95
HonorWolf 97
HybridWolf 95
LazyWolf 83
LionHunterWolf 41
MimicWolf 3
MOSHPITFRENZYWolf 22
OmegaWolf 95
ProAlpha 91
Sheep 66
StoneEatingWolf 88
WolfWithoutFear 18

Mins:
Bear 0
Lion 0
Stone 0
Wolf 0
AlphaWolf 65
CamperWolf 57
DeepWolf 83
EmoWolf 26
GatheringWolf 37
HerjanWolf 79
HonorWolf 79
HybridWolf 79
LazyWolf 58
LionHunterWolf 20
MimicWolf 0
MOSHPITFRENZYWolf 1
OmegaWolf 81
ProAlpha 70
Sheep 43
StoneEatingWolf 66
WolfWithoutFear 5

DeepWolf is in first place with an average of 90.48, although only with a narrow lead of about 2 more than the second place OmegaWolf's 88.67. About 4x the lines of code for only a 2% improvement! HerjanWolf, HonorWolf, and HybridWolf vie for third place, trailing OmegaWolf by about 2 more with very close 86.55, 86.76, and 86.78 averages, respectively.

Without further ado, I'll present the code. It's so massive that there are likely errors and/or potential for improved constants/logic that I haven't been able to see. If you have any feedback, let me know!

Code at this link, because it turns out to blow the post character limit: Ideone

Runer112

Posted 2014-04-04T18:47:50.283

Reputation: 3 576

I don't know if dropboxusercontent will lose the file, so I posted it on ideone: http://ideone.com/uRNxvj

– Justin – 2014-04-10T19:24:30.987

3@Runer112 impressive! I am looking at the code and I am a bit lost :P but one concept that intrigues me is the what you are using to base the decision that wolves are friendly or not – Moogie – 2014-04-10T22:30:42.540

@Moogie No deterministic assessment is made about whether a wolf is friendly. However, we know the number of species of wolves, we know that each species starts at 100, we know how many other friendly wolves are left, and we can guess how many unfriendly wolves are left based on the number we've killed and a guess as to how many have died to other animals. From those, we approximate the probability that any wolf we see is friendly. This information doesn't have a huge effect, but it might be the difference between choosing whether to potentially battle a wolf or to definitively battle a lion. – Runer112 – 2014-04-11T01:58:55.550

Didn't compile because MAP_SIZE is a non-static variable. Since I didn't say it would be an instance variable that, I fixed it for you. Just wanted you to know in case I posted the complete project and you saw your butchered variable names. – Rainbolt – 2014-04-11T03:33:43.100

@Rusher Oh, whoops. I think I was still running with an old version of the game code from the sandbox, and either the MAP_SIZE variable didn't actually exist back then or for some reason I ignored it and added in my own static version of it. I also wonder why my wolf scored consistently slightly better than all other wolves in my trials, but did worse in yours... Different wolf set, I guess? Or do you run your games for a number of iterations other than 1000? Or you might just need a larger sample size, 5 isn't statistically that great. – Runer112 – 2014-04-11T11:36:08.480

@Runer112 It looks like your scoreboard had 15 less submissions from your scoreboard. I run 1000 iterations per trial. I wipe the entire project and replace with a fresh copy after each trial. I'm having problems compiling the one written in Scala, but I haven't given up on it yet. I'll run more trials next time, promise! – Rainbolt – 2014-04-11T13:36:27.880

Looking at your math for computing the best move, I think you could probably do it much better if you used confidence intervals, rather than just deciding between the move with the 'most wins so far' and the 'least uses so far'. – AJMansfield – 2014-04-12T02:47:00.620

@Runer112 Ah, i see :) I have worked out a way to legitimately (i.e. no reflection hacks, and only using the information provided by the simulation) determine who are friendly wolves with a high probability. They now share a map of where friend wolves are. My problem its now how to use this information to the wolves advantage. – Moogie – 2014-04-13T10:24:28.017

19

WolfRunningWithScissors

Nobody told WolfRunningWithScissors not to run with scissors. Or maybe they did but he does it anyway.

If he runs into an enemy he'll either win with scissors, lose with scissors, tie with scissors, or poke his eye out (suicide).

package animals;

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


public class WolfRunningWithScissors extends Animal{

    public WolfRunningWithScissors() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        List<Attack> att = new ArrayList<>();
        att.add(Attack.SCISSORS);
        att.add(Attack.SUICIDE);
        Collections.shuffle(att);
        return att.get(0);
    }

    @Override
    public Move move() {
        List<Move> m = new ArrayList<>();
        m.add(Move.UP);
        m.add(Move.DOWN);
        m.add(Move.LEFT);
        m.add(Move.RIGHT);
        Collections.shuffle(m);
        return m.get(0);
    }

}

Just wanted to make a submission for fun. I've never used Java before and haven't tested this, but it should work. My random attack and moving code is based on getRandomAttack() from StoneEatingWolf.

puggsoy

Posted 2014-04-04T18:47:50.283

Reputation: 291

14

Omega Wolf

Interdependently derived solution that behaves very similar to Alpha Wolf, hence the name Omega Wolf

This wolf generates a "danger" map of the surrounding cells and will choose the movement (or hold) to the safest cell.

The firstly the cells where lions will move next are given EXTREAME_DANGER level. Then the cells surrounding any detected Wolves are given danger levels based on the immediacy of attack... i.e. if the wolf is diagonal to the omega wolf it is deemed a low threat however wolves that are adjacent are deemed a moderate threat.

The "danger" map is then blurred to allow bleeding of the threats to surrounding cells. This allows the omega wolf to "sense" threat vectors and to avoid it.

Currently the actual attack logic is very primitive. I hope to be able to give it more smarts and eek out better win/lose ratios. This should be possible if I put in some statistical heuristics.

In my testing Omega Wolf conistantly wins against alpha bot 9 out of 10 times... though the margin is very fine :P

quick results of the avg remaining living wolves after 100 rounds of 1000 iterations:

class animals.OmegaWolf - 85
class animals.HonorWolf - 82
class animals.ProAlpha - 79
class animals.AlphaWolf - 77
class animals.ShadowWolf - 77
class animals.LazyWolf - 62
class animals.CamperWolf - 61
class animals.StoneEatingWolf - 59
class animals.GatheringWolf - 48
class animals.Sheep - 42
class animals.EmoWolf - 34
class animals.LionHunterWolf - 28
class animals.GamblerWolf (no cheating) - 27
class animals.WolfWithoutFear - 11
class animals.MOSHPITFRENZYWolf - 5
class animals.Wolf - 3
class animals.Stone - 2
class animals.Bear - 0
class animals.Lion - 0
class animals.MimicWolf - 0
class animals.Wion - 0

Code:

package animals;

import wild.Wild;

public class OmegaWolf extends Animal {

    boolean lionWillMoveDown=true;

    private static final int LOW_DANGER = 10;
    private static final int MODERATE_DANGER = LOW_DANGER*2;
    private static final int EXTREAME_DANGER = MODERATE_DANGER*4;

    private static final int UP=1;
    private static final int LEFT=3;
    private static final int RIGHT=5;
    private static final int DOWN=7;
    private static final int UP_LEFT=0;
    private static final int UP_RIGHT=2;
    private static final int DOWN_LEFT=6;
    private static final int DOWN_RIGHT=8;

    private static final int WOLVES_SPECIES_COUNT=(int) Math.round(Math.pow(((float) Wild.MAP_SIZE)/20,2)-3)-3;

    /*
     * Interdependently derived solution that behaves very similar to Alpha Wolf, hence the name Omega Wolf
     * 
     * This wolf generates a "danger" map of the surrounding cells and will choose the movement (or hold) to the safest cell.
     * 
     * The firstly the cells where lions will move next are given EXTREAME_DANGER level
     * Then the cells surrounding any detected Wolves are given danger levels based on the immediacy of attack... i.e. if the wolf is diagonal to the omega wolf 
     * it is deemed a low threat however wolves that are adjacent are deemed a moderate threat.
     * The "danger" map is then blurred to allow bleeding of the threats to surrounding cells. This allows the omega wolf to "sense" threat vectors and to avoid it.
     * 
     * Currently the actual attack logic is very primitive. I hope to be able to give it more smarts and eek out better win/lose ratios. This should be possible if 
     * I put in some statistical heuristics.
     */

    public OmegaWolf() { 
        super('W'); }


    @Override
    public Attack fight(char opponent) {
        switch(opponent){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.PAPER;
        default:
            // if there is only one wolf species then it must be another omega wolf.
            if (WOLVES_SPECIES_COUNT==1)
            {
                return Attack.SCISSORS;
            }
            else
            {
                // lets just choose an attack with equal weight.
                double rand = Math.random();
                if (rand < 0.333333)
                {
                    return Attack.PAPER;
                }
                if (rand < 0.666667)
                {
                    return Attack.SCISSORS;
                }
                return Attack.ROCK;

            }
        }
    }

    public Move move() {

        lionWillMoveDown = !lionWillMoveDown;


        Move move = Move.HOLD;

        int[][] dangerMap = new int[3][3];
        int[][] blurredDangerMap = new int[3][3];

        // sense Lion Danger
        for (int y=0;y<3;y++)
        {
            for (int x=0;x<3;x++)
            {
                if (surroundings[y][x]=='L')
                {
                    if (lionWillMoveDown && y!=2)
                    {
                        dangerMap[y+1][x]+=EXTREAME_DANGER;
                    }
                    else if (x!=2)
                    {
                        dangerMap[y][x+1]+=EXTREAME_DANGER;
                    }
                }
            }
        }

        // sense Wolf Danger adjacent
        // UP
        if (surroundings[0][1]=='W')
        {
            dangerMap[0][1]+=MODERATE_DANGER;
            dangerMap[0][0]+=LOW_DANGER;
            dangerMap[0][2]+=LOW_DANGER;
            dangerMap[1][1]+=MODERATE_DANGER;
        }
        // DOWN
        if (surroundings[2][1]=='W')
        {
            dangerMap[2][1]+=MODERATE_DANGER;
            dangerMap[2][0]+=LOW_DANGER;
            dangerMap[2][2]+=LOW_DANGER;
            dangerMap[1][1]+=MODERATE_DANGER;
        }
        // LEFT
        if (surroundings[1][0]=='W')
        {
            dangerMap[1][0]+=MODERATE_DANGER;
            dangerMap[0][0]+=LOW_DANGER;
            dangerMap[2][0]+=LOW_DANGER;
            dangerMap[1][1]+=MODERATE_DANGER;
        }
        // RIGHT
        if (surroundings[1][2]=='W')
        {
            dangerMap[1][2]+=MODERATE_DANGER;
            dangerMap[0][2]+=LOW_DANGER;
            dangerMap[2][2]+=LOW_DANGER;
            dangerMap[1][1]+=MODERATE_DANGER;
        }

        // sense Wolf Danger diagonally
        // UP_LEFT
        if (surroundings[0][0]=='W')
        {
            dangerMap[0][0]+=LOW_DANGER;
            dangerMap[0][1]+=MODERATE_DANGER;
            dangerMap[1][0]+=MODERATE_DANGER;
        }
        // DOWN_LEFT
        if (surroundings[2][0]=='W')
        {
            dangerMap[2][0]+=LOW_DANGER;
            dangerMap[2][1]+=MODERATE_DANGER;
            dangerMap[1][0]+=MODERATE_DANGER;
        }
        // UP_RIGHT
        if (surroundings[0][2]=='W')
        {
            dangerMap[0][2]+=LOW_DANGER;
            dangerMap[1][2]+=MODERATE_DANGER;
            dangerMap[0][1]+=MODERATE_DANGER;
        }
        // DOWN_RIGHT
        if (surroundings[2][2]=='W')
        {
            dangerMap[2][2]+=LOW_DANGER;
            dangerMap[2][1]+=MODERATE_DANGER;
            dangerMap[1][2]+=MODERATE_DANGER;
        }


        // generate a blurred danger map. This bleeds danger to surrounding cells.
        int yj,xi,sampleCount,cumulativeDanger;
        for (int y=0;y<3;y++)
        {
            for (int x=0;x<3;x++)
            {
                sampleCount=0;
                cumulativeDanger=0;
                for (int j=-1;j<2;j++)
                {
                    for (int i=-1;i<2;i++)
                    {
                        yj=y+j;
                        xi=x+i;
                        if (yj>-1 && yj<3 && xi>-1 && xi<3)
                        {
                            cumulativeDanger+=dangerMap[yj][xi];
                            sampleCount++;
                        }
                    }
                }
                blurredDangerMap[y][x]=(dangerMap[y][x]+cumulativeDanger/sampleCount)/2;
            }
        }

        // find the safest cell
        int safestCellDanger=Integer.MAX_VALUE;
        int safestCellId = -1;
        int cellId=0;

        for (int y=0;y<3;y++)
        {
            for (int x=0;x<3;x++)
            {
                if (blurredDangerMap[y][x]<safestCellDanger)
                {
                    safestCellDanger=blurredDangerMap[y][x];
                    safestCellId=cellId;
                }
                cellId++;
            }
        }

        // safest cell is adjacent so move there
        if ((safestCellId&1)==1)
        {
            switch (safestCellId)
            {
                case UP:
                    move=Move.UP;
                    break;
                case LEFT:
                    move=Move.LEFT;
                    break;
                case RIGHT:
                    move=Move.RIGHT;
                    break;
                case DOWN:
                    move=Move.DOWN;
                    break;
            }
        }
        // safestCell is a diagonal cell or current cell
        else
        {
            // lets initialise the move to Hold.
            move = Move.HOLD;

            switch (safestCellId)
            {
                case UP_LEFT:

                    // check to see whether holding is not safer than moving up
                    if (dangerMap[1][1] > dangerMap[0][1] )
                    {
                        // move up if safer than moving left or if equally safe, when randomly chosen 
                        if (dangerMap[0][1] < dangerMap[1][0] || (dangerMap[0][1] == dangerMap[1][0] && Math.random()>0.5))
                        {
                            move=Move.UP;
                        } 
                        // left must be safest :P
                        else
                        {

                            move=Move.LEFT;
                        }
                    }
                    // check to see whether holding is not safer than moving left
                    else if (dangerMap[1][1] > dangerMap[1][0] )
                    {
                        move=Move.LEFT;
                    }

                    break;
                case UP_RIGHT:
                    // check to see whether holding is not safer than moving up
                    if (dangerMap[1][1] > dangerMap[0][1] )
                    {
                        // move up if safer than moving right or if equally safe, when randomly chosen 
                        if (dangerMap[0][1] < dangerMap[1][2]|| (dangerMap[0][1] == dangerMap[1][2] && Math.random()>0.5))
                        {
                            move=Move.UP;
                        } 
                        // right must be safest :P
                        else
                        {
                            move=Move.RIGHT;
                        }
                    }
                    // check to see whether holding is not safer than moving right
                    else if (dangerMap[1][1] > dangerMap[1][2] )
                    {
                        move=Move.RIGHT;
                    }
                    break;
                case DOWN_LEFT:
                    // check to see whether holding is not safer than moving down
                    if (dangerMap[1][1] > dangerMap[2][1] )
                    {
                        // move down if safer than moving left or if equally safe, when randomly chosen 
                        if (dangerMap[2][1] < dangerMap[1][0]|| (dangerMap[2][1] == dangerMap[1][0] && Math.random()>0.5))
                        {
                            move=Move.DOWN;
                        } 
                        // left must be safest :P
                        else
                        {
                            move=Move.LEFT;
                        }
                    }
                    // check to see whether holding is not safer than moving left
                    else if (dangerMap[1][1] > dangerMap[1][0] )
                    {
                        move=Move.LEFT;
                    }
                    break;
                case DOWN_RIGHT:
                    // check to see whether holding is not safer than moving down
                    if (dangerMap[1][1] > dangerMap[2][1] )
                    {
                        // move down if safer than moving right or if equally safe, when randomly chosen 
                        if (dangerMap[2][1] < dangerMap[2][2] || (dangerMap[2][1] == dangerMap[1][2] && Math.random()>0.5))
                        {
                            move=Move.DOWN;
                        } 
                        // right must be safest :P
                        else
                        {
                            move=Move.RIGHT;
                        }
                    }
                    // check to see whether holding is not safer than moving right
                    else if (dangerMap[1][1] > dangerMap[1][2] )
                    {
                        move=Move.RIGHT;
                    }
                    break;
            }
        }

        return move;

    }
}

Moogie

Posted 2014-04-04T18:47:50.283

Reputation: 1 325

14

StoneGuardianWolf

This was pretty fun. I made a clunky port of the java code to javascript with createjs support for visualization: JavaScript StoneGuardianWolf

The StoneGuardianWolf seeks out pet rocks and takes shelter beside Stones. She protects them and would rather sacrifice herself for their safety.

Stats

Single Player: ~75% Wolf survival rate + 35% Pet (Stone) survival rate.

Summary: 75% + 35% ---> 110% Success Rate! :)

Multi Player: Untested.

Change Log

v2: Updated AI vs GamblerWolf and pet rock seeking strategy.

v1: Better Wolf avoidance

v0: Birthday

Java Code

package animals;

public class StoneGuardianWolf extends Animal {
    public StoneGuardianWolf() {
        super('W');
    }

    private boolean petRock = false;
    private int heartache = 0;

    public Attack fight(char c) {
        this.heartache--;

        switch (c) {
        case 'B':
            return Attack.SCISSORS;
        case 'L':
            return Attack.SCISSORS;
        case 'S': // A motherly sacrifice
            return Attack.SUICIDE;
        default:
            int n = this.heartache % 3;
            if (n < 1)
                return Attack.PAPER;
            if (n < 2)
                return Attack.ROCK;
            return Attack.SCISSORS;
        }
    }

    public Move move() {
        char[][] surr = this.surroundings;
        int[][] clairvoyance = new int[3][3];

        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                clairvoyance[i][j] = 1;

        boolean seeNoStone = true;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                switch (surr[i][j]) {
                case 'L':
                    if (i < 1 && j < 1) {
                        clairvoyance[1][0] += 50;
                        clairvoyance[0][1] += 50;
                    }

                    if (i == 1 && j < 1) { // above
                        clairvoyance[1][1] += 50;
                    }

                    if (i < 1 && j == 1) { // left
                        clairvoyance[1][1] += 50;
                    }
                    break;

                case 'S': // seek stones for protection
                    seeNoStone = false;
                    this.petRock = true;
                    clairvoyance[i][j] += 999; // Only hugs!
                    if (i < 2)
                        clairvoyance[i + 1][j] -= 10;
                    if (j < 2)
                        clairvoyance[i][j + 1] -= 10;
                    if (i > 0)
                        clairvoyance[i - 1][j] -= 10;
                    if (j > 0)
                        clairvoyance[i][j - 1] -= 10;
                    break;

                case 'B': // ignore bears
                    break;

                case 'W':
                    // skip self
                    if (i == 1 && j == 1)
                        continue;
                    int m = 25; // avoid wolves

                    // don't fight unless pet rock is in danger
                    if (petRock)
                        clairvoyance[i][j] -= 999; // motherly wrath
                    else
                        clairvoyance[i][j] += 100;

                    // avoid stepping into wolf path
                    if (i != 1 && j != 1) {
                        if (i < 2)
                            clairvoyance[i + 1][j] += m;
                        if (j < 2)
                            clairvoyance[i][j + 1] += m;
                        if (i > 0)
                            clairvoyance[i - 1][j] += m;
                        if (j > 0)
                            clairvoyance[i][j - 1] += m;
                    }
                    break;

                default:
                    clairvoyance[i][j] += 0;
                }
            } // for loop
        } // for loop

        int size = clairvoyance[1][1];
        int x = 1;
        int y = 1;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (i != 1 || j != 1)
                    continue;
                int tmp = clairvoyance[i][j];
                if (tmp < size) {
                    size = tmp;
                    x = i;
                    y = j;
                }
            }
        }

        if (seeNoStone)
            this.heartache++;

        this.petRock = false;
        if (seeNoStone && heartache % 10 == 0) { // Find a pet stone! :3
            if ((heartache % 3) < 2 || clairvoyance[1][2] >= 45) {
                // try move right
                if (clairvoyance[2][1] < 45)
                    return Move.RIGHT;
            }

            // try down instead
            if (clairvoyance[1][2] < 45)
                return Move.DOWN;
        }

        if (x == 0 && y == 1)
            return Move.LEFT;
        if (x == 2 && y == 1)
            return Move.RIGHT;
        if (x == 1 && y == 0)
            return Move.UP;
        if (x == 1 && y == 2)
            return Move.DOWN;

        if (!seeNoStone)
            this.petRock = true;

        return Move.HOLD;
    }
}

talmobi

Posted 2014-04-04T18:47:50.283

Reputation: 141

5Stone Eating Wolf arch-enemy! – Averroes – 2014-04-10T17:55:24.450

:) Indeed - ain't letting you near my pet rocks! CamoWolf slaps SGW pretty hard, though. – talmobi – 2014-04-10T19:06:02.637

1Fortunately you don't need to include CamoWolf as a legitimate entry =D – justhalf – 2014-04-14T09:46:59.370

12

Wolves with a Collective Memory

A Wolf pack in R

The idea of this wolf pack is that it keeps in memory who's alive or dead, check what the dead wolves and the alive wolves used as attacks and change the choice probability accordingly.

Here is the R code:

infile <- file("stdin")
open(infile)
repeat{
    input <- readLines(infile,1)
    type <- substr(input,1,1)
    id <- substr(input,2,3)
    if(nchar(input)>3){
        info <- substr(input,4,nchar(input))
    }else{
        info <- NULL
    }
    attack <- function(id,info){
        if(info%in%c("B","L")){choice <- "S"}
        if(info=="S"){choice <- "P"}
        if(info=="W"){
            if(exists("memory")){
                dead <- memory$ID[memory$Status=="Dead"]
                veteran <- memory[memory$Attack!="" & !is.na(memory$Attack), ]
                if(nrow(veteran[!is.na(veteran[,1]),])>0){
                    deadvet<-veteran[veteran$ID%in%dead,]
                    deadvet<-unlist(lapply(split(deadvet,deadvet$ID),function(x)tail(x$Attack,1)))
                    deadvet <- table(factor(deadvet,levels=c("R","P","S","")))
                    livevet <- table(factor(veteran$Attack,levels=c("R","P","S","")))-deadvet
                    probR <- (1+livevet['R'])/(1+livevet['R']+deadvet['R'])
                    probS <- (1+livevet['S'])/(1+livevet['S']+deadvet['S'])
                    probP <- (1+livevet['P'])/(1+livevet['P']+deadvet['P'])
                    choice <- sample(c("S","P","R"),1,prob=c(probS,probP,probR))
                    memory <- rbind(memory, data.frame(ID=id, Status="Alive", Attack=choice))
                }else{
                    choice <- sample(c("S","P","R"),1)
                    memory <- rbind(memory, data.frame(ID=id, Status="Alive", Attack=choice))
                }
            }else{
                choice <- sample(c("S","P","R"),1)
                memory <- data.frame(ID=id, Status="Alive", Attack=choice)
            }
        }
        paste(choice,id,sep="")
    }
    move <- function(id,info){
        choice <- "H"
        paste(choice,id,sep="")
    }
    initialize <- function(id){
        if(exists("memory")){
            memory <- rbind(memory,data.frame(ID=id,Status="Alive",Attack=""))
        }else{
            memory <- data.frame(ID=id,Status="Alive",Attack="")
        }
        confirmed_dead <- memory$ID[memory$Status=="Dead"]
        last_seen <- memory[!memory$ID%in%confirmed_dead,]
        last_seen <- last_seen[last_seen$Attack=="",]
        lid <- table(last_seen$ID)
        turns <- max(lid)
        dead <- lid[lid<(turns-1)]
        if(length(dead)>0){
            dead_id <- names(dead)
            for(i in dead_id){
                memory <- rbind(memory, data.frame(ID=i, Status="Dead", Attack=""))
            }
        }
        paste("K",id,sep="")
    }
    result <- switch(type,"A"=attack(id,info),"M"= move(id,info),"S"=initialize(id))
    cat(result,"\n",sep="")
    flush(stdout())
}

It uses @ProgrammerDan wrapper (thank you!), with WolfCollectiveMemory as custom name and "Rscript WolfCollectiveMemory.R" as invocation.

plannapus

Posted 2014-04-04T18:47:50.283

Reputation: 8 020

Couple of things -- first, I'm pretty sure the outputs aren't being flushed. Second, once your process is invoked by the wrapper, it is kept running. Your current design assumes your process is invoked every time communication is sent to a wolf -- this would have been way too expensive in terms of process invocations, so instead I start the process and leave the communication channels open. So, you should have a main loop that continually reads lines from stdin and writes a line in reply to stdout, followed by a flush.console(). [cont] – ProgrammerDan – 2014-04-05T12:47:58.303

[cont] My process wrapper should terminate the child process when the simulation ends. – ProgrammerDan – 2014-04-05T12:48:45.230

@Rusher Here's the gist for a valid wrapper for @plannapus' R submission. Go here to download R. Install. Add R's bin folder to your PATH variable or equivalent, and you should be good (worked fine for me).

– ProgrammerDan – 2014-04-05T12:57:45.570

I think the culprit is the readlines command. Try using a readline or equiv. readlines will block until EOF. – ProgrammerDan – 2014-04-05T13:38:44.897

I just added readLines it used to be scan. readLines with the second argument being 1 means it should stop at the first newline character. – plannapus – 2014-04-05T13:44:03.790

Perfect. As soon as I can I'll try it out and let you know. Exciting! – ProgrammerDan – 2014-04-05T14:04:10.867

Works great now! @Rusher grab the gist I posted as a comment above, and I have instructions listed here. plannapus: I'm going to add your submission to my community wiki post. Ping me if you update it, and I'll update the Gist. Note that I'll be making a new version of the Wrapper soon as Rusher added MAP_SIZE back into the Animal class. – ProgrammerDan – 2014-04-05T17:04:38.680

So I did some extensive testing and basically the version you have posted here uses up file pointers like no-one's business, and rapidly runs out of the 128 that R provides. Check out the gist I have of your source to see my fixes. Since it's a single process I removed the collectivememory.txt stuff and just use the memory resident object directly. "stdin" handling is funky in R, but I've got that working by adding an open statement. See the code.

– ProgrammerDan – 2014-04-06T15:53:24.897

@ProgrammerDan Ok brilliant! thanks! I'll add your modifications to my entry. Of course now that the program is only read once inside a while loop I don't need to write to a file, just to keep object memory. Nice one, thanks! – plannapus – 2014-04-07T06:45:14.420

Modified the way the stats are done: all of the attacks made by dead wolves used to be included in the "bad decision" bin, when now only the last one they made is considered so... as it should have been since the beginning :) – plannapus – 2014-04-07T14:48:57.917

12

Wion

Tries to do as little as possible to survive as long as possible in expected value. It tries to move parallel to lions (regardless of whether it sees any).

It ignores wolves, since it decides they are unpredictable. If it encounters a wolf it should win about half the fights (this is optimal unless I attempt to do pattern matching). My wolves should never fight each other. If it encounters a lion (which it probably shouldn't) it should win about 3/4 of the fights. Bears and rocks should always lose.

Assuming there are any wolves in the simulation using another strategy, they would be wise to avoid my wolves, since they have a 50% chance of losing any encounter. On average this should perform at least as well as any other strategy.

If I understood the rules correctly, this should be the optimal strategy.

package animals;
import java.util.Random;

public class Wion extends Animal {
    private boolean down;
    public Wion() { super('W'); down=true;}
    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                Random rn = new Random();
                int i = Math.abs(rn.nextInt() % 4);
                while (i==3) {i = Math.abs(rn.nextInt() % 4);}
                return Attack.values()[i];
        }
    }
    public Move move() {
        down=!down;
        if(!down) { return Move.DOWN; }
        return Move.RIGHT;
    }
}

Tim Seguine

Posted 2014-04-04T18:47:50.283

Reputation: 604

I honestly don't know why this does so poorly in my test runs. It sounds good on paper, but in practice it's pretty much on par with EmoWolf :( – Geobits – 2014-04-05T18:08:19.517

@Geobits I didn't really test it TBH. I guess I misunderstood one of the rules, made a mistake, or my random attack versus wolves isn't uniformly random. – Tim Seguine – 2014-04-05T21:16:02.017

@Geobits I swapped out the attack logic. My suspicion is that maybe it was sometimes committing suicide. – Tim Seguine – 2014-04-05T21:52:00.867

I am trying to think about a good way to deal with CamoWolf – Tim Seguine – 2014-04-05T22:30:22.923

I've test you wolf. Here is the statistic: Wion fight with EmoWolf 57

Wion fight with GatheringWolf 84

Wion fight with LazyWolf 71

Wion fight with Sheep 26 – johnchen902 – 2014-04-06T00:38:35.027

@johnchen902 thanks for the stats. I would have expected it to do better, if I am being honest. Maybe I can get it better with a bit of tweaking. – Tim Seguine – 2014-04-06T08:47:56.070

Why should this be optimal? I think the general consensus here is that it's better to hold, not to move unless threatened. You can see from the amount of Lions left after 1000 iterations, usually 0. The reason why moving is bad because we have more than two types of wolves, and so on average this wolf will encounter other wolves more often compared to those wolves that just stand still. – justhalf – 2014-04-11T06:31:00.010

1@justhalf yes, I have already realized what the problem is. My reasoning can only possibly work with a population that consist of at most one other kind of wolf. In such a case the other wolf's encounter rate will increase/decrease at roughly the same rate as mine. In the multi breed case however, any increase in my encounter rate will be averaged out among all of the other wolves, so mine will be proportionally larger. I am thinking of some sort of minimal way to fix this, but I have unfortunately other more important things to concentrate on at the moment. – Tim Seguine – 2014-04-11T11:21:47.183

Reminds me of this: "A problem worthy of attack, proves its worth by fighting back!" - Paul Erdos – Tim Seguine – 2014-04-11T11:31:40.673

1But I agree that this method is optimal if there is at most one other breed of wolves. – justhalf – 2014-04-11T16:35:52.050

12

MimicWolf

The goal of this wolf is to mimic other wolves. It finds a wolf follows it to the best of its ability. MimicWolf doesn't ask questions like : How can I avoid that wolf/bear/lion/stone?

No, MimicWolf just asks questions like: Where is a wolf for me to follow? Where do I think the wolf that I am following is going to go? Is that the wolf that I was following is it a different wolf? Where did the wolf that I was following go?

I will admit that most of those questions are not yet be answered well, but for the time being here is my submission of MimicWolf

   package animals;
   import java.util.*;

public class MimicWolf extends Animal {

final int TURN_MEMORY = 5;

Random rand = new Random();

Animal.Move lastMove = Animal.Move.UP;

boolean mimicingWolf = false;

Pos[] wolfPreviousPos = new Pos[TURN_MEMORY];
RelativePos[] relativePositions = new RelativePos[TURN_MEMORY];
Move[] wolfPreviousMove = new Move[TURN_MEMORY - 1];

int turnsWithLostWolf = 0;

public MimicWolf() {
    super('W');
}

public Animal.Attack fight(char c) {
    switch (c) {
        case 'B':
            return Animal.Attack.SCISSORS;
        case 'L':
            return Animal.Attack.SCISSORS;
        case 'S':
            return Animal.Attack.PAPER;
        default:
            int x = rand.nextInt(4);
            return Animal.Attack.values()[x];
    }
}

public Animal.Move move() {
    Pos wolfPos = null;
    wolfPos = lookForSurroundingWolf();

    if (turnsWithLostWolf == 4) {
        mimicingWolf = false;
        wolfPreviousPos = new Pos[5];
        relativePositions = new RelativePos[5];
        turnsWithLostWolf = 0;
    }

    if (mimicingWolf) {
        int indexOfLastMove = 0;
        for (int i = 0; wolfPreviousPos[i] != null && i < wolfPreviousPos.length; i++) {
            indexOfLastMove = i;
        }

        //is wolf still visible??
        Pos wolfNewPos = isWolfVisible(wolfPreviousPos[indexOfLastMove]);
        if (wolfNewPos.x == -1) {//wolf is not visible
            turnsWithLostWolf++;
            return moveOppositeDirection(lastMove);
        } else {
            return mimicWolf(wolfNewPos, indexOfLastMove); //need Better way to mimic
        }
    } else {
        //check if new wolf around
        if (wolfPos.x == -1) {
            return searchForWolf();
        } else {
            mimicingWolf = true;
            return mimicWolf(wolfPos, 0);
        }
    }
}

private Animal.Move searchForWolf() {
    Animal.Move newMove = null;
    while (newMove == null || newMove == lastMove) {
        newMove = Animal.Move.values()[rand.nextInt(3)];
    }

    lastMove = newMove;
    return newMove;
}

private Pos lookForSurroundingWolf() {
    for (Integer i = 0; i < surroundings.length; i++) {
        for (Integer j = 0; j < surroundings[0].length; j++) {
            if (i == 1 && j == 1) {
                //this is myself >.<
            } else if (surroundings[i][j] == 'W') {
                return new Pos(i, j);
            }
        }
    }

    return new Pos(-1, -1);
}

/*
    for mimicWolf when movesMimiced == 1 or 2 this is the base case, Any
    number greater the wolf will attempt to mimic the next move based on pattern
    of previous moves
        we assume that we are following the same wolf as last time
 */

private Animal.Move mimicWolf(Pos wolfCurrentPos, int movesMimiced) {
    wolfPreviousPos[movesMimiced] = wolfCurrentPos;
    insertToRelativePos(wolfCurrentPos, movesMimiced);
    if (movesMimiced == 0) {
        Move m1 = null, m2 = null;
        if (wolfPreviousPos[0].x == 0) {
            m1 = Move.LEFT;
        } else if (wolfPreviousPos[0].x == 2) {
            m1 = Move.RIGHT;
        }

        if (wolfPreviousPos[0].y == 0) {
            m2 = Move.UP;
        } else if (wolfPreviousPos[0].y == 2) {
            m2 = Move.DOWN;
        }

        return randOfMoves(m1, m2); //guess which way to go
    }
    wolfPreviousMove[movesMimiced - 1] =  getDirection(wolfPreviousPos[movesMimiced - 1], wolfPreviousPos[movesMimiced]);
    if (movesMimiced == 1) {
        //if pos 1 was a cornor
        if(relativePositions[0] == RelativePos.CORNER){
            if(relativePositions[1] == RelativePos.CORNER){
                if(wolfPreviousPos[0].equals(wolfPreviousPos[1])){
                    return lastMove;
                }
                return moveOppositeDirection(lastMove);
            }
            else if(relativePositions[1] == RelativePos.EDGE){
                return Move.HOLD; //he held so i will hold
            }
        }else if(relativePositions[1] == RelativePos.EDGE){
            if(relativePositions[1] == RelativePos.EDGE){
                return lastMove;
            }
            else if(relativePositions[1] == RelativePos.CORNER){
                //only possibility is that I held, and he moved
                return wolfPreviousMove[0];
            }
        }
    } else {
        //Return most common move the wolf I am copying has made
        int[] mostCommonMoveArr = {0,0,0,0,0};
        for(int i = 0; i <= movesMimiced; i++){
            switch(wolfPreviousMove[i]){
                case UP:
                    mostCommonMoveArr[0]++;
                case RIGHT:
                    mostCommonMoveArr[1]++;
                case DOWN:
                    mostCommonMoveArr[2]++;
                case LEFT:
                    mostCommonMoveArr[3]++;
                case HOLD:
                    mostCommonMoveArr[4]++;
            }
        }

        int maxValue = -1;
        int maxLocal = 0;
        for(int i = 0; i < 5; i++){
            if(mostCommonMoveArr[i] > maxValue)
                maxValue =  mostCommonMoveArr[i];
                maxLocal = i;
        }

        return Move.values()[maxLocal];
    }

    return Move.HOLD; //shouldn't happen
}

private Pos isWolfVisible(Pos lastPos) {
    Pos mimicedWolfPos = lookForSurroundingWolf();
    while (mimicedWolfPos.x != -1 && mimicedWolfPos.y != -1) {
        //did we find the wolf?
        if (lastPos.x == mimicedWolfPos.x || lastPos.y == mimicedWolfPos.y) {
            return mimicedWolfPos;
        }

        surroundings[mimicedWolfPos.x][mimicedWolfPos.y] = ' ';
        mimicedWolfPos = lookForSurroundingWolf();
    }

    return new Pos(-1, -1);
}

private Animal.Move moveOppositeDirection(Move m) {
    switch (m) {
        case UP:
            return Move.DOWN;
        case RIGHT:
            return Move.LEFT;
        case DOWN:
            return Move.UP;
        case LEFT:
            return Move.RIGHT;
        case HOLD:
            return Move.LEFT; //No idea why this would happen but whatever
        default:
            return Move.HOLD;
    }
}

private Animal.Move getDirection(Pos firstPos, Pos secondPos){
    if(firstPos.equals(secondPos))
        return Move.HOLD;
    if(firstPos.x == secondPos.x){
        if(firstPos.y > secondPos.y)
            return Move.UP;
        return Move.DOWN;
    }
    if(firstPos.x > secondPos.x)
        return Move.RIGHT;
    return Move.LEFT;
}


private Animal.Move randOfMoves(Move m1, Move m2) {
    if (m1 == null) {
        return m2;
    } else if (m2 == null) {
        return m1;
    }

    int r = rand.nextInt(2);
    if (r == 0) {
        return m1;
    }
    return m2;
}

private class Pos {
    int x;
    int y;

    protected Pos(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object obj){
        Pos pos = (Pos) obj;
        return (this.x == pos.x && this.y == pos.y);
    }
}

private void insertToRelativePos(Pos pos, int posToAdd){
    if(pos.x == 1 || pos.y == 1){
        relativePositions[posToAdd] = RelativePos.EDGE;
    }else{
        relativePositions[posToAdd] = RelativePos.CORNER;
    }
}

private enum RelativePos{
    CORNER, EDGE
}
}

Edit: I added a better mimic system. Wolves still don't well as they don't attempt to avoid anything at the moment while continuously moving around.

Sahar Rabinoviz

Posted 2014-04-04T18:47:50.283

Reputation: 281

12

MultiWolf (Java)

This wolf knows about other wolves in this programming challenge. It instantiates them (as 'pets') if they're available and uses them to determine what to do by asking every wolf-pet it owns and chooses the most popular response.

This wolf should be infinite-recursion-safe - i.e. if someone else implements a similar concept - and will return a default action of Attack.ROCK/Move.HOLD if it detects being called while it is calling other animals.

In my tests, this has had varying results. I'm not sure if this will be allowed or not. But if it is, and some impossible miracle occurs causing it to win, I think the winning title should be passed on to the wolf that comes "second" - it's only fair, I probably stole its logic.

It avoids suicide.

Edit - I believe this Wolf will need to be loaded after the wolves it references to function properly.

package animals;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map.Entry;

public class MultiWolf extends Animal {

    private static final LinkedList<Animal> pets = new LinkedList<>();
    private static boolean inPetCall = false;

    private static void attemptLoadPet(String className) {
        try {
            Object pet = Class.forName(className).newInstance();

            if (pet instanceof Animal) {
                pets.add((Animal) pet);
            }
        } catch (Exception ex) {
            // this wolf is not available
            System.out.println(className + " is not available for MultiWolf cheating.");
        }
    }

    static {
        attemptLoadPet("animals.AlphaWolf");
        attemptLoadPet("animals.CamperWolf");
        attemptLoadPet("animals.GamblerWolf");
        attemptLoadPet("animals.GatheringWolf");
        attemptLoadPet("animals.LazyWolf");
        attemptLoadPet("animals.Sheep");
        attemptLoadPet("animals.Wion");

        attemptLoadPet("animals.MOSHPITFRENZYWolf");
        attemptLoadPet("animals.PassiveAgressiveWolf");
        attemptLoadPet("animals.StoneEatingWolf");
        attemptLoadPet("animals.HerjanWolf");
        attemptLoadPet("animals.HonorWolf");
        attemptLoadPet("animals.MimicWolf");
        attemptLoadPet("animals.LionHunterWolf");
        attemptLoadPet("animals.OmegaWolf");
        attemptLoadPet("animals.WolfWithoutFear");
        attemptLoadPet("animals.WolfRunningWithScissors");
        // attemptLoadPet("animals.SmartWolf");
        // According to Rusher, the above cheating of a non-Java wolf breaks the non-Java-entry wrapper.
        attemptLoadPet("animals.ShadowWolf");
        attemptLoadPet("animals.HybridWolf");
        attemptLoadPet("animals.ProAlpha");
        attemptLoadPet("animals.ForrestWolf");
        attemptLoadPet("animals.WhenIGrowUp");
        attemptLoadPet("animals.MigratingWolf");
        attemptLoadPet("animals.BlindWolf");
    }

    public MultiWolf() {
        super('W');
    }

    @Override
    public Attack fight(char opponent) {
        if (inPetCall) {
            // stop infinite recursion
            return Attack.ROCK;
        }

        inPetCall = true;

        HashMap<Attack, Integer> collect = new HashMap<>();

        collect.put(Attack.ROCK, 0);
        collect.put(Attack.PAPER, 0);
        collect.put(Attack.SCISSORS, 0);
        collect.put(Attack.SUICIDE, -9001);

        for (Animal a : pets) {
            a.surroundings = this.surroundings;
            Attack atk = a.fight(opponent);
            collect.put(atk, collect.get(atk)+1);
        }

        int top=0;
        Attack atk=Attack.ROCK;

        for (Entry<Attack, Integer> ent : collect.entrySet()) {
            if (ent.getValue() > top) {
                atk = ent.getKey();
                top = ent.getValue();
            }
        }

        inPetCall = false;

        return atk;
    }

    @Override
    public Move move() {
        if (inPetCall) {
            // stop infinite recursion
            return Move.HOLD;
        }

        inPetCall = true;

        HashMap<Move, Integer> collect = new HashMap<>();

        collect.put(Move.DOWN, 0);
        collect.put(Move.HOLD, 0);
        collect.put(Move.LEFT, 0);
        collect.put(Move.RIGHT, 0);
        collect.put(Move.UP, 0);


        for (Animal a : pets) {
            a.surroundings = this.surroundings;
            Move mv = a.move();
            collect.put(mv, collect.get(mv)+1);
        }

        int top=0;
        Move mv=Move.HOLD;

        for (Entry<Move, Integer> ent : collect.entrySet()) {
            if (ent.getValue() > top) {
                mv = ent.getKey();
                top = ent.getValue();
            }
        }

        inPetCall = false;

        return mv;
    }

}

OlivierTheOlive

Posted 2014-04-04T18:47:50.283

Reputation: 161

If I remember correctly, you could get the classes via Wild.classes, since it's a static field... So you wouldn't have to update you wolf everytime a new wolf is posted here ;) – Manu – 2014-04-10T14:37:15.950

That's true. But I've done it this way now, probably going to leave it. May also remove some of the lesser-winning wolves from this multiwolf. The Wion seems to become extinct every time in my runs, I'm considering cutting it out from MultiWolf as I wonder if that's reducing the quality of the actions. – OlivierTheOlive – 2014-04-10T14:59:48.123

I believe that the rule "You may neither read from nor modify files created by another Wolf class" was intended to include the other wolf class files themselves. So I think that this entry, while an awesome idea, is against the rules. – Runer112 – 2014-04-10T15:05:50.207

1@Runer112 I did wonder, but I've also learnt a bit from this about loading classes by name, and that there's no simple way to find all the classes in a package. Just a bit of fun – OlivierTheOlive – 2014-04-10T15:31:43.867

3Instantiating another Wolf doesn't constitute reading or modifying files created by another Wolf, so this submission is legitimate. The rule was intended to protect data from submissions written in languages that don't have things like static variables and need to write to a file instead. – Rainbolt – 2014-04-11T03:49:18.840

Oh, you made a call to SmartWolf which is a non Java Wolf. Your Wolf is breaking the Wrapper that ProgrammerDan wrote for the non java entries. I just commented out that one class so that it would run. – Rainbolt – 2014-04-11T03:52:38.177

@Rusher Okay, that's fine! Glad to see this cheeky entry is allowed, haha, (and ah, of course, the non-Java wrapper numbers all the wolves from 0-99, and this would clearly break that. ProgrammerDan saves the non-Java entries from being cheated) – OlivierTheOlive – 2014-04-11T08:25:57.013

What's the performance of this wolf? – justhalf – 2014-04-14T09:39:38.957

12

Not an entry, but since most of the wolves are just stationary, it's actually quite boring to watch, so I added a natural disaster into the Wild:

Earthquake!

About 5% of the time, an earthquake will happen with random magnitude, 100 being the highest, 20 the lowest. This will set an earthquakeCounter which will decrease exponentially over time after an earthquake.

What happen during an earthquake?

All Animals will have a chance to move randomly, depending on the value of earthquakeCounter. So if the value is 75, about 75% of the Animals (including Stones) will move randomly to any direction (distributed evenly).

This, non-surprisingly, kills many of the animals, so the maximum is usually about 50 animals after a few trials.

Also, the earthquake will be visualized in the GUI, which varies depending on the magnitude.

I can't see the earthquake!

The chance for an earthquake to happen is quite slim, only 5%.

But fret not! I've also included an "Earthquake!" button on the GUI, in case you want to nudge all the Wolves from their comfort zones...

Here is a screenshot:

an earthquake

Here is the code:

Wild.java

main() function (updated to skip the GUI for the first 100 iteration to speed up):

public static void main(String[] args) {

    int size = Math.round((float)Math.sqrt(classes.length+3)*20);
    final Game game = new Game(size);

    Statistics stats = new Statistics(game, classes);

    String[] colors = generateColors(classes.length);
    int idx = 0;
    for(Class c : classes){
        Animal.setColor(c, colors[idx]);
        idx++;
        game.populate(c, 100);
    }
    stats.update();

    JFrame gui = new JFrame();
    Container pane = gui.getContentPane();

    JLabel boardLabel = new JLabel();
    boardLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
    boardLabel.setText(game.toString());
    pane.add(boardLabel, BorderLayout.WEST);

    JLabel statsLabel = new JLabel();
    statsLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
    statsLabel.setText(stats.toString());
    pane.add(statsLabel, BorderLayout.EAST);

    JButton earthquakeButton = new JButton();
    earthquakeButton.addActionListener(new ActionListener(){

        @Override
        public void actionPerformed(ActionEvent e) {
            game.earthquake(true);
        }

    });
    earthquakeButton.setText("Earthquake!");
    pane.add(earthquakeButton, BorderLayout.SOUTH);

    gui.pack();
    gui.setVisible(true);

    for(int i=0; i<100; i++){
        game.iterate();
        stats.update();
    }

    while(true) {
        game.iterate();
        stats.update();
        boardLabel.setText(game.toString());
        statsLabel.setText(stats.toString());
        try { Thread.sleep(100); } catch (InterruptedException e) {}
    }
}

Game.java

package wild;

import animals.Animal;
import java.util.ArrayList;
import java.util.Random;
import animals.Animal.Attack;
import animals.Animal.Move;

public class Game {

    private ArrayList<ArrayList<ArrayList<Animal>>> board;
    private final Random gen = new Random();
    protected final int SIZE;
    private static int earthquakeCounter = 0;

    protected Game(int size) {
        this.SIZE = size;
        board = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            board.add(new ArrayList<ArrayList<Animal>>());
            for (int j = 0; j < size; j++) {
                board.get(i).add(new ArrayList<Animal>());
            }
        }
    }

    protected <T extends Animal> void populate(Class<T> species, int num) {
        while (num > 0) {
            int row = gen.nextInt(SIZE);
            int col = gen.nextInt(SIZE);
            if (board.get(row).get(col).isEmpty()) {
                try { board.get(row).get(col).add(species.newInstance()); } 
                catch (InstantiationException | IllegalAccessException e) {}
                num--;
            }
        }
    }

    protected void iterate() {
        earthquake(false);
        moveAll();
        flatten();
    }

    private void moveAll() {
        Game game = new Game(SIZE);
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                if (!board.get(i).get(j).isEmpty()) {
                    Animal a = board.get(i).get(j).get(0);
                    a.surroundings = getArea(i, j);
                    Move aMove;
                    try { aMove = a.move(); } 
                    catch (Exception e) { aMove = Move.HOLD; }
                    if(gen.nextInt(100)<earthquakeCounter){
                        aMove = Move.values()[gen.nextInt(4)];
                    }
                    switch(aMove) {
                        case UP:
                            game.board.get((i-1+SIZE)%SIZE).get(j).add(a);
                            break;
                        case RIGHT:
                            game.board.get(i).get((j+1)%SIZE).add(a);
                            break;
                        case DOWN:
                            game.board.get((i+1)%SIZE).get(j).add(a);
                            break;
                        case LEFT:
                            game.board.get(i).get((j-1+SIZE)%SIZE).add(a);
                            break;
                        case HOLD:
                            game.board.get(i).get(j).add(a);
                            break;
                    }
                }
            }
        }
        board = game.board;
    }

    /**
     * Give a random chance for an earthquake to happen
     */
    protected void earthquake(boolean force){
        if(force || (earthquakeCounter==0 && gen.nextInt(1000)>950)){
            earthquakeCounter = 20+gen.nextInt(80);
        } else {
            earthquakeCounter /= 2;
        }
    }

    private void flatten() {
        for (ArrayList<ArrayList<Animal>> row : board) {
            for (ArrayList<Animal> cell : row) {
                while (cell.size() > 1) {
                    int rand1, rand2;
                    rand1 = gen.nextInt(cell.size());
                    do { rand2 = gen.nextInt(cell.size()); } while (rand1 == rand2);

                    Animal a = cell.get(rand1);
                    Animal b = cell.get(rand2);
                    Attack aTack, bTack;
                    try { aTack = a.fight(b.letter); } 
                    catch (Exception e) { aTack = Attack.SUICIDE; }
                    try {  bTack = b.fight(a.letter); }
                    catch (Exception e) { bTack = Attack.SUICIDE; }

                    if (aTack == bTack) {
                        cell.remove((Animal)(Math.random() > 0.5 ? a : b));
                    } else {
                        switch (aTack) {
                            case ROCK:
                                cell.remove((Animal)(bTack == Attack.PAPER ? a : b));
                                break;
                            case PAPER:
                                cell.remove((Animal)(bTack == Attack.SCISSORS ? a : b));
                                break;
                            case SCISSORS:
                                cell.remove((Animal)(bTack == Attack.ROCK ? a : b));
                                break;
                        }
                    } 
                }
            }
        }
    }

    protected int poll(Class c) {
        int count = 0;
        for (ArrayList<ArrayList<Animal>> row : board) {
            for (ArrayList<Animal> cell : row) {
                for (Animal a : cell) {
                    if(c.isInstance(a))
                        count++;
                }
            }
        }
        return count;
    }

    public String toString() {
        String s = "<html>";
        s += "<span style='background:"+getBackgroundColor()+"'>";
        for (ArrayList<ArrayList<Animal>> row : board) {
            for (ArrayList<Animal> cell : row) {
                if (cell.isEmpty())
                    s += "&nbsp;&nbsp;";
                else
                    s += "<span style='color:"+ Animal.color.get(cell.get(0).getClass()) +"'>" + cell.get(0).letter + "</span>&nbsp;";
            }
            s+="<br>";
        }
        s += "</span>";
        return s + "</html>";
    }

    private String getBackgroundColor(){
        int shade = 255-(int)Math.floor(255*earthquakeCounter/100.0);
        String result = String.format("#%02x%02x%02x", shade, shade, shade);
        return result;
    }

    private char[][] getArea(int i, int j) {
        char[][] area = new char[3][3];
        for(int k = -1; k <= 1; k++) {
            for(int l = -1; l <= 1; l++) {
                int temp1 = k+1;
                int temp2 = l+1;
                int temp3 = (i+k+SIZE)%SIZE;
                int temp4 = (j+l+SIZE)%SIZE;
                ArrayList<Animal> cell = board.get((i+k+SIZE)%SIZE).get((j+l+SIZE)%SIZE);
                area[k+1][l+1] = (char)(cell.isEmpty() ? ' ' : cell.get(0).letter);
            }
        }
        return area;
    }
}

justhalf

Posted 2014-04-04T18:47:50.283

Reputation: 1 922

5some men just want to watch the world... earthquacking – Manu – 2014-04-10T14:35:02.227

4My GatheringWolves are crying. – johnchen902 – 2014-07-09T13:15:17.410

12

Is it a boy? Is it a wolf? No, it's the

BoyWhoCriedWolf.java

People are using reflection all over the place, so I figured, why not take it a step further?
I present you: the wolf that cannot lose.

package animals;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import javax.xml.bind.DatatypeConverter;

public class BoyWhoCriedWolf extends Animal {

    private static boolean ranAgent;

    public static void installAgent() {
        try {
            ranAgent = true;
            String javaExec = new File(System.getProperty("java.home"), "bin").getAbsolutePath() + File.separator + "java";
            Process proc = new ProcessBuilder(javaExec, "-cp", System.getProperty("java.class.path"),
                    "animals.BoyWhoCriedWolf", ManagementFactory.getRuntimeMXBean().getName().split("@")[0])
                    .inheritIO().start();
            proc.waitFor();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
    }

    public BoyWhoCriedWolf() {
        super('W');
        if (!ranAgent) {
            installAgent();
        }
    }

    @Override
    public Attack fight(char c) {
        return Attack.PAPER; // I like paper, it's my rubber duck.
    }

    @Override
    public Move move() {
        return Move.HOLD; // I'm terribly lazy.
    }

    public static void main(String[] args) {
        try {
            File temp = File.createTempFile("agent-", ".jar");
            temp.deleteOnExit();
            Manifest manifest = new Manifest();
            manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
            manifest.getMainAttributes().put(new Attributes.Name("Agent-Class"), "animals.BoyWhoCriedWolf");
            manifest.getMainAttributes().put(new Attributes.Name("Can-Redefine-Classes"), "true");
            JarOutputStream jos = new JarOutputStream(new FileOutputStream(temp), manifest);
            jos.close();

            // Add tools.jar
            Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            addURL.setAccessible(true);
            addURL.invoke(ClassLoader.getSystemClassLoader(), new URL("file:" + System.getProperty("java.home") + "/../lib/tools.jar"));

            Class<?> virtualMachineClass = Class.forName("com.sun.tools.attach.VirtualMachine");
            Object vm = virtualMachineClass.getDeclaredMethod("attach", String.class).invoke(null, args[0]);
            virtualMachineClass.getDeclaredMethod("loadAgent", String.class).invoke(vm, temp.getAbsolutePath());
            virtualMachineClass.getDeclaredMethod("detach").invoke(vm);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void agentmain(String args, Instrumentation instr) throws ClassNotFoundException, UnmodifiableClassException {
        instr.redefineClasses(new ClassDefinition(wild.Game.class, DatatypeConverter.parseBase64Binary(base64Game)));
    }

    private static final String base64Game =
              "yv66vgAAADMA9QoAOQCRBwCSCgACAJEJABIAkwkAEgCUBwCVCgAGAJEJABIAlgoABgCXCgAGAJgK"
            + "AAIAmQoABgCaCgCbAJwHAJ0HAJ4KABIAnwoAEgCgBwChCgASAKIHAKMKABIApAkAFAClCgAUAKYH"
            + "AKcJAGUAqAkAOgCpCgBlAKoKAAYAqwsArACtCwCsAK4KAAYArwcAsAoABgCxCQAUALIKABQAswkA"
            + "cQC0CgC1ALYGP+AAAAAAAAAJADoAtwoAcQCqCQBxALgJAHEAuQkAcQC6CgCbALsIALwHAL0KAC8A"
            + "kQoALwC+CAC/CgAvAMAKAC8AwQgAwggAwwgAxAcAjQcAxQcAxgEADElubmVyQ2xhc3NlcwEABWJv"
            + "YXJkAQAVTGphdmEvdXRpbC9BcnJheUxpc3Q7AQAJU2lnbmF0dXJlAQBVTGphdmEvdXRpbC9BcnJh"
            + "eUxpc3Q8TGphdmEvdXRpbC9BcnJheUxpc3Q8TGphdmEvdXRpbC9BcnJheUxpc3Q8TGFuaW1hbHMv"
            + "QW5pbWFsOz47Pjs+OwEAA2dlbgEAEkxqYXZhL3V0aWwvUmFuZG9tOwEABFNJWkUBAAFJAQAGPGlu"
            + "aXQ+AQAEKEkpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUB"
            + "AAFqAQABaQEABHRoaXMBAAtMd2lsZC9HYW1lOwEABHNpemUBAA1TdGFja01hcFRhYmxlBwChAQAI"
            + "cG9wdWxhdGUBABUoTGphdmEvbGFuZy9DbGFzcztJKVYBAAFlAQAoTGphdmEvbGFuZy9SZWZsZWN0"
            + "aXZlT3BlcmF0aW9uRXhjZXB0aW9uOwEAA3JvdwEAA2NvbAEAB3NwZWNpZXMBABFMamF2YS9sYW5n"
            + "L0NsYXNzOwEAA251bQEAFkxvY2FsVmFyaWFibGVUeXBlVGFibGUBABZMamF2YS9sYW5nL0NsYXNz"
            + "PFRUOz47BwDHBwDIAQAuPFQ6TGFuaW1hbHMvQW5pbWFsOz4oTGphdmEvbGFuZy9DbGFzczxUVDs+"
            + "O0kpVgEAB2l0ZXJhdGUBAAMoKVYBAAdtb3ZlQWxsAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAB"
            + "YQEAEExhbmltYWxzL0FuaW1hbDsBAAVhTW92ZQcAyQEABE1vdmUBABVMYW5pbWFscy9BbmltYWwk"
            + "TW92ZTsBAARnYW1lBwCjBwCnBwDJAQAHZmxhdHRlbgEABXJhbmQxAQAFcmFuZDIBAAFiAQAFYVRh"
            + "Y2sHAMoBAAZBdHRhY2sBABdMYW5pbWFscy9BbmltYWwkQXR0YWNrOwEABWJUYWNrAQAEY2VsbAEA"
            + "J0xqYXZhL3V0aWwvQXJyYXlMaXN0PExhbmltYWxzL0FuaW1hbDs+OwEAPkxqYXZhL3V0aWwvQXJy"
            + "YXlMaXN0PExqYXZhL3V0aWwvQXJyYXlMaXN0PExhbmltYWxzL0FuaW1hbDs+Oz47BwDLBwCVBwDK"
            + "AQAEcG9sbAEAFChMamF2YS9sYW5nL0NsYXNzOylJAQABYwEABWNvdW50AQAIdG9TdHJpbmcBABQo"
            + "KUxqYXZhL2xhbmcvU3RyaW5nOwEAAXMBABJMamF2YS9sYW5nL1N0cmluZzsHAMwBAAdnZXRBcmVh"
            + "AQAHKElJKVtbQwEABXRlbXAxAQAFdGVtcDIBAAV0ZW1wMwEABXRlbXA0AQABbAEAAWsBAARhcmVh"
            + "AQADW1tDBwDNAQAKU291cmNlRmlsZQEACUdhbWUuamF2YQwARABfAQAQamF2YS91dGlsL1JhbmRv"
            + "bQwAQABBDABCAEMBABNqYXZhL3V0aWwvQXJyYXlMaXN0DAA8AD0MAM4AzwwA0ADRDADSANMMANQA"
            + "1QcAxwwA1gDXAQAgamF2YS9sYW5nL0luc3RhbnRpYXRpb25FeGNlcHRpb24BACBqYXZhL2xhbmcv"
            + "SWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgwAYABfDABsAF8BAAl3aWxkL0dhbWUMAEQARQEADmFuaW1h"
            + "bHMvQW5pbWFsDACEAIUMANgAjQwA2QDaAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwA2wBnDADcAN0M"
            + "AN4A3wwA4ADhBwDLDADiANUMAOMA1wwATQDfAQAXYW5pbWFscy9Cb3lXaG9DcmllZFdvbGYMAOQA"
            + "zwwA5QDmDADnAOgMAOkAcwcA6gwA6wDsDADtAN0MAO4AcwwA7wBzDADwAHMMAPEAzwEABjxodG1s"
            + "PgEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDADyAPMBAAwmbmJzcDsmbmJzcDsMAH8AgAwA8gD0"
            + "AQAGJm5ic3A7AQAEPGJyPgEABzwvaHRtbD4BABBqYXZhL2xhbmcvT2JqZWN0AQALd2lsZC9HYW1l"
            + "JDEBAA9qYXZhL2xhbmcvQ2xhc3MBACZqYXZhL2xhbmcvUmVmbGVjdGl2ZU9wZXJhdGlvbkV4Y2Vw"
            + "dGlvbgEAE2FuaW1hbHMvQW5pbWFsJE1vdmUBABVhbmltYWxzL0FuaW1hbCRBdHRhY2sBABJqYXZh"
            + "L3V0aWwvSXRlcmF0b3IBABBqYXZhL2xhbmcvU3RyaW5nAQACW0MBAANhZGQBABUoTGphdmEvbGFu"
            + "Zy9PYmplY3Q7KVoBAANnZXQBABUoSSlMamF2YS9sYW5nL09iamVjdDsBAAduZXh0SW50AQAEKEkp"
            + "SQEAB2lzRW1wdHkBAAMoKVoBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAM"
            + "c3Vycm91bmRpbmdzAQAEbW92ZQEAFygpTGFuaW1hbHMvQW5pbWFsJE1vdmU7AQAESE9MRAEAHiRT"
            + "d2l0Y2hNYXAkYW5pbWFscyRBbmltYWwkTW92ZQEAAltJAQAHb3JkaW5hbAEAAygpSQEACGl0ZXJh"
            + "dG9yAQAWKClMamF2YS91dGlsL0l0ZXJhdG9yOwEAB2hhc05leHQBAARuZXh0AQAGcmVtb3ZlAQAG"
            + "bGV0dGVyAQABQwEABWZpZ2h0AQAaKEMpTGFuaW1hbHMvQW5pbWFsJEF0dGFjazsBAAdTVUlDSURF"
            + "AQAOamF2YS9sYW5nL01hdGgBAAZyYW5kb20BAAMoKUQBACAkU3dpdGNoTWFwJGFuaW1hbHMkQW5p"
            + "bWFsJEF0dGFjawEABVBBUEVSAQAIU0NJU1NPUlMBAARST0NLAQAKaXNJbnN0YW5jZQEABmFwcGVu"
            + "ZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAHChDKUxq"
            + "YXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAIQASADkAAAADAAIAPAA9AAEAPgAAAAIAPwASAEAAQQAA"
            + "ABQAQgBDAAAACAAEAEQARQABAEYAAADtAAMABAAAAF8qtwABKrsAAlm3AAO1AAQqG7UABSq7AAZZ"
            + "twAHtQAIAz0cG6IAOyq0AAi7AAZZtwAHtgAJVwM+HRuiAB8qtAAIHLYACsAABrsABlm3AAe2AAlX"
            + "hAMBp//ihAIBp//GsQAAAAMARwAAAC4ACwAAABEABAAOAA8AEgAUABMAHwAUACYAFQA1ABYAPAAX"
            + "AFIAFgBYABQAXgAaAEgAAAAqAAQANwAhAEkAQwADACEAPQBKAEMAAgAAAF8ASwBMAAAAAABfAE0A"
            + "QwABAE4AAAAYAAT/ACEAAwcATwEBAAD8ABUB+gAg+gAFAAQAUABRAAIARgAAARwAAgAGAAAAXRye"
            + "AFsqtAAEKrQABbYACz4qtAAEKrQABbYACzYEKrQACB22AArAAAYVBLYACsAABrYADJkAJiq0AAgd"
            + "tgAKwAAGFQS2AArAAAYrtgANtgAJV6cABToFhAL/p/+nsQACADYAUQBUAA4ANgBRAFQADwAEAEcA"
            + "AAAmAAkAAAAdAAQAHgAQAB8AHQAgADYAIQBRACIAVgAjAFkAJQBcACYASAAAAD4ABgBWAAAAUgBT"
            + "AAUAEABJAFQAQwADAB0APABVAEMABAAAAF0ASwBMAAAAAABdAFYAVwABAAAAXQBYAEMAAgBZAAAA"
            + "DAABAAAAXQBWAFoAAQBOAAAAGwAFAP8AUwAFBwBPBwBbAQEBAAEHAFwB+QACAgA+AAAAAgBdAAQA"
            + "XgBfAAEARgAAADsAAQABAAAACSq3ABAqtwARsQAAAAIARwAAAA4AAwAAACkABAAqAAgAKwBIAAAA"
            + "DAABAAAACQBLAEwAAAACAGAAXwABAEYAAAJjAAQABwAAAVu7ABJZKrQABbcAE0wDPRwqtAAFogE/"
            + "Az4dKrQABaIBLyq0AAgctgAKwAAGHbYACsAABrYADJoBESq0AAgctgAKwAAGHbYACsAABgO2AArA"
            + "ABQ6BBkEKhwdtwAVtQAWGQS2ABc6BacACjoGsgAZOgWyABoZBbYAGy6qAAAAAAAAzgAAAAEAAAAF"
            + "AAAAJAAAAEsAAABtAAAAjwAAALYrtAAIHARkKrQABWAqtAAFcLYACsAABh22AArAAAYZBLYACVen"
            + "AIYrtAAIHLYACsAABh0EYCq0AAVwtgAKwAAGGQS2AAlXpwBkK7QACBwEYCq0AAVwtgAKwAAGHbYA"
            + "CsAABhkEtgAJV6cAQiu0AAgctgAKwAAGHQRkKrQABWAqtAAFcLYACsAABhkEtgAJV6cAGyu0AAgc"
            + "tgAKwAAGHbYACsAABhkEtgAJV4QDAaf+z4QCAaf+vyortAAItQAIsQABAF4AZQBoABgAAwBHAAAA"
            + "WgAWAAAALgAMAC8AFgAwACAAMQA4ADIAUwAzAF4ANQBlADYAbwA3AJwAOQDAADoAwwA8AOIAPQDl"
            + "AD8BBABAAQcAQgErAEMBLgBFAUYAMAFMAC8BUgBLAVoATABIAAAAUgAIAGoABQBSAGEABgBTAPMA"
            + "YgBjAAQAZQADAGQAZwAFAG8A1wBkAGcABQAYATQASQBDAAMADgFEAEoAQwACAAABWwBLAEwAAAAM"
            + "AU8AaABMAAEATgAAADYADP0ADgcATwH8AAkB/wBPAAUHAE8HAE8BAQcAaQABBwBq/AAGBwBrLCYh"
            + "ISb5ABf6AAX6AAUAAgBsAF8AAQBGAAADuAAFAAwAAAFfKrQACLYAHEwruQAdAQCZAVAruQAeAQDA"
            + "AAZNLLYAHE4tuQAdAQCZATUtuQAeAQDAAAY6BBkEtgAfBKQBHiq0AAQZBLYAH7YACzYFKrQABBkE"
            + "tgAftgALNgYVBRUGn//uGQQVBbYACsAAFDoHGQQVBrYACsAAFDoIGQfBACCZAA4ZBBkItgAhV6f/"
            + "rBkIwQAgmQAOGQQZB7YAIVen/5kZBxkItAAitgAjOgmnAAo6C7IAJDoJGQgZB7QAIrYAIzoKpwAK"
            + "OguyACQ6ChkJGQqmAB0ZBLgAJRQAJpeeAAgZB6cABRkItgAhV6cAbbIAKBkJtgApLqoAAAAAAABh"
            + "AAAAAQAAAAMAAAAcAAAANAAAAEwZBBkKsgAqpgAIGQenAAUZCLYAIVenADAZBBkKsgArpgAIGQen"
            + "AAUZCLYAIVenABgZBBkKsgAspgAIGQenAAUZCLYAIVen/t+n/sin/q2xAAIAngCqAK0AGAC0AMAA"
            + "wwAYAAQARwAAAHYAHQAAAE8AGwBQADQAUQA9AFMASwBUAGAAVgBsAFcAeABZAIAAWgCIAFsAiwBc"
            + "AJMAXQCbAF4AngBiAKoAYwC0AGQAwABlAMoAZwDRAGgA6wBqARAAbAElAG0BKABvAT0AcAFAAHIB"
            + "VQB2AVgAdwFbAHgBXgB5AEgAAACEAA0ArwAFAFIAYQALAMUABQBSAGEACwBLAQoAbQBDAAUAWQD8"
            + "AG4AQwAGAGwA6QBiAGMABwB4AN0AbwBjAAgAqgADAHAAcwAJALQAoQBwAHMACQDAAAMAdABzAAoA"
            + "ygCLAHQAcwAKADQBJAB1AD0ABAAbAUAAVAA9AAIAAAFfAEsATAAAAFkAAAAWAAIANAEkAHUAdgAE"
            + "ABsBQABUAHcAAgBOAAABFQAa/AAIBwB4/QAXBwB5BwB4/AATBwB5/AAWAf4APwEHAGkHAGkSTgcA"
            + "avwABgcAek4HAGr8AAYHAHpXBwB5/wABAAsHAE8HAHgHAHkHAHgHAHkBAQcAaQcAaQcAegcAegAC"
            + "BwB5BwBpBiROBwB5/wABAAsHAE8HAHgHAHkHAHgHAHkBAQcAaQcAaQcAegcAegACBwB5BwBpBk4H"
            + "AHn/AAEACwcATwcAeAcAeQcAeAcAeQEBBwBpBwBpBwB6BwB6AAIHAHkHAGkGTgcAef8AAQALBwBP"
            + "BwB4BwB5BwB4BwB5AQEHAGkHAGkHAHoHAHoAAgcAeQcAaf8AAwAFBwBPBwB4BwB5BwB4BwB5AAD6"
            + "AAL5AAL6AAIABAB7AHwAAQBGAAABNgACAAkAAABvAz0qtAAItgAcTi25AB0BAJkAXS25AB4BAMAA"
            + "BjoEGQS2ABw6BRkFuQAdAQCZAD4ZBbkAHgEAwAAGOgYZBrYAHDoHGQe5AB0BAJkAHhkHuQAeAQDA"
            + "ABQ6CCsZCLYALZkABoQCAaf/3qf/vqf/oBysAAAABABHAAAAKgAKAAAAfAACAH0AHgB+ADsAfwBY"
            + "AIAAYQCBAGQAggBnAIMAagCEAG0AhQBIAAAAPgAGAFgADABiAGMACAA7ACwAdQA9AAYAHgBMAFQA"
            + "PQAEAAAAbwBLAEwAAAAAAG8AfQBXAAEAAgBtAH4AQwACAFkAAAAWAAIAOwAsAHUAdgAGAB4ATABU"
            + "AHcABABOAAAAJQAH/QAKAQcAeP0AGgcAeQcAeP0AHAcAeQcAeCH5AAL5AAL6AAIAAQB/AIAAAQBG"
            + "AAABWwADAAYAAACqEi5MKrQACLYAHE0suQAdAQCZAIUsuQAeAQDAAAZOLbYAHDoEGQS5AB0BAJkA"
            + "VBkEuQAeAQDAAAY6BRkFtgAMmQAauwAvWbcAMCu2ADESMrYAMbYAM0ynACa7AC9ZtwAwK7YAMRkF"
            + "A7YACsAAFLQAIrYANBI1tgAxtgAzTKf/qLsAL1m3ADArtgAxEja2ADG2ADNMp/94uwAvWbcAMCu2"
            + "ADESN7YAMbYAM7AAAAAEAEcAAAAqAAoAAACJAAMAigAeAIsAOgCMAEIAjQBZAI8AfACQAH8AkQCT"
            + "AJIAlgCTAEgAAAAqAAQAOgBCAHUAPQAFAB4AdQBUAD0AAwAAAKoASwBMAAAAAwCnAIEAggABAFkA"
            + "AAAWAAIAOgBCAHUAdgAFAB4AdQBUAHcAAwBOAAAAIwAG/QALBwCDBwB4/QAYBwB5BwB4/AA0BwB5"
            + "+gAi+gAC+QAWAAIAhACFAAEARgAAAdAABAALAAAApQYGxQA4Ak4CNgQVBASjAJYCNgUVBQSjAIcV"
            + "BARgNgYVBQRgNgcbFQRgKrQABWAqtAAFcDYIHBUFYCq0AAVgKrQABXA2CSq0AAgbFQRgKrQABWAq"
            + "tAAFcLYACsAABhwVBWAqtAAFYCq0AAVwtgAKwAAGOgotFQQEYDIVBQRgGQq2AAyZAAgQIKcADxkK"
            + "A7YACsAAFLQAIlWEBQGn/3mEBAGn/2otsAAAAAQARwAAADIADAAAAJcABwCYABAAmQAZAJoAHwCb"
            + "ACUAnAA1AJ0ARQCeAHMAnwCXAJkAnQCYAKMAogBIAAAAcAALAB8AeACGAEMABgAlAHIAhwBDAAcA"
            + "NQBiAIgAQwAIAEUAUgCJAEMACQBzACQAdQA9AAoAEwCKAIoAQwAFAAoAmQCLAEMABAAAAKUASwBM"
            + "AAAAAAClAEoAQwABAAAApQBJAEMAAgAHAJ4AjACNAAMAWQAAAAwAAQBzACQAdQB2AAoATgAAAFkA"
            + "Bv0ACgcAOAH8AAgB/wB2AAsHAE8BAQcAOAEBAQEBAQcAeQACBwCOAf8ACwALBwBPAQEHADgBAQEB"
            + "AQEHAHkAAwcAjgEB/wAGAAUHAE8BAQcAOAEAAPoABQACAI8AAAACAJAAOwAAABoAAwA6ABIAABAI"
            + "AGUAFABmQBkAcQAUAHJAGQ==";
}

Oh yeah, it does require a JDK to run, but I don't think that'll be a problem.

14mRh4X0r

Posted 2014-04-04T18:47:50.283

Reputation: 198

1Damn, you beat me to it. I was working on a SabotageAgentWolf with this exact tactic. – mackthehobbit – 2014-04-15T12:43:13.453

1What does this class do? – justhalf – 2014-04-16T09:51:57.660

3@justhalf It redefines the Game class with the file encoded in base64 near the bottom. That file has an instanceof check; if it's my wolf, the other always dies. – 14mRh4X0r – 2014-04-17T06:13:36.983

3+1 for first reflective answer that actually worked. – Rainbolt – 2014-04-23T03:16:15.297

1I had the idea to use exactly this mechanism to make all other animals commit suicide and call the thing HypnoWolf. I did not manage to make it run correctly, but you did - respect! – Francois Bourgeois – 2014-04-24T10:49:00.793

11

Stone Eating Wolf

Here is my submission. This wolf keeps in place if he doesn't see any stone, lion or wolf in his surroundings. If he sees a stone and no danger of being attacked by another wolf or lion he tries to eat it. If he sees any danger, he flees!

EDIT 1: Improved watching for danger algorithm. He flees better from danger now :)

package animals;

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


public class StoneEatingWolf extends Animal{

    public StoneEatingWolf() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        switch (c){
            case 'L': return Attack.SCISSORS;
            case 'B': return Attack.SCISSORS;
            case 'W': return getRandomAttack();
            case 'S': return Attack.PAPER;
            default: return getRandomAttack();
        }
    }

    private Attack getRandomAttack(){
        List<Attack> att = new ArrayList<>();
        att.add(Attack.PAPER);
        att.add(Attack.PAPER);
        att.add(Attack.ROCK);
        att.add(Attack.SCISSORS);
        Collections.shuffle(att);
        return att.get(0);
    }

    @Override
    public Move move() {
        List<Move> m = new ArrayList<>();

        //First see if there is any dangerous animal. If it is, then flee
        if (isThereAnyDangerousAnimal()){
            m.add(Move.UP);
            m.add(Move.RIGHT);
            m.add(Move.LEFT);
            m.add(Move.DOWN);
            getSafeMoves(m);
        }else{
        //No danger: Look for stones to eat
            if (isThereAnimalAtNorth('S')){
                m.add(Move.UP);
            }
            if (isThereAnimalAtEast('S')){
                m.add(Move.RIGHT);
            }
            if (isThereAnimalAtWest('S')){
                m.add(Move.LEFT);
            }
            if (isThereAnimalAtSouth('S')){
                m.add(Move.DOWN);
            }
        }

        if (m.isEmpty()){
            return Move.HOLD;
        } else {
            Collections.shuffle(m);
            return m.get(0);
        }
    }

    private void getSafeMoves(List<Move> lm){

        if (isThereAnimalAtNorth('L') || isThereAnimalAtNorth('W')){
            lm.remove(Move.UP);
        }
        if (isThereAnimalAtEast('L') || isThereAnimalAtEast('W')){
            lm.remove(Move.RIGHT);
        }
        if (isThereAnimalAtSouth('L') || isThereAnimalAtSouth('W')){
            lm.remove(Move.DOWN);
        }
        if (isThereAnimalAtWest('L') || isThereAnimalAtWest('W')){
            lm.remove(Move.LEFT);
        }

    }

    private boolean isThereAnimalAtNorth(char an){
        if (surroundings[0][0] == an || surroundings [0][1] == an || surroundings [0][2] == an){
            return true;
        }
        return false;
    }

    private boolean isThereAnimalAtSouth(char an){
        if (surroundings[2][0] == an || surroundings [2][2] == an || surroundings [2][2] == an){
            return true;
        }
        return false;
    }

    private boolean isThereAnimalAtEast(char an){
        if (surroundings[0][2] == an || surroundings [1][2] == an || surroundings [2][2] == an){
            return true;
        }
        return false;
    }

    private boolean isThereAnimalAtWest(char an){
        if (surroundings[0][0] == an || surroundings [1][0] == an || surroundings [2][0] == an){
            return true;
        }
        return false;
    }

    private boolean isThereAnyDangerousAnimal(){
        if (isThereAnimalAtEast('L') ||
                isThereAnimalAtEast('W') ||
                isThereAnimalAtNorth('L') ||
                isThereAnimalAtNorth('W') ||
                isThereAnimalAtSouth('L') ||
                isThereAnimalAtSouth('W') ||
                isThereAnimalAtWest('L') ||
                isThereAnimalAtWest('W')){
            return true;
        }
        return false;
    }

    }

Edit 2: Some Statistics

I have manage to make StoneEatingWolf the top 5-6 Wolf in the simulations I ran:

Average Results after 40 plays of 1000 iterations

I make some analysis of the fights where Stone Eating Wolves are implied. Running 40 plays of 1000 iterations I get these results:

Fight results chart

Victory are Stone Eating Wolf wins. Chart shows what we already know: The most successful wolves are the ones that doesn't meet other wolves. I also noticed that my other wolves (Migrating Wolves) are screwing some of my Stone Eaters. I hope they are hunting down another wolves too. Funny enough I didn't stumble with any Lazy Wolf nor any Camper Wolf. Also, these are the results of the attacks I received in 20 runs (Stones and Bears excluded):

PAPER       447
ROCK        881
SCISSORS    581
SUICIDE     230

Seems like there is a obvious bias to ROCK attacks. Knowing this I made my wolf PAPER attacks slightly more frequents.

Averroes

Posted 2014-04-04T18:47:50.283

Reputation: 2 298

Hope you don't run into CamoWolf. – RamenChef – 2017-10-26T14:48:32.680

2Please, don't use a line graph to plot categorical data. It gives me so much cancer looking at your graph. – AJMansfield – 2014-04-12T02:53:29.037

@AJMansfield Sorry to hear that. Hope you get well ;) Anyways I will take it into account for any future chart I make. – Averroes – 2014-04-12T16:55:31.987

You're still using Windows XP? o.O – justhalf – 2014-04-14T09:31:57.977

And why you're using ROCK (50% win) to fight Lion? It's best to use SCISSORS (75% win) – justhalf – 2014-04-14T09:35:09.147

@justhalf My company still uses XP... And you are right about the scissors. Fixed. Thanks :) – Averroes – 2014-04-14T18:19:59.850

11

HonorWolf

My Wolf is fleeing from the other wolves. If he can't run away, he honorful starts a fight.

package animals;
public class HonorWolf extends Animal {

    private int moves = 0;

    public HonorWolf() { 
        super('W'); 
    }

    @Override   
    public Attack fight(char opponent) { 
        switch(opponent) {
         case 'L':
            return Attack.SCISSORS; 
         case 'B':
            return Attack.SCISSORS;
         case 'S':
            return Attack.PAPER;
        default:
            return Attack.PAPER;
        }
    }

    public Move move() {
        int numWolves = 0, numLions = 0;

        moves++;

        for (int y = 0; y != 3; y++) {
            for (int x = 0; x != 3; x++) {
                if(surroundings[y][x] != ' ') {
                    if(surroundings[y][x] == 'W') {
                        numWolves++;
                    } else if(surroundings[y][x] == 'L') {
                        numLions++;
                    }
                }
            }       
        }

        if (numWolves == 1 && numLions == 0) {
            return Move.HOLD;
        }

        if (surroundings[0][1] == 'L' && moves%2 != 0) {
            return Move.UP;
        } 

        if (surroundings[1][0] == 'L' && moves%2 == 0) {
            return Move.LEFT;
        }

        if (surroundings[0][1] == 'W') {
            if (surroundings[2][1] == ' ' || surroundings[2][1] == 'S') {
                return Move.DOWN;
            } else if (surroundings[1][2] == ' ' || surroundings[1][2] == 'S') {
                return Move.RIGHT;
            } else if (surroundings[1][0] == ' ' || surroundings[1][0] == 'S') {
                return Move.LEFT;
            } else {
                return Move.UP;
            }
        }

        if (surroundings[1][0] == 'W') {
            if (surroundings[1][2] == ' ' || surroundings[1][2] == 'S') {
                return Move.RIGHT;
            } else if (surroundings[0][1] == ' ' || surroundings[0][1] == 'S') {
                return Move.UP;
            } else if (surroundings[2][1] == ' ' || surroundings[2][1] == 'S') {
                return Move.DOWN;
            } else {
                return Move.LEFT;
            }
        }

        if (surroundings[1][2] == 'W') {
            if (surroundings[1][0] == ' ' || surroundings[1][0] == 'S') {
                return Move.LEFT;
            } else if (surroundings[0][1] == ' ' || surroundings[0][1] == 'S') {
                return Move.UP;
            } else if (surroundings[2][1] == ' ' || surroundings[2][1] == 'S') {
                return Move.DOWN;
            } else {
                return Move.RIGHT;
            }
        }

        if (surroundings[2][1] == 'W') {
            if (surroundings[0][1] == ' ' || surroundings[0][1] == 'S') {
                return Move.UP;
            } else if (surroundings[1][0] == ' ' || surroundings[1][0] == 'S') {
                return Move.LEFT;
            } else if (surroundings[1][2] == ' ' || surroundings[1][2] == 'S') {
                return Move.RIGHT;
            } else {
                return Move.DOWN;
            }
        }

        return Move.HOLD;
    }
}

android-ftw

Posted 2014-04-04T18:47:50.283

Reputation: 211

I need to change my pro alpha wolf attacking tactics. If I was holding and got attacked by wolf: Fight with SCISSORS ;) – Ilya_Gazman – 2014-04-08T13:55:03.387

11

The Blind Wolf

The blind wolf is afraid to move and never knows what it is fighting. By playing scissors every time it has the best odds, as it will never run into a real stone.

package animals;

public class BlindWolf extends Animal {
    public BlindWolf() { super('W'); }

    @Override
    public Attack fight(char c) { 
        return Attack.SCISSORS;
    }

    @Override
    public Move move() {
        return Move.HOLD;
    }
}

dtcarls

Posted 2014-04-04T18:47:50.283

Reputation: 119

11

WhenIGrowUp

When this wolf grows up, it wants to be a Lion. So it randomly walks around looking for Lions to follow their footsteps and learn how to be a Lion.

This wolf was designed as a counter to the wolves that swap places with Lions.

package animals;

import java.util.Random;

/**
 *
 * @author Quincunx
 */
public class WhenIGrowUp extends Animal {

    Random r;
    boolean following;
    boolean toggle;

    public WhenIGrowUp() {
        super('W');
        r = new Random();
        following = false;
        toggle = false;
    }

    @Override
    public Attack fight(char c) {
        switch (c) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
            case 'S':
                return Attack.PAPER;
            default:
                return Attack.values()[r.nextInt(4)];
        }
    }

    @Override
    public Move move() {
        if (surroundings[1][2] == 'L') {
            return Move.RIGHT;
        }
        if (surroundings[2][1] == 'L') {
            return Move.DOWN;
        }
        Move direction = Move.values()[r.nextInt(5)];
        out:
        for (int y = 0; y < 3; y++) {
            for (int x = 0; x < 3; x++) {
                if (surroundings[y][x] == 'L') {
                    if (y == 0 && x == 1) {
                        direction = Move.UP;
                    } else if (y == 1 && x == 0) {
                        direction = Move.LEFT;
                    } else {
                        direction = Move.HOLD;
                    }
                    break out;
                }
            }
        }
        return direction;
    }
}

Justin

Posted 2014-04-04T18:47:50.283

Reputation: 17 266

11

SpyWolf

SpyWolf spies on it's enemies and logs their activity so that it can keep tabs on everyone while keeping it's distance. Wouldn't want to be found out!

package animals;

import static animals.Animal.Attack.*;
import static animals.Animal.Move.*;

import java.awt.Point;
import java.util.*;

public class SpyWolf extends Animal {

    private static final Random r = new Random();
    private static boolean hasTestedPRNG = false;
    private static int PRNG = -1;
    private boolean lionTracker = true;
    private boolean useScissors = false;

    private final ArrayList<MapTile> map = new ArrayList<MapTile>();
    private final Point location = new Point();

    public SpyWolf() {
        super('W');
    }

    @Override
    public Animal.Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return SCISSORS;
            case 'S':
                return PAPER;
            default:
                if (useScissors) {
                    useScissors = false;
                    return SCISSORS;
                }
                return PAPER;
        }
    }

    @Override
    public Animal.Move move() {

        Move m = HOLD;

        if (!hasTestedPRNG) {
            hasTestedPRNG = true;
            double d = 0;
            for (int i = 0; i < 100; i++)
                d += Math.random();
            if (d > 99) {
                PRNG = 1;
            } else if (d > 30 && d < 70) PRNG = 0;
        }

        lionTracker = !lionTracker;
        boolean adj = false;

        updateMap();

        scan: {
            if (PRNG < 1) {
                if (look(LEFT) == 'L' && !lionTracker) {
                    useScissors = true;
                    m = LEFT;
                    break scan;
                }

                if (look(UP) == 'L' & lionTracker) {
                    useScissors = true;
                    m = UP;
                    break scan;
                }
            }

            int x = 0, y = 0;
            ArrayList<Move> moves = new ArrayList<Move>(4);

            for (Move i : Move.values())
                moves.add(i);

            if (look(UP) == 'W') {
                y += 54;
                moves.remove(UP);
                adj = true;
            }
            if (look(DOWN) == 'W') {
                y -= 54;
                moves.remove(DOWN);
                adj = true;
            }
            if (look(LEFT) == 'W') {
                x += 54;
                moves.remove(LEFT);
                adj = true;
            }
            if (look(RIGHT) == 'W') {
                x -= 54;
                moves.remove(RIGHT);
                adj = true;
            }

            if (moves.isEmpty() || !adj) break scan;

            for (MapTile t : map) {
                if (t.x >= location.x - 2 && t.x <= location.x + 2 && t.y >= location.y - 2 && t.y <= location.y + 2 && t.d) {
                    int dist = Math.abs(t.x - location.x) + Math.abs(t.y - location.y);
                    y += t.y > location.y ? -60 / dist : 60 / dist;
                    x += t.x < location.x ? 60 / dist : -60 / dist;
                }
            }
            m = moveDir(x, y);
            if (!moves.contains(m)) m = HOLD;
        }
        switch (m) {
            case UP:
                location.y--;
                return m;
            case DOWN:
                location.y++;
                return m;
            case LEFT:
                location.x--;
                return m;
            case RIGHT:
                location.x++;
                return m;
            default:
                return m;
        }
    }

    private void updateMap() {
        for (int y = -1; y < 2; y++)
            xloop: for (int x = -1; x < 2; x++) {
                if (x == 0 && y == 0) continue;
                for (MapTile t : map)
                    if (t.x == x + location.x && t.y == y + location.y) {
                        t.d = surroundings[y + 1][x + 1] == 'W';
                        continue xloop;
                    }
                map.add(new MapTile(x + location.x, y + location.y, surroundings[y + 1][x + 1] == 'W'));
            }
    }

    private Move moveDir(int x, int y) {
        if (x == 0) return y < 0 ? UP : y > 0 ? DOWN : HOLD;
        if (y == 0) return x < 0 ? LEFT : RIGHT;
        if (x < 0) {
            if (y < 0) {
                if (y < x)
                    return UP;
                else if (x < y) return LEFT;
                return r.nextBoolean() ? UP : LEFT;
            } else {
                if (-y < x)
                    return DOWN;
                else if (x < -y) return LEFT;
                return r.nextBoolean() ? DOWN : LEFT;
            }
        }
        if (y < 0) {
            if (y < -x)
                return UP;
            else if (-x < y) return RIGHT;
            return r.nextBoolean() ? UP : RIGHT;
        } else {
            if (y > x)
                return DOWN;
            else if (x < y) return RIGHT;
        return r.nextBoolean() ? DOWN : RIGHT;
        }
    }

    private char look(Move direction) {
        switch (direction) {
            case UP:
                return surroundings[0][1];
            case DOWN:
                return surroundings[2][1];
            case LEFT:
                return surroundings[1][0];
            case RIGHT:
                return surroundings[1][2];
            default:
                return surroundings[1][1];
        }
    }

    private static class MapTile {
        int x, y;
        boolean d;

        MapTile(int x, int y, boolean d) {
            this.x = x;
            this.y = y;
            this.d = d;
        }
    }
}

It fares pretty well, but that lame HybridWolf is too cheaty! SpyWolf may go back to spy school and train advanced anti-wolf techniques, we'll see.

BurntPizza

Posted 2014-04-04T18:47:50.283

Reputation: 357

1You call it lame, i call it intelligent ;) – Manu – 2014-04-11T06:14:50.433

5AHHHHH! So many gotos! And in a language that doesn't even have them! – AJMansfield – 2014-04-12T02:58:53.013

9

HybridWolf

I couldn't resist but to make another wolf. This one is very different (in its code, not in its behaviour), as it chooses the attack/move, which other good wolves would do.
Of course all wolves are good, but I mean the ones with the most points :)

package animals;

import java.util.ArrayList;
import java.util.Random;

public class HybridWolf extends Animal{
    private final Class[] classes = {ProAlpha.class, OmegaWolf.class, SpyWolf.class, HerjanWolf.class, DeepWolf.class, ProtoWolf.class};
    private final ArrayList<Animal> wolves = new ArrayList<Animal>(); 

    public HybridWolf() {
        super('W');
        for(Class c: classes) {
            try {
                wolves.add((Animal)c.newInstance());
            } catch (Exception ex) {}
        }
    }

    @Override
    public Attack fight(char opponent) {
        switch(opponent){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.PAPER;
        default:
            try {
                int[] attacks = new int[3];
                Attack bestAttack = randomAttack();
                for(Animal wolf : wolves) {
                    wolf.surroundings = this.surroundings;
                    attacks[wolf.fight(opponent).ordinal()]++;
                }
                for(int i =0; i < 5; i++) {
                    if(attacks[i] > attacks[bestAttack.ordinal()]) {
                        bestAttack = Attack.values()[i];
                    }
                }
                return bestAttack;
            } catch (Exception e) {
                return randomAttack();
            }
        }
    }

    @Override
    public Move move() {
        try {
            int[] moves = new int[5];
            Move bestMove = Move.HOLD;
            for(Animal wolf : wolves) {
                wolf.surroundings = this.surroundings;
                moves[wolf.move().ordinal()]++;
            }
            for(int i =0; i < 5; i++) {
                if(moves[i] > moves[bestMove.ordinal()]) {
                    bestMove = Move.values()[i];
                }
            }
            return bestMove;
        } catch (Exception e) {
            return Move.HOLD;
        }
    }

    public Attack randomAttack() {
        Random rand = new Random();
        switch (rand.nextInt(3)){
            case 1: return Attack.SCISSORS;
            case 2: return Attack.ROCK;
            default: return Attack.PAPER;
        }
    }

}

It my tests it scores better than my previous AlphaWolf, but Omega/Honor/ProAlpha sometimes beat me... The more good submissions are here, the better this wolf will get :D

Manu

Posted 2014-04-04T18:47:50.283

Reputation: 3 642

This is a little cheeky! clever idea though. Not sure how this will fair as legitimate entry as I have no idea of the tests that Rusher will applying to determine a legitimate entry. i.e. if he does a test in isolation with out the other wolves present then this wolf will fail miserably :P – Moogie – 2014-04-09T21:08:05.860

@Moogie 90% of the entries are legit. So far, I've only excluded entries that changed their letter to something other than 'W', or entries that I couldn't figure out how to compile in another language (and I let them know if that's the case, but you may not see it here because I talk to them in chat). – Rainbolt – 2014-04-10T03:47:04.487

@Rusher there are some entries that try change the odds in their favor. e.g. Gambler wolf changes the random number generator of java's Math.random() to always return 1! Funnily enough it has little impact on results as the winning wolves are the wolves that avoid fighting! – Moogie – 2014-04-10T04:03:28.543

@Moogie GamblerWolf is a legit submission (and pretty clever too). You're right, it didn't change the results much. If things get out of hand, I'll just say "Ok, he wins but here are the result if he WASN'T included." That way everyone else can still enjoy not being obliterated. – Rainbolt – 2014-04-10T04:09:41.023

@Rusher If that is legitimate then I will have to modify my omegawolf to use it's own custom random number generator to avoid that effect. Hmm... I could use it to my own advantage... or i could be altruistic and restore the Math.random() – Moogie – 2014-04-10T04:21:52.717

@Rusher I do implore you to reconsider the legitimacy of the custom number generator trick as one could perform the same trick to replace all the wolves in the simulation with their own wolf! – Moogie – 2014-04-10T04:39:40.963

2@Moogie If something is not specifically forbidden in the specifications of the challenge it is de facto legitimate, and the tradition on this site is to not change the rules once people have posted answers to it. – plannapus – 2014-04-10T06:19:14.950

@plannapus ok I yield to the conventions of this site as I am the newbie here. It just seems that this tradition encourages solutions that go against the spirit of the competition. No big deal I guess. – Moogie – 2014-04-10T06:34:57.480

@Moogie it just encourage the posting of challenges that don't have loopholes: this is why it is encouraged to try out new challenges in the sandbox before posting them on the main site. Welcome to [codegolf.SE] by the way! :)

– plannapus – 2014-04-10T06:37:19.940

@plannapus I would like to point out that a spec could say "Anything not specifically allowed is banned." if they wanted to avoid specifically forbidding every possible loophole. You have to be careful how you say it though. – Rainbolt – 2014-04-11T03:28:41.283

Hey creators of OmegaWolf, DeepWolf, HonorWolf, ShadowWolf, AlphaWolf and ProAlpha, what about we replace our wolves right before the new scoreboard comes up with suicide wolves and upload our wolves under a new name? :) – Herjan – 2014-04-11T16:38:01.683

@Herjan I guess I am not an exploitive kind of guy. I would rather play by the spirit of the rules knowing that I am able to produce a solution that does not "cheat" or "bend the rules". It is the challenge that brought on by following the spirit of the rules that gives the pleasure. If a "cheating" bot wins that is not such a problem for me. We could always redo the challenge and make the rules such that "cheating" is allowed but those solutions would not be competing against the "legitimate" solutions... – Moogie – 2014-04-13T21:59:48.507

@Herjan infact I would like to do something similar again. Perhaps with only slight deviations in the rules to allow for more complex behaviour: i.e. wolves have HP and reward attacking by transfering HP from losing wolf to winning wolf. And have have a constant HP lose per simulation tick to reduce the boring tactic of not moving/avoiding conflict. – Moogie – 2014-04-13T23:17:05.840

@Moogie actually, I like the rules as they are, I like the boring tactic of avoiding conflict and you can make that HP idea really cool I actually came up with some ideas myself but just enjoy this game instead of redoing it because I think Hybrid is just smart instead of cheating (Even though I hate it so much that I made Hybrid, my enemy, myself) – Herjan – 2014-04-14T17:00:50.347

@Herjan I do agree it is the simplicity of the rules that makes it challenging and fun. But it is a competition to see which wolf can do the least :P and then it comes down to the random placement of the wolves in the world that will have the largest influence on how many wolves will survive :P There is no reason to attempt to destroy, or manoeuvre other wolves to destroy them selves as the risk is too great. – Moogie – 2014-04-14T22:23:53.357

9

EvoWolf

All you silly intelligently designed wolves! EvoWolf lives in the Wild with other tough wolves like DeepWolf and HerjanWolf so it had to evolve to survive.

The code uses a genetic algorithm to evolve the best wolf (I didn't notice LionHunterWolf until I was grabbing Wolves to train against). The are different genes for each animal/attack combo, movement direction when safe, and movement direction for each animal in a surrounding. After 1000 rounds, the wolves which lasted the highest number of turns have the highest probability of producing offspring (i.e. the longer you live the more chances you get to mate). We also throw in a random mutation in about 10% of children an hope it helps.

Here is the EvoWolf code, YOU WILL ALSO NEED evowolf.txt IN YOUR WORKING DIRECTORY - IT CONTAINS THE CURRENT GENEPOOL. If you want to evolve your own wolves from primordial randomness, don't include evowolf.txt but the one provided is currently the best evolution. It's really neat to watch it evolve, at the beginning only 2-3 survive but then it gets up to 60.

package animals;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;

public class EvoWolf extends Animal {
    public EvoWolf() { super('W'); birth();}
    public Attack fight(char c) { 
        List<Attack> attacks = getAttacks(c);
        if(attacks.size() == 0)
            return Attack.SUICIDE; //Discourage wolves without attacks, Darwin Award

        return attacks.get(random.nextInt(attacks.size()));
    }

    public Move move() {
        ++turns;
        List<Move> moves = new ArrayList<Move>();
        if(isSafe())
            moves = getSafeMoves();
        else
            moves = getThreatenedMoves();

        return (Move)moves.toArray()[random.nextInt(moves.size())];
    }

    /*====STATIC METHODS====*/
    //Shared RNG
    public static Random random = new Random();

    //Collection of 100 sets of genes
    public static String[] genePool = null;

    //Get the genes from disk or randomly generate some
    public static void readGenePool(){
        genePool = new String[100];
        int gIdx = 0;
        try (BufferedReader br = new BufferedReader(new FileReader("evowolf.txt"))){
            String sCurrentLine; 
            while ((sCurrentLine = br.readLine()) != null) {
                genePool[gIdx] = sCurrentLine;
                ++gIdx;
            }
        } catch (IOException e) {

        } 

        //if can't read genes, make some
        if(gIdx < 100){
            primordial(gIdx);
        }
    }
    public static void primordial(int idx){
        for(;idx < 100; ++idx){
            genePool[idx] = getRandomGenes();
        }
    }

    public static String getRandomGenes(){
        StringBuilder sb = new StringBuilder();
        for(int idx = 0; idx < GENE_COUNT; ++idx){
            if(random.nextBoolean())
                sb.append("1");
            else
                sb.append("0");
        }
        return sb.toString();
    }

    //Evolve wolves
    public static void nextGen(){
        //Check survival of current gen
        int survivors = 0;
        for(int idx = 0; idx < 100; ++idx){
            survivors = survivors + (generation[idx].turns == 1000 ? 1 : 0);
        }
        if(survivors > 65)
            writeGenePool(Long.toString(survivors));

        //Weighted resivour sampling
        //Take the highest of r^(1/w) where r is a random an w is the weight
        for(int idx = 0; idx < 100; ++idx){
            genePool[idx] = mateFitWolves();
        }
        writeGenePool("");
        birthCount = 0;
    }

    //Pick two wolves randomly by weighted fitness and mate them
    public static String mateFitWolves(){
        EvoWolf w1 = null;
        double weight1 = -1;
        EvoWolf w2 = null;
        double weight2 = -1;

        for(int idx = 0; idx < 100; ++idx){
            double weight = generation[idx].getWeightSample();
            if(weight > weight1){
                weight2 = weight1;
                w2 = w1;
                weight1 = weight;
                w1 = generation[idx];
            } else if(weight > weight2){
                weight2 = weight;
                w2 = generation[idx];
            }
        }

        return mateFitWolves(w1, w2);
    }

    //Make offspring
    public static String mateFitWolves(EvoWolf w1, EvoWolf w2){
        StringBuilder sb = new StringBuilder();
        //Random splice
        for(int rIdx = 0; rIdx < w1.genes.length(); ++rIdx){
            if(random.nextBoolean())
                sb.append(w1.genes.charAt(rIdx));
            else
                sb.append(w2.genes.charAt(rIdx));
        }


        //Random mutation
        while(random.nextInt(10) == 0){
            int mIdx = random.nextInt(w1.genes.length());
            if(sb.charAt(mIdx) == '0')
                sb.setCharAt(mIdx, '1');
            else
                sb.setCharAt(mIdx, '0');
        }


        return sb.toString();
    }

    //Save the next generation's gene pool back to disk
    public static void writeGenePool(String survivors){
        try {
            String str = "";
            if(!survivors.equals(""))
                str = Long.toString(System.currentTimeMillis());

            File file = new File("evowolf" + survivors + str + ".txt");

            // if file doesn't exists, then create it
            if (!file.exists()) {
                file.createNewFile();
            }

            FileWriter fw = new FileWriter(file.getAbsoluteFile());
            BufferedWriter bw = new BufferedWriter(fw);
            for(int gIdx = 0; gIdx < genePool.length; ++gIdx){
                bw.write(genePool[gIdx]);
                bw.write('\n');
            }
            bw.close();
        } catch (IOException e) {

        }
    }

    //Keep track of the wolves in this generation
    public static int birthCount = 0;
    public static EvoWolf[] generation = new EvoWolf[100];

    /*====INSTANCE METHODS====*/
    //Populate this wolf from the gene pool
    public void birth(){
        if(genePool == null){
            readGenePool();
        }
        genes = genePool[birthCount];
        generation[birthCount] = this;
        birthCount = (birthCount + 1) % 100;    
    }

    //How long wolf has been alive
    public int turns = 0;

    //Fitness based on how long wolf survived
    public double getWeightSample(){
        return Math.pow(random.nextDouble(), 1.0/turns);
    }


    /*===GENETICS===*/
    public String genes = null;
    //Genes are made up of 182+ bits (stored at a string)
    //Each turns on the possibility of that move or attack in a given situation
    //  Attack: BLSW * RPSX = 16 bits [0-15] = Animal->Attacks
    //  Threatened Moves: BLSW * 12345678 * UDLRH = 160 bits [16-175] = Y -> X -> Animal -> Moves
    //  Safe Moves: UDLRH = 5 bits [176-180] = Moves
    //  Extra: default move [181], move combination [182]
    public static final int GENE_INDEX_ATTACKS = 0;
    public static final int GENE_INDEX_THREATENED_MOVES = GENE_INDEX_ATTACKS + (4 * 4);
    public static final int GENE_INDEX_SAFE_MOVES = GENE_INDEX_THREATENED_MOVES + (8 * 4 * 5);
    public static final int GENE_INDEX_DEFAULT_MOVE = GENE_INDEX_SAFE_MOVES + (5);
    public static final int GENE_INDEX_COMBINE_MOVES = GENE_INDEX_DEFAULT_MOVE + (1);
    public static final int GENE_COUNT = GENE_INDEX_COMBINE_MOVES + 1;
    public static int getAnimalIndex(char c){
        switch (c) {
            case 'B':
                return 0;
            case 'L':
                return 1;
            case 'S':
                return 2;
            case 'W':
            default: //Shouldn't occur but we'll assume it's the dangerous wolf
                return 3;
        } 
    }

    public static int getXYIndex(int x, int y){
        int idx = (y * 3) + x;
        if(idx > 4) //We don't need to look at ourself
            --idx;
        return idx;
    }

    public List<Attack> getAttacks(char c){
        List<Attack> attacks = new ArrayList<Attack>();
        int idx = GENE_INDEX_ATTACKS + getAnimalIndex(c);
        if(genes.charAt(idx + 0) == '1')
            attacks.add(Attack.ROCK);
        if(genes.charAt(idx + 1) == '1')
            attacks.add(Attack.PAPER);
        if(genes.charAt(idx + 2) == '1')
            attacks.add(Attack.SCISSORS);
        /*
        if(genes.charAt(idx + 3) == '1')
            attacks.add(Attack.SUICIDE);
        */
        //Suicide didn't remove itself from the gene pool like I thought so I manually removed it

        return attacks;
    }

    public boolean isSafe(){
        for(int x = 0; x <= 2; ++x){
            for(int y = 0; y <= 2; ++y){
                if(y == 1 && x == 1)
                    continue;
                if(surroundings[y][x] != ' ')
                    return false;
            }
        }
        return true;
    }

    public List<Move> getSafeMoves(){
        List<Move> moves = new ArrayList<Move>();
        int idx = GENE_INDEX_SAFE_MOVES;
        if(genes.charAt(idx + 0) == '1')
            moves.add(Move.UP);
        if(genes.charAt(idx + 1) == '1')
            moves.add(Move.DOWN);
        if(genes.charAt(idx + 2) == '1')
            moves.add(Move.LEFT);
        if(genes.charAt(idx + 3) == '1')
            moves.add(Move.RIGHT);
        if(genes.charAt(idx + 4) == '1')
            moves.add(Move.HOLD);

        return moves;
    }

    public List<Move> getThreatenedMoves(){
        List<Move> moves = new ArrayList<Move>();
        if(genes.charAt(GENE_INDEX_COMBINE_MOVES) == '0')
            moves.addAll(EnumSet.of(Move.UP, Move.DOWN, Move.LEFT, Move.RIGHT, Move.HOLD));

        for(int x = 0; x <= 2; ++x){
            for(int y = 0; y <= 2; ++y){
                if(y == 1 && x == 1)
                    continue;
                if(genes.charAt(GENE_INDEX_COMBINE_MOVES) == '1')
                    moves.addAll(getThreatenedMoves(x,y));
                else
                    moves.retainAll(getThreatenedMoves(x,y));
            }
        }

        if(moves.size() == 0){
            if(this.genes.charAt(GENE_INDEX_DEFAULT_MOVE) == '1')
                moves.addAll(EnumSet.of(Move.UP, Move.DOWN, Move.LEFT, Move.RIGHT, Move.HOLD));
            else
                moves.add(Move.HOLD);
        }

        return moves;
    }

    public EnumSet<Move> getThreatenedMoves(int x, int y){
        //Lookup what moves we can make for a cell unless it is blank (allow any)
        if(surroundings[y][x] != ' ')
            return getThreatenedMoves(x,y,surroundings[y][x]);
        else if(genes.charAt(GENE_INDEX_COMBINE_MOVES) == '1')
            return EnumSet.noneOf(Move.class);
        else
            return EnumSet.of(Move.UP, Move.DOWN, Move.LEFT, Move.RIGHT, Move.HOLD);
    }

    public EnumSet<Move> getThreatenedMoves(int x, int y, char c){
        int aIdx = getAnimalIndex(c);
        int sIdx = getXYIndex(x,y);
        int idx = GENE_INDEX_THREATENED_MOVES + (sIdx * 20) + (aIdx * 5);

        EnumSet<Move> moves = EnumSet.noneOf(Move.class);

        if(genes.charAt(idx + 0) == '1')
            moves.add(Move.UP);
        if(genes.charAt(idx + 1) == '1')
            moves.add(Move.DOWN);
        if(genes.charAt(idx + 2) == '1')
            moves.add(Move.LEFT);
        if(genes.charAt(idx + 3) == '1')
            moves.add(Move.RIGHT);
        if(genes.charAt(idx + 4) == '1')
            moves.add(Move.HOLD);

        return moves;
    }

    public static String setAt(String str, int index, char replace){     
        if(str==null){
            return str;
        }else if(index<0 || index>=str.length()){
            return str;
        }
        char[] chars = str.toCharArray();
        chars[index] = replace;
        return String.valueOf(chars);       
    }
}

I've also made some changes to Statistics.java and Wild.java just to show me how many turns and generations have passed. After you've run 1000 turns, call EvoWolf.nextGen(); to calculate offspring. This isn't necessary for competition, only if you want to evolve your own set.

All Files Here EDIT: FIXED LINK

As far as evolving as the best, it kind of doesn't get any better than the top 10. Part of the limitation is it has very little memory of it's previous moves. Although it does function like WolvesWithCollectiveMemory in that the experiences of previous generations will affect how the next generation functions serving as a long term global memory. It sure was fun though. In the previous link there is an Excel sheet which can help you analyze the gene pool. Replace all 1's and 0's in the .txt with 1's and 0's with commas then paste in the spreadsheet.

Some interesting notes, most of which confirm everyone's strategies:

  • The actual attacks are less important than avoiding fighting. Or maybe all the non-wolves get eliminated quickly so they aren't a threat. The competition generation has an even chance between RPS against bears even though you should throw S.
  • Like the above, I had to manually disable suicide since it wasn't evolving out even though you think it would.
  • Holding is the best move when no one is around
  • Running away seems to be good too when someone is around
  • You should hold instead of moving a random direction (this choice was an extra gene that evolved out)
  • When more than 1 animal is around, taking a random move out of the intersection of moves for each surrounding/animal is better than the union (another extra gene)

MatthewMMorrow

Posted 2014-04-04T18:47:50.283

Reputation: 91

8

SmartWolf

The results are in(1000 iterations)(I'll keep updating this but view it as an independent test with no averaging because with that many wolves it is quite slow).

enter image description here

Compilation:

*nix(Mono is required):

gmcs SmartWolf.cs

Windows:

csc SmartWolf.cs

Copy to working directory.

Note: When using Windows, you need to replace "mono SmartWolf.exe" with just "SmartWolf.exe" in the wrapper code.

SmartWolf.cs:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SmartWolf
{
    #region Enums
    enum Attack
    {
        Rock, Paper, Scissors, Suicide
    }
    enum Movement
    {
        Up, Down, Left, Right, Hold
    }
    enum Animal
    {
        Stone, Lion, Wolf, Bear, Empty
    }
    #endregion
    class KnowledgeBase
    {
        static Random rnd = new Random();
        public List<KeyValuePair<KeyValuePair<Animal, Attack>, int>> knowledge = new List<KeyValuePair<KeyValuePair<Animal, Attack>, int>>();
        public KnowledgeBase ()
        {
        }
        public void PunishMove (KeyValuePair<Animal, Attack> move)
        {
            if (knowledge.Count (t => t.Key.Key == move.Key && t.Key.Value == move.Value) == 0) {
                knowledge.Add (new KeyValuePair<KeyValuePair<Animal, Attack>, int> (move, -1));
            } else {
                int i = knowledge.FindIndex (t => t.Key.Equals (move));
                knowledge[i] = new KeyValuePair<KeyValuePair<Animal, Attack>, int>(knowledge[i].Key, knowledge[i].Value - 1);
            }

        }
        public void RewardMove (KeyValuePair<Animal, Attack> move)
        {
            if (knowledge.Count (t => t.Key.Key == move.Key && t.Key.Value == move.Value) == 0) {
                knowledge.Add (new KeyValuePair<KeyValuePair<Animal, Attack>, int> (move, 1));
            } else {
                int i = knowledge.FindIndex (t => t.Key.Equals (move));
                knowledge[i] = new KeyValuePair<KeyValuePair<Animal, Attack>, int>(knowledge[i].Key, knowledge[i].Value + 1);
            }
        }
        public Attack GetBestMove (Animal opponent)
        {
            Attack best = GetRandomMove();
            int j = 0;
            foreach (var pair in knowledge) {
                if(pair.Key.Key == opponent && j < pair.Value)
                {
                    best = pair.Key.Value;
                    j = pair.Value;
                }
            }
            if(j < 2)
                return GetRandomMove ();
            return best;
        }
        public static Attack GetRandomMove()
        {
            int r = rnd.Next (3);
            return r == 0 ? Attack.Paper :
                r == 1 ? Attack.Rock :
                    r == 2 ? Attack.Scissors :
                    Attack.Scissors;
        }
    }
    class MainClass
    {
        static KnowledgeBase knowledge = new KnowledgeBase();
        public static void Main (string[] args)
        {
            List<SmartWolf> list = new List<SmartWolf> ();
            List<int> temp = new List<int>();
            int l = 0;
            while (true) {
                string str = Console.ReadLine ();
                int id = int.Parse (str.Substring (1, 2));
                if(str.StartsWith ("S"))
                {
                    list.Add (new SmartWolf(id));
                    Console.WriteLine("K" + id.ToString ().PadLeft (2, '0'));
                } else if(str.StartsWith ("M"))
                {
                    if(temp.Contains (id))
                    {
                        for(int i = 0; i < 100; i++)
                        {
                            SmartWolf s = list.Where (t => t.ID == i).ToList ()[0];
                            if(s.AttackedInLastRound == 0 && !temp.Contains(i))
                            {
                                s.IsAlive = false;
                                knowledge.PunishMove (s.LastMove);
                                s.AttackedInLastRound = -1;
                            } else if(s.AttackedInLastRound == 0 && temp.Contains (i))
                            {
                                knowledge.RewardMove (s.LastMove);
                                s.AttackedInLastRound = -1;
                            }
                            if(s.AttackedInLastRound > 0)
                                s.AttackedInLastRound--;
                        }
                        temp.Clear();
                        l++;
                    }
                    temp.Add (id);

                    Console.WriteLine('H' + id.ToString ().PadLeft (2, '0'));
                } else if(str.StartsWith ("A"))
                {
                    Animal enemy = str[3] == 'W' ? Animal.Wolf :
                                   str[3] == 'L' ? Animal.Lion :
                                   str[3] == 'S' ? Animal.Stone :
                                   str[3] == 'B' ? Animal.Bear : Animal.Empty;
                    Attack atk = knowledge.GetBestMove (enemy);
                    Console.WriteLine((atk == Attack.Paper ? "P" :
                                      atk == Attack.Rock ? "R" : 
                                      atk == Attack.Scissors ? "S" :
                                      "P") + id.ToString ().PadLeft (2, '0'));
                    list.Where (t => t.ID == id).ToList ()[0].AttackedInLastRound = 2;
                    list.Where (t => t.ID == id).ToList ()[0].LastMove = new KeyValuePair<Animal, Attack>(enemy, atk);
                }
            }
        }
    }
    class SmartWolf
    {
        public int ID;
        public bool IsAlive = true;
        public KeyValuePair<Animal, Attack> LastMove = new KeyValuePair<Animal, Attack>(Animal.Empty, Attack.Suicide);
        public int AttackedInLastRound = -1;
        public SmartWolf(int n)
        {
            ID = n;
        }
    }
}

Wrapper(credit to @ProgrammerDan, I'm just including it here so it's easier to just copy paste):

package animals;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Remote SmartWolf wrapper class. 
 */
public class SmartWolf extends Animal {
    /**
     * Simple test script that sends some typical commands to the
     * remote process.
     */
    public static void main(String[]args){
        SmartWolf[] wolves = new SmartWolf[100];
        for(int i=0; i<10; i++) {
            wolves[i] = new SmartWolf();
        }
        char map[][] = new char[3][3];
        for (int i=0;i<9;i++)
            map[i/3][i%3]=' ';
        map[1][2] = 'W';
        for(int i=0; i<10; i++) {
            wolves[i].surroundings=map;
            System.out.println(wolves[i].move());
        }
        for(int i=0; i<10; i++) {
            System.out.println(wolves[i].fight('S'));
            System.out.println(wolves[i].fight('B'));
            System.out.println(wolves[i].fight('L'));
            System.out.println(wolves[i].fight('W'));
        }
        wolfProcess.endProcess();
    }
    private static WolfProcess wolfProcess = null;

    private static SmartWolf[] wolves = new SmartWolf[100];
    private static int nWolves = 0;

    private boolean isDead;
    private int id;

    /**
     * Sets up a remote process wolf. Note the static components. Only
     * a single process is generated for all Wolves of this type, new
     * wolves are "initialized" within the remote process, which is
     * maintained alongside the primary process.
     * Note this implementation makes heavy use of threads.
     */
    public SmartWolf() {
        super('W');
        if (SmartWolf.wolfProcess == null) {
            SmartWolf.wolfProcess = new WolfProcess();
            SmartWolf.wolfProcess.start();
        }

        if (SmartWolf.wolfProcess.initWolf(SmartWolf.nWolves, MAP_SIZE)) {
            this.id = SmartWolf.nWolves;
            this.isDead = false;
            SmartWolf.wolves[id] = this;
        } else {
            SmartWolf.wolfProcess.endProcess();
            this.isDead = true;
        }
        SmartWolf.nWolves++;
    }

    /**
     * If the wolf is dead, or all the wolves of this type are dead, SUICIDE.
     * Otherwise, communicate an attack to the remote process and return
     * its attack choice.
     */
    @Override
    public Attack fight(char opponent) {
        if (!SmartWolf.wolfProcess.getRunning() || isDead) {
            return Attack.SUICIDE;
        }
        try {
            Attack atk = SmartWolf.wolfProcess.fight(id, opponent);

            if (atk == Attack.SUICIDE) {
                this.isDead = true;
            }

            return atk;
        } catch (Exception e) {
            System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
            isDead = true;
            return Attack.SUICIDE;
        }
    }

    /**
     * If the wolf is dead, or all the wolves of this type are dead, HOLD.
     * Otherwise, get a move from the remote process and return that.
     */
    @Override
    public Move move() {
        if (!SmartWolf.wolfProcess.getRunning() || isDead) {
            return Move.HOLD;
        }
        try {
            Move mv = SmartWolf.wolfProcess.move(id, surroundings);

            return mv;
        } catch (Exception e) {
            System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
            isDead = true;
            return Move.HOLD;
        }
    }

    /**
     * The shared static process manager, that synchronizes all communication
     * with the remote process.
     */
    static class WolfProcess extends Thread {
        private Process process;
        private BufferedReader reader;
        private PrintWriter writer;
        private ExecutorService executor;
        private boolean running;

        public boolean getRunning() {
            return running;
        }

        public WolfProcess() {
            process = null;
            reader = null;
            writer = null;
            running = true;
            executor = Executors.newFixedThreadPool(1);
        }

        public void endProcess() {
            running = false;
        }

        /**
         * WolfProcess thread body. Keeps the remote connection alive.
         */
        public void run() {
            try {
                System.out.println("Starting SmartWolf remote process");
                ProcessBuilder pb = new ProcessBuilder("mono SmartWolf.exe".split(" "));
                pb.redirectErrorStream(true);
                process = pb.start();
                System.out.println("SmartWolf process begun");
                // STDOUT of the process.
                reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); 
                System.out.println("SmartWolf reader stream grabbed");
                // STDIN of the process.
                writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
                System.out.println("SmartWolf writer stream grabbed");
                while(running){
                    this.sleep(0);
                }
                reader.close();
                writer.close();
                process.destroy(); // kill it with fire.
                executor.shutdownNow();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("SmartWolf ended catastrophically.");
            }
        }

        /**
         * Helper that invokes a read with a timeout
         */
        private String getReply(long timeout) throws TimeoutException, ExecutionException, InterruptedException{
            Callable<String> readTask = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return reader.readLine();
                }
            };

            Future<String> future = executor.submit(readTask);
            return future.get(timeout, TimeUnit.MILLISECONDS);
        }

        /**
         * Sends an initialization command to the remote process
         */
        public synchronized boolean initWolf(int wolf, int map_sz) {
            while(writer == null){
                try {
                this.sleep(0);
                }catch(Exception e){}
            }
            boolean success = false;
            try{
                writer.printf("S%02d%d\n", wolf, map_sz);
                writer.flush();
                String reply = getReply(5000l);
                if (reply != null && reply.length() >= 3 && reply.charAt(0) == 'K') {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        success = true;
                    }
                }
                if (reply == null) {
                    System.out.println("did not get reply");
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("SmartWolf %d failed to initialize, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("SmartWolf %d failed to initialize, %s\n", wolf, e.getMessage());
            }
            return success;
        }

        /**
         * Send an ATTACK command to the remote process.
         */
        public synchronized Attack fight(int wolf, char opponent) {
            Attack atk = Attack.SUICIDE;
            try{
                writer.printf("A%02d%c\n", wolf, opponent);
                writer.flush();
                String reply = getReply(1000l);
                if (reply.length() >= 3) {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        switch(reply.charAt(0)) {
                            case 'R':
                                atk = Attack.ROCK;
                                break;
                            case 'P':
                                atk = Attack.PAPER;
                                break;
                            case 'S':
                                atk = Attack.SCISSORS;
                                break;
                            case 'D':
                                atk = Attack.SUICIDE;
                                break;
                        }
                    }
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("SmartWolf %d failed to attack, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("SmartWolf %d failed to attack, %s\n", wolf, e.getMessage());
            }
            return atk;
        }

        /**
         * Send a MOVE command to the remote process.
         */
        public synchronized Move move(int wolf, char[][] map) {
            Move move = Move.HOLD;
            try{
                writer.printf("M%02d", wolf);
                for (int row=0; row<map.length; row++) {
                    for (int col=0; col<map[row].length; col++) {
                        writer.printf("%c", map[row][col]);
                    }
                }
                writer.print("\n");
                writer.flush();
                String reply = getReply(1000l);
                if (reply.length() >= 3) {
                    int id = Integer.valueOf(reply.substring(1));
                    if (wolf == id) {
                        switch(reply.charAt(0)) {
                            case 'H':
                                move = Move.HOLD;
                                break;
                            case 'U':
                                move = Move.UP;
                                break;
                            case 'L':
                                move = Move.LEFT;
                                break;
                            case 'R':
                                move = Move.RIGHT;
                                break;
                            case 'D':
                                move = Move.DOWN;
                                break;
                        }
                    }
                }
            } catch (TimeoutException ie) {
                endProcess();
                System.out.printf("SmartWolf %d failed to move, timeout\n", wolf);
            } catch (Exception e) {
                endProcess();
                System.out.printf("SmartWolf %d failed to move, %s\n", wolf, e.getMessage());
            }
            return move;
        }
    }
}

Not really that great, averages ~75 survival rate for 1000 iterations with a few of the top wolves.

Uses a ML approach to the solution.

user3188175

Posted 2014-04-04T18:47:50.283

Reputation: 329

I think I understood roughly what your wolf does, but could you explain it in more detail? Also you should probably mention what language it is (I'm sure it's obvious for most people but it was not obvious for me). – plannapus – 2014-04-09T08:13:15.360

It's written in C# and has a knowledge list, which lists each move that has been made against a type of animal and its success rate. When a wolf attacks and dies, that move's success rate is decreased. If it lives, the success rate is increased. The system starts to pick moves from the knowledge base instead of selecting it randomly after a few(20-30) turns. – user3188175 – 2014-04-09T14:48:37.817

1Glad to see you using the wrapper! You should probably include instructions on how to compile your code, and what mono is, for @Rusher to use. – ProgrammerDan – 2014-04-09T16:20:52.087

@ProgrammerDan Thanks! There's a mistake in your post though, SmartWolf uses C#. – user3188175 – 2014-04-09T17:21:16.573

1Markdown fail. If you look at source, the '#' is there, but since it was in a heading block, Markdown ignored it. Fixed. :D – ProgrammerDan – 2014-04-09T17:31:35.743

2FYI - I was able to compile and successfully run it with @ProgrammerDan's wrapper (and without his help this time too). You'll be in the next results. Sorry for the delay! – Rainbolt – 2014-04-10T03:34:13.860

@user3188175 I think adding the explanation of what your wolf does to your post would be far more valuable than showing your test runs (since Rusher will update the leaderboard frequently anyhow, but no one else than you can explain what you tried to achieve). That being said: congrats on a very successful wolf! – plannapus – 2014-04-10T06:57:59.850

7

Passive-Aggressive Wolf (a Scala Wolf)

Avoids everything it can for the first 500 turns, letting the field clear itself. Then goes off in it's assigned direction attacking things that it comes within range of.

package animals;

import animals._
import scala.util.Random

class PassiveAgressiveWolf extends Animal('W') {

    val myId=PassiveAgressiveWolf.nextId
    var movecounter=0

    def fight(opponent: Char) = {
        PassiveAgressiveWolf.lastopponents(myId-1)=opponent
        opponent match {
            case 'B' => Animal.Attack.SCISSORS
            case 'L' => Animal.Attack.SCISSORS
            case 'S' => Animal.Attack.ROCK
            case _ => Random.shuffle(List(Animal.Attack.SCISSORS, Animal.Attack.ROCK, Animal.Attack.PAPER)).head
        }
    }

    def move = {
        movecounter+=1
        if(movecounter < 500) avoidall else seen match {
            case ('B', pos: Int) => seenbear(pos)
            case ('S', pos: Int) => seenstone(pos)
            case ('L', pos: Int) => seenlion(pos)
            case ('W', pos: Int) => seenwolf(pos)
            case (' ', _) => myDirection
        }
    }

    def myDirection = myId % 4 match {
        case 0 => if(surroundings(0)(1)==' ') Animal.Move.LEFT else randommove
        case 1 => if(surroundings(1)(0)==' ') Animal.Move.DOWN else randommove
        case 2 => if(surroundings(1)(2)==' ') Animal.Move.RIGHT else randommove
        case 3 => if(surroundings(2)(1)==' ') Animal.Move.UP else randommove
    }

    def randommove = Random.shuffle(List(Animal.Move.UP, Animal.Move.LEFT, Animal.Move.RIGHT, Animal.Move.DOWN)).head

    def seen = {
        surroundings(1)(1)=' '
        val surroundingsflat=surroundings.flatten.mkString
        val seenbeasts = for {
            beast <- "BSLW" if surroundingsflat contains beast
        } yield (beast, surroundingsflat.indexOf(beast))
        seenbeasts.headOption.getOrElse((' ', 0))
    }

    def seenbear(pos: Int) = chase(pos)

    def seenstone(pos: Int) = pos match {
        case 1 => Animal.Move.LEFT
        case 3 => Animal.Move.UP
        case _ => myDirection
    }

    def seenlion(pos: Int) = pos match {
        case 1 => Animal.Move.LEFT
        case 3 => Animal.Move.UP
        case 5 => Animal.Move.HOLD
        case 7 => Animal.Move.HOLD
        case 0 => Animal.Move.UP
        case 2 => Animal.Move.HOLD
        case 6 => Animal.Move.HOLD
        case 8 => Animal.Move.HOLD
    }

    def seenwolf(pos: Int) = chase(pos)

    def chase(pos: Int) = pos match {
        case 1 => Animal.Move.UP
        case 3 => Animal.Move.LEFT
        case 5 => Animal.Move.RIGHT
        case 7 => Animal.Move.DOWN
        case 0 => Animal.Move.UP
        case 2 => Animal.Move.UP
        case 6 => Animal.Move.DOWN
        case 8 => Animal.Move.DOWN
    }

    def avoidall = {
        val safemoves = for {
            move <- List(
                            (0, 1, Animal.Move.UP), 
                            (1, 0, Animal.Move.LEFT), 
                            (1, 2, Animal.Move.RIGHT), 
                            (2, 1, Animal.Move.DOWN)
                        ) if(surroundings(move._1)(move._2)==' ')
        } yield move
        if(safemoves.length < 4) Random.shuffle(safemoves).head._3 else Animal.Move.HOLD
    }

}

object PassiveAgressiveWolf {
    private var id=0
    private def nextId = {id+=1; id}

    private var lastopponents=Array.fill[Char](100)(' ');
}

As a JVM based language, Scala can be integrated relatively easily.

If you're compiling the program yourself, put the scala file in with the java .class files (not the .java files) and use

scalac PassiveAggressiveWolf.scala

to compile it. You can then use the PassiveAggressiveWolf.class in the main Wild.java class as you do with the Java classes. You'll need to add the scala-library.jar to your classpath too (I've been using the command line option -cp /path/to/scala-library.jar).

Alternatively, I've uploaded a jar containing the generated class files and the scala-library.jar for Scala 2.10.3 so that you can download them.

PassiveAggressiveWolf.jar
scala-library.jar

Gareth

Posted 2014-04-04T18:47:50.283

Reputation: 9 575

Maybe you can pack the 10 files into a single zip file to ease downloading. – johnchen902 – 2014-04-08T11:05:10.930

@johnchen902 It was suggested to me in chat that I create a jar to contain my classes. I'll be looking into doing that this evening after work. That'll reduce it to 2 downloads and hopefully solve the issues that Rusher is having with including it. – Gareth – 2014-04-08T11:21:38.893

7

LionHunterWolf

The infamous lion hunter pack is a rare breed of mutating wolves who have a tunnel vision instinct for killing lions. Genetics have given rise to two types, proactive and reactive types. Proactives are the go-getters, looking for lions across the map. Reactives are sly lurkers, keeping to its own territory until a lion invades, and then BAM! It pounces.

LionHunterWolf seeks to align itself with other wolves for the sake of wolf-kind, recognizing that all wolves have their own purpose in computational biology, even if they are different. It seeks to put itself at risk to keep the data-wilds safe for wolf-kind, and therefore hopes that wolf-kind does not try to make it extinct. This is the true survival strategy of LionHunterWolf!

import java.util.Random;

import animals.Animal;

/**
 * Creates alternating breeds of lion hunting wolves. Both breeds do their best
 * to kill all lions, without influence from other animals. One breed will take the
 * proactive approach and wander until it finds lions or dies. Another breed
 * will take the reactive approach and "hides" until it finds a lion it can
 * capture.
 * 
 * Genetic mutation is key to continued existence in real life, so I figured
 * that I would allow for some genetic variance to ensure the survival of my own
 * species of LionHunters.
 */
public class LionHunterWolf extends Animal {
    // pseudo-random number generator
    private static Random r = new Random();
    // logical moves for a shot at victory (remove suicide option)
    private static Attack[] al = new Attack[] { Attack.ROCK, Attack.PAPER, Attack.SCISSORS };
    // possible wander movements
    private static Move[] ml = new Move[] { Move.UP, Move.DOWN, Move.RIGHT, Move.LEFT };
    // alternating lazy tracker (for wolf creation)
    private static boolean lazyFlag = false;

    // is lion class on its down phase?
    private boolean lionDown;
    // am I a go getter or a lazy bum??
    private boolean lazy;

    public LionHunterWolf() {
    super('W');
    lionDown=true;
    lazy = lazyFlag;
    lazyFlag = !lazyFlag;
    }

    // choose a random direction and move, trying to find lions
    private Move wander() {
        return lazy ? Move.HOLD : ml[r.nextInt(ml.length)];
    }

    // look for lions or attack them
    private Move lionOrWander() {
        if (surroundings[0][0] == 'L') {
            // top left
            // we got him! attack!
            return lionDown ? Move.LEFT : Move.UP;
        } else if (surroundings[0][1] == 'L') {
            // top
            // he's right above us
            // hold if we will get attacked next turn
            // otherwise, move right with him.
            return lionDown ? Move.HOLD : Move.RIGHT;
        } else if (surroundings[0][2] == 'L') {
            // top right
            // if he's on his down phase, we can get him.
            // pounce!
            if (lionDown)
                return Move.RIGHT;
        } else if (surroundings[1][0] == 'L') {
            // left
            // hold if we will get attacked next turn
            // otherwise, move down with him.
            return lionDown ? Move.DOWN : Move.HOLD;
        } else if (surroundings[1][1] == 'L') {
            // middle
            // should be impossible, assume this never happens
        } else if (surroundings[1][2] == 'L') {
            // right
        } else if (surroundings[2][0] == 'L') {
            // bottom left
            // if he's on his right phase, we can get him.
            // pounce!
            if (!lionDown)
                return Move.DOWN;
        } else if (surroundings[2][1] == 'L') {
            // bottom
        } else if (surroundings[2][2] == 'L') {
            // bottom right
        }

        // we can't get him, so let's just wander
        return wander();
    }

    @Override
    public Attack fight(char c) {
        switch (c) {
        case 'L': // ~75% victory with scissors (optimal)
        case 'B': // 100% victory with scissors (optimal)
            return Attack.SCISSORS;
        case 'S': // 100% victory with paper (optimal)
            return Attack.PAPER;
        case 'W': // ~50% victory (optimal without predictive analytics)
        default:
            return al[r.nextInt(al.length)];
        }
    }

    @Override
    public Move move() {
        // predict lion movement this iteration
        lionDown = !lionDown;
        return lionOrWander();
    }
}

Update: After some testing, I've found that LionHunterWolf's surviving members are all lazy. This seems to be the dominant strategy, as any chance encounters with competitors are out of my control with the current rules. If I make them all lazy I can get around 40-65 ending wolves, but then they don't completely wipe out all of the lions. Will do some further refinement later, perhaps I should make some wolf avoiding strategy on a second priority to lions, since most wolves won't attempt to fight a lion if given the choice, I can be sure that attacking a lion will have low risk of also fighting a wolf. In this way, my wandering wolves are less likely to randomly bump into lazy wolves or even themselves, although lazy wolves will dominate unless someone goes after them :(

Sheph

Posted 2014-04-04T18:47:50.283

Reputation: 171

1When the lion is on top right, why do you move right only when the lion is going down? Even if the lion is going right, you should move right with him also, and in the next round you can get him. – justhalf – 2014-04-11T04:16:31.083

Hmm good thought. justhalf. I imagine I can do the reverse when he is bottom left as well. Thank you! :) – Sheph – 2014-04-11T13:37:09.307

7

LoneWolf, aka 'The Highlander'

v1.0: Always holds. Optimal attack pattern: 75% survival vs L, 50% vs W, 100% otherwise (CamoWolf notwithstanding).

v2.0: Included some move logic to improve lone survival. Estimates survival chances for each potential move. As you can see by commented out code, I was going to try and guess the moves of a neighbour 'W', but my attempts did not yield better results than assuming equal distribution.

Originally I wasn't going to update this, as it is clearly just a fun entry. But I thought I could do better than 60% survival rate (although that is still better than some...)

package animals;

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

public class LoneWolf extends Animal {
    private final static Attack[] attacks = Attack.values();
    private final static Move[] moves = Move.values();
    private final static Random random = new Random();
    private static boolean highlander = false;

    private boolean lionDown = false;

    public LoneWolf() throws InstantiationException {
        super('W'); 
        if (!highlander) {
            highlander = true;
        } else {
            throw new InstantiationException("There can only be one!");
        }
    }

    @Override
    public Attack fight(char c) {
        switch (c) {
            case 'B':
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            case 'W':
            default:
                return attacks[random.nextInt(3)];
        }
    }

    @Override
    public Move move() {
        // Lions are deterministic.
        lionDown = !lionDown;

        // Wolves are not...

        double[] spaceSurvival = new double[5]; // up, right, down, left, hold
        double[] enemyMoves = new double[5];
        for (int i = 0; i < 5; ++i) {
            spaceSurvival[i] = 1.0;
            enemyMoves[i] = 0.2;   // ... assume equal chance.
        }

        switch (surroundings[0][0]) {   // top left
            case 'L':
                if (lionDown) {
                    spaceSurvival[Move.LEFT.ordinal()] *= 0.75;
                } else {
                    spaceSurvival[Move.UP.ordinal()] *= 0.75;
                }
                break;
            case 'W':
                //enemyMoves = PredictWolfMoves(0,0);
                spaceSurvival[Move.LEFT.ordinal()] *= ((1.0 - enemyMoves[Move.DOWN.ordinal()]) * 0.5);
                spaceSurvival[Move.UP.ordinal()] *= ((1.0 - enemyMoves[Move.RIGHT.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[0][1]) {   // top
            case 'L':
                if (lionDown) {
                    spaceSurvival[Move.HOLD.ordinal()] *= 0.75;
                }
                break;
            case 'W':
                //enemyMoves = PredictWolfMoves(0,1);
                spaceSurvival[Move.HOLD.ordinal()] *= ((1.0 - enemyMoves[Move.DOWN.ordinal()]) * 0.5);
                spaceSurvival[Move.UP.ordinal()] *= ((1.0 - enemyMoves[Move.HOLD.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[0][2]) {   // top right
            case 'L':
                if (lionDown) {
                    spaceSurvival[Move.RIGHT.ordinal()] *= 0.75;
                }
                break;
            case 'W':
                //enemyMoves = PredictWolfMoves(0,2);
                spaceSurvival[Move.RIGHT.ordinal()] *= ((1.0 - enemyMoves[Move.DOWN.ordinal()]) * 0.5);
                spaceSurvival[Move.UP.ordinal()] *= ((1.0 - enemyMoves[Move.LEFT.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[1][0]) {   // left
            case 'L':
                if (!lionDown) {
                    spaceSurvival[Move.HOLD.ordinal()] *= 0.75;
                }
                break;
            case 'W':
                //enemyMoves = PredictWolfMoves(1,0);
                spaceSurvival[Move.LEFT.ordinal()] *= ((1.0 - enemyMoves[Move.HOLD.ordinal()]) * 0.5);
                spaceSurvival[Move.HOLD.ordinal()] *= ((1.0 - enemyMoves[Move.RIGHT.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[1][2]) {   // right
            case 'W':
                //enemyMoves = PredictWolfMoves(1,2);
                spaceSurvival[Move.RIGHT.ordinal()] *= ((1.0 - enemyMoves[Move.HOLD.ordinal()]) * 0.5);
                spaceSurvival[Move.HOLD.ordinal()] *= ((1.0 - enemyMoves[Move.LEFT.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[2][0]) {   // bottom left
            case 'L':
                if (!lionDown) {
                    spaceSurvival[Move.DOWN.ordinal()] *= 0.75;
                }
                break;
            case 'W':
                //enemyMoves = PredictWolfMoves(2,0);
                spaceSurvival[Move.LEFT.ordinal()] *= ((1.0 - enemyMoves[Move.UP.ordinal()]) * 0.5);
                spaceSurvival[Move.DOWN.ordinal()] *= ((1.0 - enemyMoves[Move.RIGHT.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[2][1]) {   // bottom
            case 'W':
                //enemyMoves = PredictWolfMoves(2,1);
                spaceSurvival[Move.HOLD.ordinal()] *= ((1.0 - enemyMoves[Move.UP.ordinal()]) * 0.5);
                spaceSurvival[Move.DOWN.ordinal()] *= ((1.0 - enemyMoves[Move.HOLD.ordinal()]) * 0.5);
                break;
        }

        switch (surroundings[2][2]) {   // bottom right
            case 'W':
                //enemyMoves = PredictWolfMoves(2,2);
                spaceSurvival[Move.RIGHT.ordinal()] *= ((1.0 - enemyMoves[Move.UP.ordinal()]) * 0.5);
                spaceSurvival[Move.DOWN.ordinal()] *= ((1.0 - enemyMoves[Move.LEFT.ordinal()]) * 0.5);
                break;
        }

        Move bestMove = Move.HOLD;
        for (int i = 0; i < spaceSurvival.length; ++i) {
            if (spaceSurvival[i] > spaceSurvival[bestMove.ordinal()]) {
                bestMove = moves[i];
            }
        }

        return bestMove;
    }
}

mike32

Posted 2014-04-04T18:47:50.283

Reputation: 171

+1 for "Why not?" – Geobits – 2014-04-15T18:49:36.387

You get honorable mention if I ever run a single test run where the 1 Highlander Wolf actually lives. – Rainbolt – 2014-04-23T03:13:42.703

@Rusher While I was happy with 0.6 average result, I hope some movement logic will improve this! – mike32 – 2014-04-23T09:28:36.643

6

ECMAScript (also known as JavaScript):

Running under Node.js (for the standard input/output, which ECMAScript doesn't have by default).

var returnAttack = {
    'L': 'RS',
    'B': 'S',
    'S': 'P',
    'W': 'PRS'
};

function initialise(chunk, id) {
    return 'K' + id + '\n';
};

function move(chunk, id) {
    var squares = chunk.slice(3, 11);

    // Idea: Move into the space if there isn't a wolf in it

    // Move up
    if (chunk[4] !== 'W') return 'U' + id + '\n';

    // Move left
    if (chunk[6] !== 'W') return 'L' + id + '\n';

    // Move below
    if (chunk[10] !== 'W') return 'B' + id + '\n';

    // Move right
    if (chunk[8] !== 'W') return 'R' + id + '\n';

    // Hold the space
    return 'H' + id + '\n';
};

function attack(chunk, id) {
    var attack = returnAttack[chunk[3]];

    if (attack.length > 1 && Math.random() > .8) {
        return 'D' + id + '\n';
    } else if (attack.length > 1) {
        return attack[Math.floor(Math.random() * attack.length)] + id + '\n';
    } else {
        return attack + id + '\n';
    }
};

var functions = {
    'S': initialise,
    'M': move,
    'A': attack
};

var stdin = process.openStdin();

stdin.setEncoding('utf-8');

stdin.on('data', function(chunk) {
    if (chunk.length === 0) return;

    process.stdout.write(functions[chunk[0]](chunk, chunk.slice(1, 3)));
});

Use @ProgrammerDan's wrapper.

Save as toothbrush.js. Invoke using node toothbrush.js.

As far as I understand, the code doesn't need to be golfed. However, please inform me via the comments if I'm wrong.

Note: I've not tested it, as I've not got Java dev. installed, but it should work.

Toothbrush

Posted 2014-04-04T18:47:50.283

Reputation: 3 087

@ProgrammerDan Thanks about the typo. I was converting the ID to a number on the second-to-last line with +chunk.slice(1, 2). I've corrected that, too. – Toothbrush – 2014-04-07T18:06:24.907

I'm still getting errors when attacking from the line if (attack.length &gt; 1 &amp;&amp; Math.random() &gt; .8) { -- the error is TypeError: Cannot read property 'length' of undefined. Something seems wrong with the way you are looking up the attack response. – ProgrammerDan – 2014-04-10T01:12:03.813

@ProgrammerDan OK. I think that I've finally got the code right. I've tried it (without Java), and it seems to work fine. – Toothbrush – 2014-04-10T18:37:16.410

Awesome! I'll give it a go a little later. – ProgrammerDan – 2014-04-10T19:32:00.587

your random math is a bit off, but other than that things seem good. I'm going to patch up your random code, and edit your answer to reflect it. I'll also post it in a Gist and provide the link here shortly. – ProgrammerDan – 2014-04-11T01:34:57.730

@Rusher Here's the javascript file that works, and the java wrapper as a Gist

– ProgrammerDan – 2014-04-11T01:58:05.663

@ProgrammerDan Got it working. – Rainbolt – 2014-04-11T03:22:40.853

@ProgrammerDan Thank you very much. I don't know why I didn't just use Math.round in the first place. – Toothbrush – 2014-04-14T15:14:33.237

@toothbrush No worries, I'm glad your submission is now working and competing! Let me know if you update your answer, and I'll be happy to keep the Gist up to date. – ProgrammerDan – 2014-04-14T18:53:16.153

6

MOSHPITFRENZYWolf

I present to you the MOSHPITFRENZYWolf! He doesn't give a single damn about what's going on. he just moshes and moshes around to thrash metal. A submission just for fun

package animals;
public class MOSHPITFRENZYWolf extends Animal {

static Move lastMove;

public MOSHPITFRENZYWolf() {
    super('W');
    lastMove = Move.UP;
}

@Override
public Attack fight(char other) {
    switch (other) {
        case 'B':
            return Attack.SCISSORS;
        case 'L':
            return Attack.ROCK;
        case 'S':
            return Attack.PAPER;
        default:
            return Attack.ROCK;
    }
}

@Override
public Move move() {
    if (lastMove == Move.UP) {
        lastMove = Move.RIGHT;
        return Move.RIGHT;
    }
    if (lastMove == Move.RIGHT) {
        lastMove = Move.DOWN;
        return Move.DOWN;
    }
    if (lastMove == Move.DOWN) {
        lastMove = Move.LEFT;
        return Move.LEFT;
    }
    if (lastMove == Move.LEFT) {
        lastMove = Move.UP;
        return Move.UP;
    }
    return Move.HOLD;
}

}

Theoxarhs2099

Posted 2014-04-04T18:47:50.283

Reputation: 89

I think you forgot to actually put the last move in memory didn't you? And shouldn't if(lastMove=Move.UP) be if(lastMove==Move.UP)? Otherwise, funny idea. – plannapus – 2014-04-07T11:38:52.213

Thanks for the info, fix'd it. I was kinda absent minded – Theoxarhs2099 – 2014-04-07T11:39:46.487

Funny idea. Welcome to [codegolf.se]! – plannapus – 2014-04-07T11:41:56.620

Pleased to meet you all :) – Theoxarhs2099 – 2014-04-07T11:50:57.427

Just some code cleanliness advice... maybe switch on lastMove and return lastMove = Move.CurrMove? – recursion.ninja – 2014-04-11T19:04:51.577

Not a bad idea actually. To be honest, that was my second idea. The first one was an array that I'd itterate. Thanks for the advice :) – Theoxarhs2099 – 2014-04-14T06:26:11.683

6

For fun... ForrestWolf

package animals;

public class ForrestWolf extends Animal {
private int runForrestRun;
private Move firstMove;
private Move nextMove;
private boolean isFirstMove;

public ForrestWolf() { 
    super('W'); 
    runForrestRun = (int)(Math.random() * 9); 
    setUpMove(runForrestRun);
}
@Override
public Attack fight(char c) {
    switch (c) {
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S':
            return Attack.PAPER;
        default:
            return Attack.values()[(int) (Math.random() * 3)];
    } 
}
@Override
public Move move() {
    Move move = isFirstMove ? firstMove : nextMove;
    isFirstMove = !isFirstMove;
    return move;        
}
private void setUpMove(int dir){
    switch(dir){
        case 0:
            firstMove = Move.LEFT;
            nextMove = Move.UP;
            break;
        case 1:
            firstMove = nextMove = Move.UP;
            break;
        case 2:
            firstMove = Move.RIGHT;
            nextMove = Move.UP;
            break;
        case 3:
            firstMove = nextMove = Move.LEFT;
            break;
        case 4:
            firstMove = nextMove = Move.HOLD;
            break;
        case 5:
            firstMove = nextMove = Move.RIGHT;
            break;
        case 6:
            firstMove = Move.DOWN;
            nextMove = Move.LEFT;
            break;
        case 7:
            firstMove = nextMove = Move.DOWN;
            break;
        case 8:
            firstMove = Move.DOWN;
            nextMove = Move.RIGHT;
            break;
    }
}
}

Picks a direction and just keeps going - if it runs into anything makes a stab at fighting but if I get time I'd ideally like it to attempt to run around the critter and get back on course.

Alterations, suggestions and critique all welcome.

DaveParsons

Posted 2014-04-04T18:47:50.283

Reputation: 219

1I corrected 'bool' to 'boolean' and 'var' to 'Move'. Your code wouldn't compile otherwise. I went ahead and edited your post too. If that was a mistake, I apologize and you can undo the edit yourself. – Rainbolt – 2014-04-10T03:55:06.797

Thanks, I was rushing, using notepad, and haven't wrote Java for about 10 years so I appreciate the fixes. – DaveParsons – 2014-04-10T08:32:39.417

You should really have a default: on your switch in setUpMove(), in the presence of GamblerWolf, firstMove and secondMove wind up both being null, generating a NPE and halting the sim. Either that, or use a Random instance instead of Math.random() – BurntPizza – 2014-04-11T21:21:21.683

6

I'm surprised I didn't see anyone post this, so here it is:

This is a version of Wild.java that speeds up the implementation by many times. It was taking ~3 minutes with the number of wolves I had, but with this it takes < 4 seconds. It simply calculates the result before going into swing:

package wild;

import animals.*;
import javax.swing.JFrame; //star imports are bad!
import javax.swing.JLabel;

public class Wild {

    private static final Class[] classes
            = { //Listed like this to make commenting out easy: CTRL-/
                Bear.class,
                Lion.class,
                Stone.class,
                Wolf.class
            };
    public static final int MAP_SIZE = Math.round((float) Math.sqrt(classes.length + 3) * 20);

    public static void main(String[] args) {

        int size = MAP_SIZE; //This was redundant. Removed.
        Game game = new Game(size);

        Statistics stats = new Statistics(game, classes);

        for (Class c : classes) {
            game.populate(c, 100);
        }
        stats.update();

        for (int i = 0; i < 1000; i++) {
            game.iterate();
            stats.update();
        }
        //If you want to run multiple times and average, run until here several times.
        JFrame frame = new JFrame();
        frame.add(new JLabel(stats.toString()));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Here is a version that will output the average of ten runs (rounded to the nearest integer):

package wild;

import animals.*;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Wild {

    private static final Class[] classes
            = {
                Bear.class,
                Lion.class,
                Stone.class,
                Wolf.class
            };
    public static final int MAP_SIZE = Math.round((float) Math.sqrt(classes.length + 3) * 20);

    public static void main(String[] args) {

        int size = MAP_SIZE;
        double[] avgs = new double[classes.length];
        for (int i = 0; i < avgs.length; i++) {
            avgs[i] = 0;
        }
        for (int n = 0; n < 10; n++) { // change 10 to the number of runs you'd like to make.
            Game game = new Game(size);

            Statistics stats = new Statistics(game, classes);

            for (Class c : classes) {
                game.populate(c, 100);
            }
            stats.update();

            for (int i = 0; i < 1000; i++) {
                game.iterate();
                stats.update();
            }
            int[] livings = stats.nums();

            if (n != 0) {
                for (int i = 0; i < livings.length; i++) {
                    avgs[i] = (avgs[i] * n + livings[i]) / (n + 1);
                }
            } else {
                for (int i = 0; i < livings.length; i++) {
                    avgs[i] = livings[i];
                }
            }
        }
        String s = "<html>";
        for (int i = 0; i < classes.length; i++) {
            s += classes[i] + "&nbsp;-&nbsp;" + Math.round(avgs[i]) + "<br>";
        }
        s += "</html>";

        JFrame frame = new JFrame();
        frame.add(new JLabel(s));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}

Justin

Posted 2014-04-04T18:47:50.283

Reputation: 17 266

Current rankings (Java-only, I don't have the means to do the non-java ones): OmegaWolf: 86 HybridWolf: 82 HerjanWolf: 81 HonorWolf: 79 ProAlpha: 79 ShadowWolf: 73 AlphaWolf: 72 StoneEatingWolf: 71 LazyWolf: 49 CamperWolf: 48 BlindWolf: 41 GatheringWolf: 33 Sheep: 33 LionHunterWolf: 25 EmoWolf: 21 WolfWithoutFear: 15 GamblerWolf: 14 MOSHPITFRENZYWolf: 9 ForrestWolf: 5 MigratingWolf: 4 Stone: 2 MimicWolf: 1 Wion: 0 WhenIGrowUp: 0 Bear: 0 WolfRunningWithScissors: 0 Lion: 0 – Justin – 2014-04-10T07:04:25.317

This time I ran the test 100 times: OmegaWolf: 85 HybridWolf: 83 HerjanWolf: 83 ProAlpha: 80 HonorWolf: 78 ShadowWolf: 73 AlphaWolf: 71 StoneEatingWolf: 70 LazyWolf: 49 CamperWolf: 47 BlindWolf: 43 Sheep: 34 GatheringWolf: 31 LionHunterWolf: 23 EmoWolf: 21 GamblerWolf: 14 WolfWithoutFear: 14 MOSHPITFRENZYWolf: 9 ForrestWolf: 6 MigratingWolf: 3 Stone: 2 MimicWolf: 1 Bear: 0 Lion: 0 WolfRunningWithScissors: 0 Wion: 0 WhenIGrowUp: 0 – Justin – 2014-04-10T07:10:55.350

1:) indeed. There is so many wolves just standing still that it is not that surprising though. One funny thing is that many people write wolves that interact specifically with lions when statistically they will maybe encounter one or two during a run at most (100 lions versus ca. 3000 wolves at this point). – plannapus – 2014-04-10T07:12:15.747

1@plannapus That's why removing those wolves from the test doesn't really change the ranking. Now I'm trying adding in many more Lions. – Justin – 2014-04-10T07:15:15.690

@plannapus the results after adding 10 lions per 100 of every wolf added (some wolves moved around in the ranking some): OmegaWolf: 84 HerjanWolf: 83 HybridWolf: 80 ProAlpha: 79 HonorWolf: 72 AlphaWolf: 70 ShadowWolf: 68 StoneEatingWolf: 67 LazyWolf: 45 CamperWolf: 42 BlindWolf: 38 Sheep: 29 GatheringWolf: 29 LionHunterWolf: 16 EmoWolf: 15 WolfWithoutFear: 14 GamblerWolf: 12 MOSHPITFRENZYWolf: 7 ForrestWolf: 6 MigratingWolf: 2 Stone: 2 MimicWolf: 1 Lion: 0 WolfRunningWithScissors: 0 Bear: 0 Wion: 0 WhenIGrowUp: 0 – Justin – 2014-04-10T07:20:28.237

Surprisingly similar – plannapus – 2014-04-10T07:21:44.643

Nice tests, I already made that 'avgs of 10 rounds, 1000 iterations' loop for myself, works great for testing indeed. Sad how Im not first in the lion test, I originally built it so it was 99.99% lionproof. And I like the idea to make the lions (and maybe stones/bears as well) increase depending on the amount of wolves. – Herjan – 2014-04-10T12:08:57.050

1This is nice for testing only Java submissions. It breaks when you include the R or the C# submission along with ProgrammerDan's wrapper class, because they depend on the program restarting between runs. – Rainbolt – 2014-04-10T13:25:21.470

@Rusher Then just rerun whatever runs when ProgrammerDan's class starts. – Justin – 2014-04-10T18:48:11.130

6

EndbringerWolf

If no wolves survive, that's kind of like winning, right?

package animals;

import java.lang.Runtime;

public class EndbringerWolf extends Animal {

public EndbringerWolf() {
        super('W');
    }
    public Attack fight(char opponent) { return Attack.SUICIDE; }
    public Move move() { Runtime.getRuntime().halt(0);
        return Move.HOLD; }

}

Sconibulus

Posted 2014-04-04T18:47:50.283

Reputation: 161

Doesn't compile (not joking). – Rainbolt – 2014-04-11T18:08:36.563

Oops, had the wrong library for Runtime as I tried to distill it. – Sconibulus – 2014-04-11T18:12:50.453

error: unreported exception IOException; must be caught or declared to be thrown. How will you know what my process is called anyway? – Rainbolt – 2014-04-11T18:13:32.153

It looks like he's assuming 1) you're running on windows 2) you compiled Wild to an exe instead of just running it with java. – Geobits – 2014-04-11T18:14:46.223

@Rusher I was kind of assuming you wouldn't actually try to run it, so I didn't spend as much time on it as I ought to have. :) – Sconibulus – 2014-04-11T18:23:19.647

2Well, even with the cheating or "underhanded" wolves, I try to at least get it to run. I surrounded the Runtime statement with a try catch. If you just figure out how to get the name of the "current process" then your Wolf will work as intended and you'll at least get a +1 from me (even if I don't include it). – Rainbolt – 2014-04-11T18:26:59.300

2Try System.exit(0). Credit to @Geobits. – Rainbolt – 2014-04-11T18:32:55.887

@Rusher Be prepared, the next step will be an attempt at rm -rf / – user80551 – 2014-04-11T18:34:47.280

@Rusher Updated: System.Exit would allow a finalizer to prevent the shutdown, wouldn't it? While going through the runtime can stop the execution in a far more unsafe, but more certain manner? – Sconibulus – 2014-04-11T18:48:58.417

@Sconibulus Works like a charm now. You're probably right. I didn't know a finalize method existed. I knew the garbage collector called it, but I didn't connect those dots. – Rainbolt – 2014-04-11T18:54:55.657

5

The Wolf Without Fear

The title tells a lot about my wolf. It has no fear. It doesn't run away for other animals (only for wolves): it attacks them! The disadvantage of my wolf is that there are not very many wolves left after 1000 iterations, because while trying to avoid that two wolves attack each other, it can still happen. I could improve that, of course, but I have tried that it didn't help.

package animals;

import java.util.Random;

public class WolfWithoutFear extends Animal {

    Random r = new Random(); // Use a seed if necessary when testing this wolf
    Move nextLionMove = Move.RIGHT;
    Move nextBearMove = Move.LEFT;
    int bearMoves = 4;

    public WolfWithoutFear() {
        super('W');
    }

    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                return Attack.ROCK;
        }
    }

    public Move move() {
        Move m = bestMoveToAvoidWolf();
        if (m != null) {
            return m;
        }
        m = bestMoveForLion();
        if (m != null) {
            return m;
        }
        m = bestMoveForStone();
        if (m != null) {
            return m;
        }
        m = bestMoveForBear();
        if (m != null) {
            return m;
        }
        int n = r.nextInt(5);
        return Move.values()[n];
    }

    Move bestMoveForStone() {
        if (surroundings[0][1] == 'S') {
            return Move.UP;
        }
        if (surroundings[1][2] == 'S') {
            return Move.RIGHT;
        }
        if (surroundings[1][0] == 'S') {
            return Move.LEFT;
        }
        if (surroundings[2][1] == 'S') {
            return Move.DOWN;
        }
        return null;
    }

    Move bestMoveForBear() {
        if (surroundings[0][0] == 'B') {
            if (nextBearMove == Move.RIGHT) {
                return Move.UP;
            }
            if (nextBearMove == Move.DOWN) {
                return Move.LEFT;
            }
            return nextBearMove;
        }
        if (surroundings[0][1] == 'B') {
            if (nextBearMove == Move.DOWN) {
                return Move.HOLD;
            }
            return nextBearMove;
        }
        if (surroundings[0][2] == 'B') {
            if (nextBearMove == Move.DOWN) {
                return Move.RIGHT;
            }
            if (nextBearMove == Move.LEFT) {
                return Move.UP;
            }
            return nextBearMove;
        }
        if (surroundings[1][0] == 'B') {
            if (nextBearMove == Move.RIGHT) {
                return Move.HOLD;
            }
            return nextBearMove;
        }
        if (surroundings[1][2] == 'B') {
            if (nextBearMove == Move.LEFT) {
                return Move.HOLD;
            }
            return nextBearMove;
        }
        if (surroundings[2][0] == 'B') {
            if (nextBearMove == Move.UP) {
                return Move.LEFT;
            }
            if (nextBearMove == Move.RIGHT) {
                return Move.DOWN;
            }
            return nextBearMove;
        }
        if (surroundings[2][1] == 'B') {
            if (nextBearMove == Move.UP) {
                return Move.HOLD;
            }
            return nextBearMove;
        }
        if (surroundings[2][2] == 'B') {
            if (nextBearMove == Move.UP) {
                return Move.RIGHT;
            }
            if (nextBearMove == Move.LEFT) {
                return Move.DOWN;
            }
            return nextBearMove;
        }
        return null;
    }

    Move bestMoveForLion() {
        if (surroundings[0][0] == 'L') {
            return Move.HOLD;
        }
        if (surroundings[0][1] == 'L') {
            return Move.RIGHT;
        }
        if (surroundings[1][0] == 'L') {
            if (nextLionMove == Move.RIGHT) {
                return Move.HOLD;
            } else {
                return Move.DOWN;
            }
        }
        if (surroundings[2][0] == 'L') {
            if (nextLionMove == Move.RIGHT) {
                return Move.DOWN;
            }
        }
        if (surroundings[0][2] == 'L') {
            if (nextLionMove == Move.DOWN) {
                return Move.RIGHT;
            }
        }
        return null;
    }

    Move bestMoveToAvoidWolf() {
        if (surroundings[0][1] == 'W') {
            return Move.DOWN;
        }
        if (surroundings[1][0] == 'W') {
            return Move.RIGHT;
        }
        if (surroundings[1][2] == 'W') {
            return Move.LEFT;
        }
        if (surroundings[2][1] == 'W') {
            return Move.UP;
        }
        return null;
    }

    void predictNextMoves() {
        if (nextLionMove == Move.DOWN) {
            nextLionMove = Move.RIGHT;
        } else {
            nextLionMove = Move.DOWN;
        }

        if (bearMoves == 4) {
            bearMoves = 0;
            switch (nextBearMove) {
                case DOWN:
                    nextBearMove = Move.RIGHT;
                case RIGHT:
                    nextBearMove = Move.UP;
                case UP:
                    nextBearMove = Move.LEFT;
                case LEFT:
                    nextBearMove = Move.DOWN;
            }
        } else {
            bearMoves++;
        }
    }
}

ProgramFOX

Posted 2014-04-04T18:47:50.283

Reputation: 6 554

5

Shadow Wolf

This wolf likes to be alone. If he sans any lion or other wolfs he will try to escape, but he will not run in to attack doing so, if there is no where to go he will hold, until good opportunity will arrive.

I guess the most dangerous thing here is moving...

package animals;

public class ShadowWolf extends Animal {

    private int attackCounter = 0;
    private static final Attack POSIBLE_ATTACKS[] = {
        Attack.PAPER,
        Attack.SCISSORS,
        Attack.ROCK
    };

    public ShadowWolf() {
        super('W');
    }

    @Override
    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            case 'W':
                return wolfAttack();
            default:
                return wolfAttack();
        }
    }

    private Attack wolfAttack() {
        return POSIBLE_ATTACKS[(attackCounter++) % 3];
    }

    @Override
    public Move move() {
        Move move = getLionEscapeMove();
        if(move != null){
            return move;
        }

        move = getWolfEscapeMove();
        if(move != null){
            return move;
        }

        return Move.HOLD;
    }

    private boolean isWolfAround() {
        return
                isWolf(0, 0) || 
                isWolf(0, 1) ||
                isWolf(0, 2) ||
                isWolf(1, 0) ||
//              isWolf(1, 1) || this one is me
                isWolf(1, 2) ||
                isWolf(2, 0) ||
                isWolf(2, 1) ||
                isWolf(2, 2);
    }

    private Move getLionEscapeMove() {
        if(isLion(0,0) || isLion(0, 1) || isLion(1, 0)){
            if(!isLionOrWolf(2,0) && !isLionOrWolf(2,1)){
                return Move.DOWN;
            }
            else if(!isLionOrWolf(0,2)){
                return Move.RIGHT;
            }
        }

        return null;
    }

    private boolean isLion(int y, int x) {
        return surroundings[y][x] == 'L';
    }

    private boolean isWolf(int y, int x) {
        return surroundings[y][x] == 'W';
    }

    private boolean isLionOrWolf(int y, int x) {
        return surroundings[y][x] == 'L' || surroundings[y][x] == 'W';
    }

    private Move getWolfEscapeMove() {
       if(!isWolfAround()){
           return Move.HOLD;
       }
       if(!isLionOrWolf(0, 2) && !isLionOrWolf(1, 2) && !isLionOrWolf(2, 2)){
           return Move.RIGHT;
       }
       if(!isLionOrWolf(0, 0) && !isLionOrWolf(0, 1) && !isLionOrWolf(0, 2)){
           return Move.UP;
       }
       if(!isLionOrWolf(0, 0) && !isLionOrWolf(1, 0) && !isLionOrWolf(2, 0)){
           return Move.LEFT;
       }
       if(!isLionOrWolf(2, 0) && !isLionOrWolf(2, 1) && !isLionOrWolf(2, 2)){
           return Move.DOWN;
       }

       return null;
    }
}

Ilya_Gazman

Posted 2014-04-04T18:47:50.283

Reputation: 484

5

WaryWolf

I m trying to be defensive

package animals;

import java.util.Random;

public class WaryWolf extends Animal{

    public WaryWolf() {
        super('W');
    }
    @Override
    public Attack fight(char opponent) {
        switch(opponent){
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S': 
            return Attack.PAPER;
        default:
            return randomAttack();
        }
    }

    @Override
    public Move move() {
        if(surroundings[0][1] == 'W' && surroundings[2][1] != 'W') {
            return Move.DOWN;
        }
        if(surroundings[1][2] == 'W' && surroundings[1][0] != 'W') {
            return Move.LEFT;
        }
        if(surroundings[2][1] == 'W' && surroundings[0][1] != 'W') {
            return Move.UP;
        }
        if(surroundings[1][0] == 'W' && surroundings[1][2] != 'W') {
            return Move.RIGHT;
        }
        return Move.HOLD;
    }

    public Attack randomAttack() {
        Random rand = new Random();
        switch (rand.nextInt(3)){
            case 1: return Attack.SCISSORS;
            case 2: return Attack.ROCK;
            default: return Attack.PAPER;
        }
    }
}

Uday Shankar

Posted 2014-04-04T18:47:50.283

Reputation: 171

4

Pro Alpha wolf

I been trying hard, but the alpha wolf was biting my Shadow wolfy. So I decided: If you can't beat them, join them!

And so I crafted the Pro Alpha wolf based on @Manu danger idea, with little bit changes, I also give waits to lions.

package animals;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

public class ProAlpha extends Animal {

    private final HashMap<Move, Integer> dangerMap = new HashMap<Move, Integer>();
    private boolean lionMoveDown = true;

    public ProAlpha() {
        super('W');
    }

    @Override
    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                return randomAttack();
        }
    }

    @Override
    public Move move() {

        resetMovesMap();

        final int wolfDanger = 2;
        final int lionDanger = 1;

        lionMoveDown = !lionMoveDown;

        if (surroundings[0][1] == 'L' && lionMoveDown) {
            increasDanger(Move.HOLD, lionDanger);
            increasDanger(Move.UP, lionDanger);
        }
        if (surroundings[1][0] == 'L' && !lionMoveDown) {
           increasDanger(Move.HOLD, lionDanger);
            increasDanger(Move.LEFT, lionDanger);
        }
        if (surroundings[0][1] == 'W') {
            increasDanger(Move.UP, wolfDanger);
            increasDanger(Move.HOLD, wolfDanger);
        }
        if (surroundings[1][2] == 'W') {
            increasDanger(Move.RIGHT, wolfDanger);
            increasDanger(Move.HOLD, wolfDanger);
        }
        if (surroundings[2][1] == 'W') {
            increasDanger(Move.DOWN, wolfDanger);
            increasDanger(Move.HOLD, wolfDanger);
        }
        if (surroundings[1][0] == 'W') {
            increasDanger(Move.LEFT, wolfDanger);
            increasDanger(Move.HOLD, wolfDanger);
        }
        if (surroundings[0][0] == 'W') {
            increasDanger(Move.UP, wolfDanger);
            increasDanger(Move.LEFT, wolfDanger);
        }
        if (surroundings[0][2] == 'W') {
            increasDanger(Move.UP, wolfDanger);
            increasDanger(Move.RIGHT, wolfDanger);
        }
        if (surroundings[2][2] == 'W') {
            increasDanger(Move.DOWN, wolfDanger);
            increasDanger(Move.RIGHT, wolfDanger);
        }
        if (surroundings[1][2] == 'W') {
            increasDanger(Move.DOWN, wolfDanger);
            increasDanger(Move.LEFT, wolfDanger);
        }

        if (dangerMap.get(Move.HOLD) == 0) {
            return Move.HOLD;
        }

        ArrayList<Move> bestMove = new ArrayList<Move>();

        int minemumDanger = 10000;

        minemumDanger = checkDanger(Move.LEFT, minemumDanger, bestMove);
        minemumDanger = checkDanger(Move.RIGHT, minemumDanger, bestMove);
        minemumDanger = checkDanger(Move.UP, minemumDanger, bestMove);
        minemumDanger = checkDanger(Move.DOWN, minemumDanger, bestMove);

        Random rand = new Random();
        int index = rand.nextInt(bestMove.size());

        return bestMove.get(index);
    }

    public Attack randomAttack() {
        Random rand = new Random();
        switch (rand.nextInt(5)) {
            case 2:
                return Attack.SCISSORS;
            case 3:
                return Attack.ROCK;
            default:
                return Attack.PAPER;
        }
    }

    private void resetMovesMap() {
        dangerMap.put(Move.UP, 0);
        dangerMap.put(Move.LEFT, 0);
        dangerMap.put(Move.RIGHT, 0);
        dangerMap.put(Move.DOWN, 0);
        dangerMap.put(Move.HOLD, 0);
    }

    private void increasDanger(Move move, int danger) {
        int currentDanger = dangerMap.get(move);
        dangerMap.put(move, currentDanger + danger);
    }

    private int checkDanger(Move move, int minemumDanger, ArrayList<Move> bestMove) {
        if (dangerMap.get(move) < minemumDanger) {
            bestMove.clear();
            bestMove.add(move);
            return dangerMap.get(move);
        }
        else if(dangerMap.get(move) == minemumDanger){
            bestMove.add(move);
        }

        return minemumDanger;
    }
}

Ilya_Gazman

Posted 2014-04-04T18:47:50.283

Reputation: 484

4

Migrating Wolf

This wolf goes up for several turns and then goes down for several turns. The distance travelled before turning depends on the total number of Migrating Wolf: As there is less migrating wolves the surviving ones move further.

package animals;

public class MigratingWolf extends Animal{

    private static boolean NORTH_MIGRATION = true;
    private static Attack NORTH_ATTACK = Attack.ROCK;
    private static Attack SOUTH_ATTACK = Attack.SCISSORS;
    private static int STEPS = 0;

    public MigratingWolf() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        switch (c){
            case 'L': return getMigrationAttack();
            case 'B': return Attack.SCISSORS;
            case 'W': return getMigrationAttack();
            case 'S': return Attack.PAPER;
            default: return getMigrationAttack();
        }
    }

    private Attack getMigrationAttack(){
        if (MigratingWolf.NORTH_MIGRATION){
            return NORTH_ATTACK;
        } else {
            return SOUTH_ATTACK;
        }
    }

    @Override
    public Move move() {
        //Checks for changing migration direction
        MigratingWolf.STEPS++;
        if (MigratingWolf.STEPS > 500){
            MigratingWolf.STEPS = 0;
            MigratingWolf.NORTH_MIGRATION = !MigratingWolf.NORTH_MIGRATION;
        }

        if (MigratingWolf.NORTH_MIGRATION){
            return Move.UP;
        } else {
            return Move.DOWN;
        }
    }
}

Averroes

Posted 2014-04-04T18:47:50.283

Reputation: 2 298

I strongly recommend that you make your static variables private. You don't want other wolves modifying your variables, do you? – Rainbolt – 2014-04-08T22:06:12.813

Yes, I misread the part where you said other wolves cannot modify files created by a Wolf class. – Averroes – 2014-04-08T22:31:56.257

4

The Mirror Wolf

I just used reflection to make sure your wolves cut themselves with scissors, smash themselves with rock, and cover themselves with paper.

package animals;
import static animals.Animal.Attack;
import java.lang.reflect.*;
public class Mirror extends Animal
{ 
    public Mirror() { super('W'); }
    public Attack fight(char opponent) { return realRock; }
    public Move move() { return Move.values()[new java.util.Random().nextInt(5)]; }
    private static Attack realRock;

    //This is run by the classloader
    static{
        realRock = Attack.ROCK;
        try
        {
            for(String s : "ROCK,PAPER,SCISSORS".split(",")){
                //Get the reflection fields for Attack.ROCK, Attack.PAPER, and Attack.SCISSORS
                Field attackValue = Attack.class.getField(s);

                //Allow us to modify the fields
                attackValue.setAccessible(true);

                //Use reflection on reflection
                Field attackValueModifiers = Field.class.getDeclaredField("modifiers");

                //Allow us to modify the modification
                attackValueModifiers.setAccessible(true);

                //Attack.ROCK, Attack.PAPER, and Attack.SCISSORS are no longer final
                attackValueModifiers.setInt(attackValue, Modifier.FINAL ^ attackValueModifiers.getInt(attackValue));

                //Attack.ROCK, Attack.PAPER, and Attack.SCISSORS are now aliases for Attack.Suicide.  Have fun :)
                attackValue.set(null, Attack.SUICIDE);
            }
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex)
        {
            ex.printStackTrace();
        }
    }
}

Zaq

Posted 2014-04-04T18:47:50.283

Reputation: 1 465

I want this to work as you intended it to, but I just don't know enough about reflection and the NetBeans compiler to understand why your wolf is just losing and the rest of the competition seems is unaffected. I have a hunch that the final static enum that you modify is replaced by the compiler (since it is a constant), and your changes just go unnoticed. Any thoughts, since you are obviously more familiar with it? – Rainbolt – 2014-04-09T03:37:02.220

It will not work. The field is only a copy of actual class filed. Which you cannot modify. I think the fact that Enums are final in Java should give you a hint ;) – Ilya_Gazman – 2014-04-09T05:48:02.470

5public Mirror() { super('M'); } <- I think this is specifically disallowed in the rules. – plannapus – 2014-04-09T08:16:13.193

@plannapus the camo wolf specifically uses super('S') as its strategy, so I think that this is not against any rules, but since you insist I'll go ahead and change it to a 'W' anyway. – Zaq – 2014-04-09T20:19:07.437

@Zaq CamoWolf is a not considered a valid wolf because it uses super('S') – Sahar Rabinoviz – 2014-04-09T20:40:59.687

@Rusher I think that the problem with my code not working is that the classloader runs in order that the classes are used in code. For example, in new FooWolf(); new Mirror(); new BazWolf(), BazWolf will be affected, while FooWolf will not, presumably. I didn't expect this to be a problem with my code. – Zaq – 2014-04-09T20:42:03.597

@DisplayName101 I changed my code to super('W') before you made that comment. – Zaq – 2014-04-09T20:43:35.667

@Zaq I was just clarifying that CamoWolf is not a valid wolf. – Sahar Rabinoviz – 2014-04-09T20:45:10.957

@Zaq Last night, I tried putting your class first in the Class[] classes list, and it didn't affect the results. Just now, I tried removing every other Animal in the animals package, added yours back, and then added the rest, and it didn't make a difference. What other special accommodations your Wolf need? I'm using JDK 8.0 in NetBeans 7.4. Also, thanks for changing your letter back to 'W'. – Rainbolt – 2014-04-09T20:55:06.863

@Rusher I'm not sure exactly in which order the classloader will load the classes, but obviously my code won't do much if it doesn't run before other wolves' classloaders. I'm surprised that it does nothing when put at the beginning of the list. What does it output when you println(Attack.ROCK) after populating the board? – Zaq – 2014-04-09T21:37:43.820

4

AvoidingWolfLionLike is convinced that moving like a lion will solve all his problems.

The only problem is meeting other wolves, so he tries to avoid them (not always successfully, he must say).

N.B. He would have liked to look like a bear (because then, he won all his match-ups), but he soon found out that it was cheating, so... he is just a wolf.

package animals;

import java.util.Random;

/**
 * Lions are dangerous. So, walk like a lion.
 * I avoid Wolves, try not to stay stuck with other amalgams of wolves.
 * 
 * I don't run into lions, and I fight viciously.
 * 
 * I am avoiding wolf lion like.
 * 
 * @author OroshiX
 * 
 */
public class AvoidingWolfLionLike extends Animal {

    private boolean toggle = true;

    public AvoidingWolfLionLike() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        switch (c) {
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S':
            return Attack.PAPER;
        default:
            return attackWolf();
        }
    }

    private Attack attackWolf() {
        Random random = new Random();
        int choice = random.nextInt(3);
        switch (choice) {
        case 0:
            return Attack.PAPER;
        case 1:
            return Attack.ROCK;
        case 2:
            return Attack.SCISSORS;
        default:
            return Attack.SUICIDE; // Haha
        }
    }

    @Override
    public Move move() {
        toggle = !toggle;
        boolean hasToWait = false;

        // I don't like wolves. Wait for them to leave.
        if ((toggle && surroundings[2][1] == 'W')
                || (!toggle && surroundings[1][2] == 'W')) {
            hasToWait = true;
        }

        if (hasToWait) {
            return tryToRunAwayFromWolves();
        }
        // Else move like a lion (Because Lions are cool)
        return toggle ? Move.DOWN : Move.RIGHT;
    }

    /**
     * If he can move Down, then go
     * else if he can move Right, then go
     * else if he can move Left, then go
     * else if he can move Up, then go
     * else just stay
     * 
     * @return
     */
    private Move tryToRunAwayFromWolves() {
        // There is a wolf where we want to go (right or bottom)
        Move move = Move.HOLD;
        if (surroundings[2][1] != 'W') {
            move = Move.DOWN;
        } else if (surroundings[1][2] != 'W') {
            move = Move.RIGHT;
        } else if (surroundings[1][0] != 'W') {
            move = Move.LEFT;
        } else if (surroundings[0][1] != 'W') {
            move = Move.UP;
        }
        return move;
    }
}

OroshiX

Posted 2014-04-04T18:47:50.283

Reputation: 45

4

I thought I would add GeoWolf to the mix. Here he is

package animals;


import java.util.*;

public class GeoWolf extends Animal {

    public static final char WOLF = 'W';
    public static final char BEAR = 'B';
    public static final char LION = 'L';
    public static final char STONE = 'S';


    private static final Random random = new Random();

    private final Coordinate CURRENT_POSITION;

    public GeoWolf() {
        super(GeoWolf.WOLF);

        CURRENT_POSITION = new Coordinate(1,1);
    }

    public Attack fight(char c) {
        switch (c) {
            case GeoWolf.BEAR:
            case GeoWolf.LION:
                return Attack.SCISSORS;
            case GeoWolf.STONE:
                return Attack.PAPER;
            default:
                return Attack.values()[random.nextInt(3)];
        }
    }

    public Move move() {
        final char topLeftOccupier = getOccupier(CURRENT_POSITION.left().up());
        if(isLionOrWolf(topLeftOccupier)) {
            return Move.HOLD;
        }

        final char leftOccupier = getOccupier(CURRENT_POSITION.left());
        if(isLionOrWolf(leftOccupier)) {
            return Move.UP;
        }

        final char topOccupier = getOccupier(CURRENT_POSITION.up());
        if(isLionOrWolf(topOccupier)) {
            return Move.LEFT;
        }

        return Move.HOLD;
    }



    private boolean isLionOrWolf(char occupier) {
        return (occupier == WOLF) || (occupier == LION);
    }

    private char getOccupier(Coordinate coordinate) {
        return surroundings[coordinate.getY()][coordinate.getX()];
    }

    private class Coordinate {
        private final int x;
        private final int y;

        private Coordinate(int x, int y) {
            validate(x);
            validate(y);
            this.x = x;
            this.y = y;
        }

        private void validate(int num) {
            if(num<0 || num>2) {
                final String message = "Cannot create coordinate num = " + num;
                System.err.println(message);
                throw new IllegalArgumentException(message);
            }
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public Coordinate left() {
            return new Coordinate(x-1, y);
        }

        public Coordinate up() {
            return new Coordinate(x, y-1);
        }
    }
}

geoand

Posted 2014-04-04T18:47:50.283

Reputation: 141

3

ChameleonWolf V 1.2: Illegal please read comments.

Especially designed to hunt lazy wolfs ;). Working pretty well on sheep too. The idea is moving as collective. When one wolf decide to move all the wolfs moving in the same direction. So If I decide to hunt my self, I will flee. I implemented this using AtomicInteger to support true multi-threading.

package animals;

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class ChameleonWolf extends Animal {

    private final static Random random = new Random();

    private static final Attack POSIBLE_ATTACKS[] = {
        Attack.SCISSORS,
        Attack.PAPER,
        Attack.ROCK
    };

    public ChameleonWolf() {
        super(randomMe());
    }

    private static char randomMe() {
        // There are many faces of me but stone is the favorites.
        switch (random.nextInt(5)) {
            case 0:
                return 'B';
            case 1:
                return 'L';
            case 2:
                return 'W';
            case 3:
            default:
                return 'S';
        }
    }

    @Override
    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return randomeAttack(Attack.SCISSORS);
            case 'S':
                return randomeAttack(Attack.PAPER);
            case 'W':
                if (this.letter == 'S') {
                    // Anti LazyWolf tactics ;)
                    return randomeAttack(Attack.PAPER);
                } else if (this.letter == 'W') {
                    return randomeAttack(); // Dodge this
                } else {
                    // No one is expecting the beers to rock, but they are!
                    return randomeAttack(Attack.ROCK);
                }
            default:
                return randomeAttack();
        }
    }

    private Attack randomeAttack() {
        int attack = random.nextInt(POSIBLE_ATTACKS.length);
        return POSIBLE_ATTACKS[attack];
    }

    private Attack randomeAttack(Attack preferedAttack) {
        // There is 5% that will use random attack
        if (random.nextInt(100) == 7) {
            return randomeAttack();
        }
        return preferedAttack;
    }

    private static final int MAX_MOVES = 20;
    private static Move groupMove = Move.DOWN;
    private static final AtomicInteger movesCounter = new AtomicInteger(MAX_MOVES);

    @Override
    public Move move() {
        int counter = movesCounter.decrementAndGet();
        if(counter == 0){
            groupMove = null;
            movesCounter.set(MAX_MOVES);
        }

        Move move = groupMove;

        if(move != null){
            return move;
        }

        move = move('W');
        if (move != null) {
            return move;
        }
        move = move('B');
        if (move != null) {
            return move;
        }
        move = move('L');
        if (move != null) {
            return move;
        }
        move = move('S');
        if (move != null) {
            return move;
        }
        move = move(' ');
        if (move != null) {
            return move;
        }

        return Move.DOWN;
    }

    private Move move(char prefferedMove) {
        if (checkSurounding(0, 1, prefferedMove)) {
            groupMove = Move.UP;
            return Move.UP;
        } else if (checkSurounding(1, 2, prefferedMove)) {
            groupMove = Move.RIGHT;
            return Move.RIGHT;
        } else if (checkSurounding(1, 0, prefferedMove)) {
            groupMove = Move.LEFT;
            return Move.LEFT;
        } else if (checkSurounding(2, 1, prefferedMove)) {
            groupMove = Move.DOWN;
            return Move.DOWN;
        }

        return null;
    }

    private boolean checkSurounding(int y, int x, char prefferedMove) {
        return surroundings[x][y] == prefferedMove;
    }
}

Ilya_Gazman

Posted 2014-04-04T18:47:50.283

Reputation: 484

6I can't include this if your Wolf isn't represented by a 'W'. The spec says the Wolf is "Represented by 'W'." – Rainbolt – 2014-04-06T16:49:13.450

@Rusher – that excludes CamoWolf, too, then? Or is this one only rejected based on not cleverly abusing the source restrictions, which would be easy enough to “fix”? – Christopher Creutzig – 2014-04-07T16:36:26.267

7@ChristopherCreutzig CamoWolf will be excluded. Proof: Wolves are represented by 'W'. CamoWolf is represented by 'S'. 'S' is not 'W'. Therefore, CamoWolf is not a Wolf. Proof 2: Wolves submitted as an answer will be included. CamoWolf is not a Wolf. Therefore, it is not the case that CamoWolf will be included. – Rainbolt – 2014-04-07T18:13:41.603

3

BastardWolf

BastardWolf is a little bastard. Kills weaker preys like bears or stones and runs like a coward from wolves and lions.

package animals;

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

public class BastardWolf extends Animal {

    //Random to generate an attack when fighting a Wolf
    private final Random random = new Random();
    //The attacks
    private static final Attack[] attacksForWolf = new Attack[]{Attack.ROCK, Attack.PAPER, Attack.SCISSORS};
    private static final Attack[] attacksForLion = new Attack[]{Attack.ROCK, Attack.SCISSORS};

    public BastardWolf() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        switch (c) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
                return attacksForLion[random.nextInt(attacksForLion.length)];
            case 'S':
                return Attack.PAPER;
            default:
                return attacksForWolf[random.nextInt(attacksForWolf.length)];
        }
    }

    @Override
    public Move move() {

        //Checks what positions are safe or easy kills(Stones, Bears or nothing)
        //Checks what positions are risky (Lions)
        //Checks what positions are really dangerous (Wolves)
        List<Position> safePositions = new ArrayList<>();
        List<Position> riskyPositions = new ArrayList<>();
        List<Position> reallyDangerousPositions = new ArrayList<>();
        if (surroundings[0][1] != 'W' && surroundings[0][1] != 'L') {
            safePositions.add(new Position(0, 1));
        } else if (surroundings[0][1] == 'L') {
            riskyPositions.add(new Position(0, 1));
        } else if (surroundings[0][1] == 'W') {
            reallyDangerousPositions.add(new Position(0, 1));
        }

        if (surroundings[1][0] != 'W' && surroundings[1][0] != 'L') {
            safePositions.add(new Position(1, 0));
        } else if (surroundings[1][0] == 'L') {
            riskyPositions.add(new Position(1, 0));
        } else if (surroundings[1][0] == 'W') {
            reallyDangerousPositions.add(new Position(1, 0));
        }

        if (surroundings[2][1] != 'W' && surroundings[2][1] != 'L') {
            safePositions.add(new Position(2, 1));
        } else if (surroundings[2][1] == 'L') {
            riskyPositions.add(new Position(2, 1));
        } else if (surroundings[2][1] == 'W') {
            reallyDangerousPositions.add(new Position(2, 1));
        }

        if (surroundings[1][2] != 'W' && surroundings[1][2] != 'L') {
            safePositions.add(new Position(1, 2));
        } else if (surroundings[1][2] == 'L') {
            riskyPositions.add(new Position(1, 2));
        } else if (surroundings[1][2] == 'W') {
            reallyDangerousPositions.add(new Position(2, 1));
        }

        Position pos = null;
        //Choses a safe position if any
        if (!safePositions.isEmpty()) {
           //Checks for a position where theres a Stone or a Bear
            for(Position position : safePositions){
                if(surroundings[position.getX()][position.getY()] == 'S' || surroundings[position.getX()][position.getY()] == 'B'){
                    pos = position;
                    break;
                }
            }
            //If there are no Stones or Bears, chooses one randomly
            if(pos == null){
              pos = safePositions.get(random.nextInt(safePositions.size() ));  
            }
        } //If there are no safe position, chooses a risky position
        else if (!riskyPositions.isEmpty()) {
            pos = riskyPositions.get(random.nextInt(riskyPositions.size() ));
        } //If there are no safe nor risky positions, chooses one dangerous.
        else if (!reallyDangerousPositions.isEmpty()) {
            pos = reallyDangerousPositions.get(random.nextInt(reallyDangerousPositions.size() ));
        } //If there are no positions choosen, then Holds
        else {
            return Move.HOLD;
        }

        //Calculates the movement
        if (pos.x == 0 && pos.y == 1) {
            return Move.UP;
        } else if (pos.x == 1 && pos.y == 0) {
            return Move.LEFT;
        } else if (pos.x == 1 && pos.y == 2) {
            return Move.RIGHT;
        } else if (pos.x == 2 && pos.y == 1) {
            return Move.LEFT;
        }
        return Move.HOLD;
    }

    private class Position {

        private int x;
        private int y;

        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }
    }
}

Aitor Gonzalez

Posted 2014-04-04T18:47:50.283

Reputation: 61

I have submitted a change to your wolf to fix array out of bounds when selecting randomly from a list... you were using size-1 where size should be used. – Moogie – 2014-04-14T05:37:51.177

@Moogie thanks for the fix :) – Aitor Gonzalez – 2014-04-14T07:01:20.413

3

FizzBuzzWolf

First of all, really fun game. Has kept me entertained while unable to access the internet. Managed to download the game and LazyWolf before going away, so most testing has been done against LazyWolf. Having a look through now, it is a little similar to some Wolves here, I promise though, this is all from my own head, not having internet until just now, I haven't been able to look at or test against other solutions. It's probably overly complicated and also ugly code (I'm usually C#) but it seems to work quite well. Have tried to comment a lot in case anyone would like to try and figure out what I'm doing.

tl;dr - Weights moves and moves accordingly. Hold is prioritised and avoids other Wolves as much as possible, even if avoiding is somehow a worse move than attacking a Wolf.

package animals;

/**
 * FizzBuzzWolf attempts to be very smart and only move when necessary. When it
 * does move, it attempts to move in the safest direction, avoiding other wolves
 * as much as possible.
 * 
 * @version 1.0
 * @author FizzBuzz
 */
public class FizzBuzzWolf extends Animal
{
    static int last = 0;
    static final Attack[] attacks = Attack.values();

    public FizzBuzzWolf() 
    {
        super('W');
    }

    @Override
    public Attack fight(char c) 
    {
        switch(c)
        {
            case 'B': // Bear attacks with Paper, so fighting with Scissors will always win.
            case 'L': // Lion fights with Paper or Scissors, so fighting with Scissors will win or draw.
                return Attack.SCISSORS;
            case 'S': // Paper always beats Rock.
                return Attack.PAPER;
            default: // Random attack (thankyou LazyWolf).
                return attacks[last++%3];
        }
    }

    @Override
    public Move move() 
    {   
        return DetermineBestAction();
    }

    private int leftDanger;
    private int rightDanger;
    private int topDanger;
    private int downDanger;
    private int holdDanger;

    /**
     * Uses danger ratings to determine which action (a.k.a. Move) is the
     * best/safest.
     * <p>
     * Aims for the lowest danger rating. If there are equal danger ratings, it
     * will attempt to move away from the highest danger. If the lowest danger 
     * is a wolf, it will choose the next lowest danger or hold it's ground.
     * 
     * @return The safest Move to make.
     * @see Move
     */
    private Move DetermineBestAction()
    {
        Move bestMove = Move.HOLD;
        CheckDangers();

        char left = surroundings[1][0];
        char right = surroundings[1][2];
        char up = surroundings[0][1];
        char down = surroundings[2][1];

        // 0 check is minor optimisation to prevent further 4 evaluations
        if (holdDanger != 0 && (holdDanger >= topDanger || holdDanger >= downDanger || holdDanger >= leftDanger || holdDanger >= rightDanger))
        {
            // First four checks should always move in the right direction if one is highest.
            if (topDanger > downDanger && topDanger > leftDanger && topDanger > rightDanger) // Top is the most dangerous, move down.
            {
                if (downDanger <= leftDanger && downDanger <= rightDanger && down != 'W')
                    bestMove = Move.DOWN;
                else
                {
                    if (leftDanger < rightDanger && left != 'W')
                        bestMove = Move.LEFT;
                    else if (right != 'W')
                        bestMove = Move.RIGHT;
                }
            }
            else if (downDanger > topDanger && downDanger > leftDanger && downDanger > rightDanger) // Down is most dangerous, move up.
            {
                if (topDanger <= leftDanger && topDanger <= rightDanger && up != 'W')
                    bestMove = Move.UP;
                else
                {
                    if (leftDanger < rightDanger && left != 'W')
                        bestMove = Move.LEFT;
                    else if (right != 'W')
                        bestMove = Move.RIGHT;
                }
            }
            else if (leftDanger > topDanger && leftDanger > downDanger && leftDanger > rightDanger) // Left is most dangerous, move right.
            {
                if (rightDanger <= topDanger && rightDanger <= downDanger && right != 'W')
                    bestMove = Move.RIGHT;
                else
                {
                    if (topDanger < downDanger && up != 'W')
                        bestMove = Move.UP;
                    else if (down != 'W')
                        bestMove = Move.DOWN;
                }
            }
            else if (rightDanger > topDanger && rightDanger > downDanger && rightDanger > leftDanger) // Right is most dangerous, move left.
            {
                if (leftDanger <= topDanger && leftDanger <= downDanger && left != 'W')
                    bestMove = Move.LEFT;
                else
                {
                    if (topDanger < downDanger && up != 'W')
                        bestMove = Move.UP;
                    else if (down != 'W')
                        bestMove = Move.DOWN;
                }
            }
            else // Some moves are equally dangerous.
            {
                if (topDanger == leftDanger && downDanger == rightDanger)
                {
                    if (topDanger > downDanger)
                    {
                        if (Math.random() > 0.5 && down != 'W')
                            bestMove = Move.DOWN;
                        else if (right != 'W')
                            bestMove = Move.RIGHT;
                    }
                    else
                    {
                        if (Math.random() > 0.5 && up != 'W')
                            bestMove = Move.UP;
                        else if (left != 'W')
                            bestMove = Move.LEFT;
                    }
                }
                else if (topDanger == rightDanger && downDanger == leftDanger)
                {
                    if (topDanger > downDanger)
                    {
                        if (Math.random() > 0.5 && down != 'W')
                            bestMove = Move.DOWN;
                        else if (left != 'W')
                            bestMove = Move.LEFT;
                    }
                    else
                    {
                        if (Math.random() > 0.5 && up != 'W')
                            bestMove = Move.UP;
                        else if (right != 'W')
                            bestMove = Move.RIGHT;
                    }
                }
                else if (topDanger == downDanger && leftDanger == rightDanger)
                {
                    if (topDanger > leftDanger)
                    {
                        if (Math.random() > 0.5 && left != 'W')
                            bestMove = Move.LEFT;
                        else if (right != 'W')
                            bestMove = Move.RIGHT;
                    }
                    else
                    {
                        if (Math.random() > 0.5 && up != 'W')
                            bestMove = Move.UP;
                        else if (down != 'W')
                            bestMove = Move.DOWN;
                    }
                }
                else // 3 sides equal
                {
                    if (topDanger < downDanger && topDanger < leftDanger && topDanger < rightDanger && up != 'W')
                        bestMove = Move.UP;
                    else if (downDanger < topDanger && downDanger < leftDanger && downDanger < rightDanger && down != 'W')
                        bestMove = Move.DOWN;
                    else if (leftDanger < topDanger && leftDanger < downDanger && leftDanger < rightDanger && left != 'W')
                        bestMove = Move.LEFT;
                    else if (right != 'W')
                        bestMove = Move.RIGHT;
                }
            }
        }

        return bestMove;
    }

    /**
     * Weight each action (UP, LEFT, DOWN, RIGHT, HOLD) based on surroundings.
     * <p>
     * Each movement (not HOLD), has an initial danger of 1 as not all cells
     * around the "new" cell are known.
     * If the HOLD action presents no danger, then movements are not checked.
     * A Wolf is danger level 2; This is due to the fact that it can attack with
     * any available action.
     * A Lion has a danger level of 1; This is due to the fact that there is a
     * 50 - 50 chance of winning (fights with Paper or Scissors, so this wolf
     * always uses Scissors against a Lion).
     * A Bear presents no danger as it always fights with Paper, so fighting 
     * with Scissors provides a 100% win rate.
     * A Stone presents no danger as it always fights with Rock, so fighting
     * with Paper provides a 100% win rate.
     */
    private void CheckDangers()
    {
        // Make sure these are reset each time.
        // Moving has a minimum danger as the outside is unknown.
        leftDanger = 1;
        rightDanger = 1;
        topDanger = 1;
        downDanger = 1;
        holdDanger = 0;

        // Directly left
        switch (surroundings[1][0])
        {
            case 'W':
                leftDanger += 2;
                holdDanger += 2;
                break;
            case 'L':
                leftDanger++;
                holdDanger++;
                break;
        }

        // Directly right
        switch (surroundings[1][2])
        {
            case 'W':
                rightDanger += 2;
                holdDanger += 2;
                break;
            case 'L':
                rightDanger++;
                holdDanger++;
                break;
        }

        // Directly above
        switch (surroundings[0][1])
        {
            case 'W':
                topDanger += 2;
                holdDanger += 2;
                break;
            case 'L':
                topDanger++;
                holdDanger++;
                break;
        }

        // Directly below
        switch (surroundings[2][1])
        {
            case 'W':
                downDanger += 2;
                holdDanger += 2;
                break;
            case 'L':
                downDanger++;
                holdDanger++;
                break;
        }

        if (holdDanger > 0) // Only need to look at moving if there is hold danger.
        {
            // Top left corner
            switch (surroundings[0][0])
            {
                case 'W':
                    leftDanger += 2;
                    topDanger += 2;
                    break;
                case 'L':
                    leftDanger++;
                    topDanger++;
                    break;
            }

            // Bottom left corner
            switch (surroundings[2][0])
            {
                case 'W':
                    leftDanger += 2;
                    downDanger += 2;
                    break;
                case 'L':
                    leftDanger++;
                    downDanger++;
                    break;
            }

            // Top right corner
            switch (surroundings[0][2])
            {
                case 'W':
                    rightDanger += 2;
                    topDanger += 2;
                    break;
                case 'L':
                    rightDanger++;
                    topDanger++;
                    break;
            }

            // Bottom right corner
            switch (surroundings[2][2])
            {
                case 'W':
                    rightDanger += 2;
                    downDanger += 2;
                    break;
                case 'L':
                    rightDanger++;
                    downDanger++;
                    break;
            }
        }
    }
}

Trent

Posted 2014-04-04T18:47:50.283

Reputation: 161

2

CautiousWolf

package animals;

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

public class CautiousWolf extends Animal {
    private static final int NW = 0;
    private static final int N = 1;
    private static final int NE = 2;
    private static final int E = 3;
    private static final int SE = 4;
    private static final int S = 5;
    private static final int SW = 6;
    private static final int W = 7;
    private static final int M = 8;

    private static final char LION = 'L';
    private static final char BEAR = 'B';
    private static final char STONE = 'S';
    private static final char WOLF = 'W';
    private static final char OPEN = ' ';

    private static final Move NORTH = Move.UP;
    private static final Move EAST = Move.RIGHT;
    private static final Move SOUTH = Move.DOWN;
    private static final Move WEST = Move.LEFT;
    private static final Move STAND = Move.HOLD;

    private List<Move> movePreferences = Arrays.asList(STAND, SOUTH, WEST, EAST, NORTH);
    private List<Move> availableMoves;

    private Bear bearTracker;

    public CautiousWolf() { 
        super('W');
        bearTracker = new Bear();
    }
    public Attack fight(char c) { 
        System.out.println("Fighting " + c);
        switch (c) {
            case 'B':
                return Attack.SCISSORS;
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                return Attack.SCISSORS;
        } 
    }
    private static int ct = 0;
    public Move move() {
        if (++ct > 200) {
            ct = ct % 200;
            System.out.println(surroundings.toString());
            System.out.println(surroundings[0][0] + "|" + surroundings[0][1] + "|" + surroundings[0][2]);
            System.out.println(surroundings[1][0] + "|" + surroundings[1][1] + "|" + surroundings[1][2]);
            System.out.println(surroundings[2][0] + "|" + surroundings[2][1] + "|" + surroundings[2][2]);
        }
        Move bearMove = bearTracker.move();
        availableMoves = new ArrayList<Move>(5);
        availableMoves.addAll(movePreferences);
        Move preferredMove = availableMoves.get(0);
        if (creatureAt(N) != OPEN) {
            availableMoves.remove(NORTH);
            preferredMove = SOUTH;
        }
        if (creatureAt(S) != OPEN) {
            availableMoves.remove(SOUTH);
            preferredMove = NORTH;
        }
        if (creatureAt(E) != OPEN) {
            availableMoves.remove(EAST);
        }
        if (creatureAt(W) != OPEN) {
            availableMoves.remove(WEST);
            preferredMove = EAST;
        }
        if (creatureAt(W) == STONE) {
            preferredMove = SOUTH;
        }
        Move chosenMove;
        if (availableMoves.indexOf(preferredMove) != -1) {
            chosenMove = preferredMove;
        } else {
            chosenMove = availableMoves.get(0);
        }

        if (chosenMove == oppositeMove(bearMove) && availableMoves.size() > 2) {
            availableMoves.remove(chosenMove);
            chosenMove = availableMoves.get(1);
        }

        return chosenMove;
    }

    protected char creatureAt(int direction) {
        switch(direction) {
            case NW:
                return surroundings[0][0];
            case N:
                return surroundings[0][1];
            case NE:
                return surroundings[0][2];
            case W:
                return surroundings[1][0];
            case M:
                return surroundings[1][1];
            case E:
                return surroundings[1][2];
            case SW:
                return surroundings[2][0];
            case S:
                return surroundings[2][1];
            case SE:
                return surroundings[2][2];
            default:
                return surroundings[1][1];
        }
    }

    private Move oppositeMove(Move move) {
        switch(move) {
            case UP:
                return Move.DOWN;
            case DOWN:
                return Move.UP;
            case LEFT:
                return Move.RIGHT;
            case RIGHT:
                return Move.LEFT;
            default:
                return Move.HOLD;
        }
    }
}

Brian

Posted 2014-04-04T18:47:50.283

Reputation: 231

2

BreakStaticFieldsWolf

This wolf tries to outsmart the other fancy wolves by destructively modifying any static fields that it comes across. The breakEverything method is called whenever the wolf moves. It first gets the value of Wild.classes, casts it to a Class[] array, and then iterates over it. In each class, it iterates over all fields, clearing and adding null to any Collection it finds. Failing that, it will write an appropriate random value to a field of any native type, or null to an object field.

Apart from this tactic it's quite basic. It simply stands still, and chooses the 'best' option when fighting.

I haven't tested this much yet, it should cause several Wolves to throw NullPointerExceptions and suicide though.

package animals;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

import wild.Wild;

public class BreakStaticFieldsWolf extends Animal {

    private Random random = new Random();

    public BreakStaticFieldsWolf() {
        super('W');
    }

    void breakEverything() {
        try {
            Field wildClasses = Wild.class.getDeclaredField("classes");
            wildClasses.setAccessible(true);
            Class[] classes = (Class[]) wildClasses.get(null);
            for(Class clazz : classes) {
                Field[] fields = clazz.getDeclaredFields();
                for(Field field : fields) {
                    try {
                        field.setAccessible(true);
                        Class type = field.getType();
                        Object obj = field.get(null);
                        if(obj instanceof Collection) {
                            ((Collection) obj).clear();
                            ((Collection) obj).add(null); // teehee
                        }
                        if(type == long.class) field.setLong(null, random.nextLong());
                        else if(type == int.class) field.setInt(null, random.nextInt());
                        else if(type == short.class) field.setShort(null, (short) random.nextInt());
                        else if(type == char.class) field.setChar(null, (char) random.nextInt());
                        else if(type == byte.class) field.setByte(null, (byte) random.nextInt());
                        else if(type == boolean.class) field.setBoolean(null, random.nextBoolean());
                        else if(type == double.class) field.setDouble(null, random.nextDouble());
                        else if(type == float.class) field.setFloat(null, random.nextFloat());
                        else field.set(null, type.newInstance());
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    @Override
    public Attack fight(char c) {
        switch(c) {
        case 'L':
        case 'B':
            return Attack.SCISSORS;
        case 'S':
            return Attack.PAPER;
        case 'W':
        default:
            Attack[] attacks = {Attack.PAPER, Attack.ROCK, Attack.SCISSORS};
            return attacks[random.nextInt(3)];
        }
    }

    @Override
    public Move move() {
        breakEverything();
        return Move.HOLD;
    }

}

mackthehobbit

Posted 2014-04-04T18:47:50.283

Reputation: 304

The problem here is probably that reflection is slow, and since breakEverything() is constantly being called, it could end up causing the slowdown that's being seen. – Cel Skeggs – 2015-08-23T04:02:57.767

2This prompted me to learn a thing or two, especially about catching Errors (something I had never heard of). After catching and handling the errors, the program just never stops running. I attached a profiler to find where it was getting hung up. Twenty minutes the program has been running (as opposed to 10 seconds without your wolf) and 99% of it has been spent inside of your class. The other wolves may be committing suicide, but at least they do it quickly. – Rainbolt – 2014-04-23T03:08:07.287

2

ProtoWolf

ProtoWolf does what seems better to survive: Takes few risks, evaluates the environment for not to clash with another lions nor wolves and mostly tries to keep quiet. I have tried to make a better surroundings analysis algorithm here that my previous "serious" submission (StoneEatingWolf). I think I cannot improve that algorithm much more myself. I will try to improve his attacks after I analyze what kind of rivals he dies to.

package animals;

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


public class ProtoWolf extends Animal{

    private static int OVERLAP_WOLF = 100;
    private static int OVERLAP_LION = 50;
    private static int NEXT_LION = 0;
    private static int NEXT_LION_DANGER = 6;
    private static int NEXT_WOLF = 0;
    private static int NEXT_WOLF_DANGER = 10;   


    class MapPoints implements Comparable<MapPoints>{
        public MapPoints(Move move, int danger) {
            super();
            this.move = move;
            this.danger = danger;
        }

        private Move move;
        private int danger;

        public Move getMove() {
            return move;
        }
        public void setMove(Move move) {
            this.move = move;
        }
        public int getDanger() {
            return danger;
        }
        public void setDanger(int danger) {
            this.danger = danger;
        }
        @Override
        public int compareTo(MapPoints o) {
            return new Integer(danger).compareTo(o.getDanger()); 
        }
    }

    public ProtoWolf() {
        super('W');
    }

    @Override
    public Attack fight(char c) {
        switch (c){
            case 'L': return Attack.SCISSORS;
            case 'B': return Attack.SCISSORS;
            case 'W': return getRandomAttack();
            case 'S': return Attack.PAPER;
            default: return getRandomAttack();
        }
    }

    private Attack getRandomAttack(){
        List<Attack> att = new ArrayList<>();
        att.add(Attack.PAPER);
        att.add(Attack.PAPER);
        att.add(Attack.ROCK);
        att.add(Attack.SCISSORS);
        Collections.shuffle(att);
        return att.get(0);
    }

    private int evalUp(){
        int res = 0;

        //Eval move

        if (surroundings[0][1] == 'L') {
            res += OVERLAP_LION;
        } else if (surroundings[0][1] == 'W') {
            res += OVERLAP_WOLF;
        }


        //Eval surroundings after move
        if (surroundings[0][0] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[0][0] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[0][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[0][2] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[1][0] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[1][0] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[1][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[1][2] == 'W'){
            res += NEXT_WOLF;
        }

        return res;
    }


    private int evalDown(){
        int res = 0;

        //Eval move
        if (surroundings[2][1] == 'L') {
            res += OVERLAP_LION;
        } else if (surroundings[2][1] == 'W') {
            res += OVERLAP_WOLF;
        }

        //Eval surroundings after move
        if (surroundings[2][0] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[2][0] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[2][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][2] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[1][0] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[1][0] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[1][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[1][2] == 'W'){
            res += NEXT_WOLF;
        }

        return res;
    }

    private int evalLeft(){
        int res = 0;

        //Eval move
        if (surroundings[1][0] == 'L') {
            res += OVERLAP_LION;
        } else if (surroundings[1][0] == 'W') {
            res += OVERLAP_WOLF;
        }

        //Eval surroundings after move
        if (surroundings[0][0] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[0][0] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[0][1] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[0][1] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[2][1] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][1] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[2][0] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][0] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        return res;
    }

    private int evalRight(){
        int res = 0;

        //Eval move
        if (surroundings[1][2] == 'L') {
            res += OVERLAP_LION;
        } else if (surroundings[1][2] == 'W') {
            res += OVERLAP_WOLF;
        }

        //Eval surroundings after move
        if (surroundings[0][2] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[0][2] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[0][1] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[0][1] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[2][1] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][1] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[2][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][2] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        return res;
    }

    private int evalHold(){
        int res = -1;


        //Eval surroundings after move
        if (surroundings[0][0] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[0][0] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[0][1] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[0][1] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[0][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[0][2] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[1][0] == 'L'){
            res += NEXT_LION_DANGER;
        } else if (surroundings[1][0] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[1][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[1][2] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[2][0] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][0] == 'W'){
            res += NEXT_WOLF;
        }

        if (surroundings[2][1] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][1] == 'W'){
            res += NEXT_WOLF_DANGER;
        }

        if (surroundings[2][2] == 'L'){
            res += NEXT_LION;
        } else if (surroundings[2][2] == 'W'){
            res += NEXT_WOLF;
        }

        return res;
    }

    @Override
    public Move move(){

        MapPoints north = new MapPoints(Move.UP, evalUp());
        MapPoints south = new MapPoints(Move.DOWN, evalDown());
        MapPoints east = new MapPoints(Move.RIGHT, evalRight());
        MapPoints west = new MapPoints(Move.LEFT, evalLeft());
        MapPoints hold = new MapPoints(Move.HOLD, evalHold());

        List<MapPoints> mpList = new ArrayList<>();
        mpList.add(north);
        mpList.add(south);
        mpList.add(east);
        mpList.add(west);
        mpList.add(hold);

        Collections.sort(mpList);

        return mpList.get(0).getMove();
    }


    }

Averroes

Posted 2014-04-04T18:47:50.283

Reputation: 2 298

2

MilkyWolf

Statistically based

I found bug in suicide handling. If both suicide only 1 dies

package animals;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class MilkyWolf extends Animal {

    private final Random random = new Random();

    Map<Move, int[]> moves = new HashMap<>();

    {
        moves.put(Move.HOLD, new int[]{1, 1});
        moves.put(Move.LEFT, new int[]{1, 0});
        moves.put(Move.RIGHT, new int[]{1, 2});
        moves.put(Move.UP, new int[]{0, 1});
        moves.put(Move.DOWN, new int[]{2, 1});
    }

    public MilkyWolf() {
        super('W');
    }

    @Override
    public Attack fight(char opponent) {
        switch (opponent) {
            case 'B':
            case 'L':
                return Attack.SCISSORS;
            case 'S':
                return Attack.PAPER;
            default:
                switch (random.nextInt(3)) {
                    case 0: return Attack.PAPER;
                    case 1: return Attack.SCISSORS;
                    default: return Attack.ROCK;
                }
        }
    }

    final double lionThreat = 0.25;
    final double wolfThreat = 0.5;

    @Override
    public Move move() {
        float[][] threat;
        threat = new float[3][3];
        threat[1][1] -= 0.0001;
        threat[0][1] += 0.0001;
        threat[1][0] += 0.0001;

        for (int y = 0; y < 3; y++) {
            for (int x = 0; x < 3; x++) {
                if (x == 1 && y == 1) {
                    continue;
                }
                if (surroundings[y][x] == 'W') {
                    threat[y][x] += wolfThreat * 0.24;
                    if (y < 2) {
                        threat[y + 1][x] += wolfThreat * 0.19;
                    }
                    if (x < 2) {
                        threat[y][x + 1] += wolfThreat * 0.19;
                    }
                    if (y > 0) {
                        threat[y - 1][x] += wolfThreat * 0.19;
                    }
                    if (x > 0) {
                        threat[y][x - 1] += wolfThreat * 0.19;
                    }

                } else if (surroundings[y][x] == 'L') {
                    if (y < 2) {
                        threat[y + 1][x] += lionThreat * 0.5;
                    }
                    if (x < 2) {
                        threat[y][x + 1] += lionThreat * 0.5;
                    }
                }
            }
        }

        Move m = getThreatBasedMove(threat);
        return m;
    }

    private Move getThreatBasedMove(float threat[][]) {
        Move move = null;
        float minThreat = Float.POSITIVE_INFINITY;

        for (Map.Entry<Move, int[]> entry : moves.entrySet()) {
            Animal.Move move1 = entry.getKey();
            int[] coordinates = entry.getValue();

            if (threat[coordinates[0]][coordinates[1]] < minThreat) {
                minThreat = threat[coordinates[0]][coordinates[1]];
                move = move1;
            }

        }
        return move;
    }

}

Suicide fix

if (aTack == bTack) {
    if (Attack.SUICIDE == aTack) {
        cell.remove(a);
        cell.remove(b);
    } else {
        cell.remove((Math.random() > 0.5 ? a : b));
    }
}

mleko

Posted 2014-04-04T18:47:50.283

Reputation: 171

Someone pointed that out on April 8 in an earlier comment but I never bothered to fix it. I saw that you went ahead and fixed it for me. Thanks for that! If you leave it in your post, I'll be sure to remember to fix it before I run again. I've had two submissions come in since last time I posted results, so it is probably time for another batch. I'll try to do that this week. – Rusher 1 min ago edit – Rainbolt – 2014-05-13T15:08:26.550

@Rusher If you could put all code on github it would be much easier to fix bugs. – mleko – 2014-05-13T15:32:38.367

I actually noticed the bug but figured, "well, that's how it behaves I guess." It impeded my original LoneWolf design, which allowed 100 to be instantiated but they would all suicide until 1 was left. I couldn't reliably decrement my wolf count so I'd routinely be left with 20-30 wolves. – mike32 – 2014-05-14T06:55:28.623

2

SittingWolf

package animals;

public class SittingWolf extends Animal {
    public SittingWolf() {
        super('W');
    }
    public Attack fight(char enemy) { 
        switch (enemy) {
            case 'B': // Bear: paper
            case 'L': // Lion: paper or scissors
                return Attack.SCISSORS;
            case 'S': // Stone: rock
                return Attack.PAPER;
            default:
                return Attack.values()[(int)(Math.random() * 3)];
        } 
    }
    public Move move() {
        return Move.HOLD;
    }
}

Just sits there (hold).

Solomon Ucko

Posted 2014-04-04T18:47:50.283

Reputation: 254

1

WanderingWolf

package animals;

public class WanderingWolf extends Animal {
    public WanderingWolf() {
        super('W');
    }
    public Attack fight(char enemy) { 
        switch (enemy) {
            case 'B': // Bear: paper
            case 'L': // Lion: paper or scissors
                return Attack.SCISSORS;
            case 'S': // Stone: rock
                return Attack.PAPER;
            default:
                return Attack.values()[(int)(Math.random() * 3)];
        } 
    }
    public Move move() {
        return Move.values()[(int)(Math.random() * 5)];
    }
}

To move, it wanders. To attack, it uses the move that always wins (or ties), except for other wolves, which it attacks randomly for.

Solomon Ucko

Posted 2014-04-04T18:47:50.283

Reputation: 254

1

RandomWolf

package animals;

public class RandomWolf extends Animal {
    public RandomWolf() {
        super('W');
    }
    public Attack fight(char enemy) { 
        return Attack.values()[(int)(Math.random() * 4)];
    }
    public Move move() {
        return Move.values()[(int)(Math.random() * 5)];
    }
}

Does everything randomly. Commits suicide a quarter of the time it attacks :-)

Solomon Ucko

Posted 2014-04-04T18:47:50.283

Reputation: 254

1

PsychoWolf

import java.lang.reflect.Field;
import java.util.Random;

public class PsychoWolf extends Animal {
    private int tracker = 0;

    public PsychoWolf() {
        super('W');
        try {
            Field field = Math.class.getDeclaredField("randomNumberGenerator");
            field.setAccessible(true);
            field.set(null, new Random() {
                public double nextDouble() {
                    return 0;
                }
            });
        } catch (RuntimeException | NoSuchFieldException
                | IllegalAccessException e) {

        }
    }

    public Attack fight(char c) {
        switch (c) {
        case 'B':
        case 'L':
            return Attack.SCISSORS;
        case 'S':
            return Attack.PAPER;
        default:
            return Attack.ROCK;
        }
    }

    public Move move() {
        if (surroundings[0][1] == 'W') {
            tracker = 0;
            return Move.DOWN;
        } else if (surroundings[1][0] == 'W') {
            tracker = 0;
            return Move.RIGHT;
        } else if (surroundings[2][1] == 'W') {
            tracker = 0;
            return Move.UP;
        } else if (surroundings[1][2] == 'W') {
            tracker = 0;
            return Move.LEFT;
        } else if (tracker > 10) {
            tracker = 0;
            return Move.values()[new Random().nextInt(5)];
        } else if (surroundings[0][1] == 'S' || surroundings[0][1] == 'B') {
            tracker++;
            return Move.UP;
        } else if (surroundings[1][0] == 'S' || surroundings[1][0] == 'B') {
            tracker++;
            return Move.LEFT;
        } else if (surroundings[2][1] == 'S' || surroundings[2][1] == 'B') {
            tracker++;
            return Move.DOWN;
        } else if (surroundings[1][2] == 'S' || surroundings[1][2] == 'B') {
            tracker++;
            return Move.RIGHT;
        } else {
            tracker = 0;
            return Move.values()[new Random().nextInt(5)];
        }
    }
}

Attempts to counter GamblerWolf by setting random value back into range [0, 1). This also makes it always defeat Lions. Vulnerable to CamoWolf. Tracks Bears and also goes after Stones (which makes it especially vulnerable to CamoWolf.

HyperNeutrino

Posted 2014-04-04T18:47:50.283

Reputation: 17 180