247
\$\begingroup\$

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.

\$\endgroup\$
80
  • 5
    \$\begingroup\$ @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 :) \$\endgroup\$ Apr 4, 2014 at 19:27
  • 4
    \$\begingroup\$ "* 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. \$\endgroup\$ Apr 4, 2014 at 21:53
  • 6
    \$\begingroup\$ how about trees showing paper? \$\endgroup\$ Apr 5, 2014 at 5:32
  • 4
    \$\begingroup\$ @Rusher, I think some people are doing this already, but is inter-Wolf communication allowed via static members (within your own breed)? \$\endgroup\$ Apr 5, 2014 at 9:35
  • 11
    \$\begingroup\$ @m.buettner Allowed. Go forth and build your hive-minded wolf. \$\endgroup\$
    – Rainbolt
    Apr 5, 2014 at 15:02

56 Answers 56

1
2
7
\$\begingroup\$

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 :(

\$\endgroup\$
2
  • 1
    \$\begingroup\$ When 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. \$\endgroup\$
    – justhalf
    Apr 11, 2014 at 4:16
  • \$\begingroup\$ Hmm good thought. justhalf. I imagine I can do the reverse when he is bottom left as well. Thank you! :) \$\endgroup\$
    – Sheph
    Apr 11, 2014 at 13:37
7
\$\begingroup\$

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

}
\$\endgroup\$
10
  • \$\begingroup\$ Doesn't compile (not joking). \$\endgroup\$
    – Rainbolt
    Apr 11, 2014 at 18:08
  • \$\begingroup\$ Oops, had the wrong library for Runtime as I tried to distill it. \$\endgroup\$
    – Sconibulus
    Apr 11, 2014 at 18:12
  • \$\begingroup\$ error: unreported exception IOException; must be caught or declared to be thrown. How will you know what my process is called anyway? \$\endgroup\$
    – Rainbolt
    Apr 11, 2014 at 18:13
  • 2
    \$\begingroup\$ Well, 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). \$\endgroup\$
    – Rainbolt
    Apr 11, 2014 at 18:26
  • 2
    \$\begingroup\$ Try System.exit(0). Credit to @Geobits. \$\endgroup\$
    – Rainbolt
    Apr 11, 2014 at 18:32
7
\$\begingroup\$

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;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ You get honorable mention if I ever run a single test run where the 1 Highlander Wolf actually lives. \$\endgroup\$
    – Rainbolt
    Apr 23, 2014 at 3:13
  • \$\begingroup\$ @Rusher While I was happy with 0.6 average result, I hope some movement logic will improve this! \$\endgroup\$
    – mike32
    Apr 23, 2014 at 9:28
6
\$\begingroup\$

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.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I 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. \$\endgroup\$
    – Rainbolt
    Apr 10, 2014 at 3:55
  • \$\begingroup\$ Thanks, I was rushing, using notepad, and haven't wrote Java for about 10 years so I appreciate the fixes. \$\endgroup\$
    – user20151
    Apr 10, 2014 at 8:32
  • \$\begingroup\$ 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() \$\endgroup\$
    – BurntPizza
    Apr 11, 2014 at 21:21
6
\$\begingroup\$

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

}
\$\endgroup\$
9
  • \$\begingroup\$ 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 \$\endgroup\$
    – Justin
    Apr 10, 2014 at 7:04
  • \$\begingroup\$ 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 \$\endgroup\$
    – Justin
    Apr 10, 2014 at 7:10
  • 1
    \$\begingroup\$ :) 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). \$\endgroup\$
    – plannapus
    Apr 10, 2014 at 7:12
  • 1
    \$\begingroup\$ @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. \$\endgroup\$
    – Justin
    Apr 10, 2014 at 7:15
  • 1
    \$\begingroup\$ This 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. \$\endgroup\$
    – Rainbolt
    Apr 10, 2014 at 13:25
6
\$\begingroup\$

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.

\$\endgroup\$
9
  • \$\begingroup\$ @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. \$\endgroup\$
    – Toothbrush
    Apr 7, 2014 at 18:06
  • \$\begingroup\$ I'm still getting errors when attacking from the line if (attack.length > 1 && Math.random() > .8) { -- the error is TypeError: Cannot read property 'length' of undefined. Something seems wrong with the way you are looking up the attack response. \$\endgroup\$ Apr 10, 2014 at 1:12
  • \$\begingroup\$ @ProgrammerDan OK. I think that I've finally got the code right. I've tried it (without Java), and it seems to work fine. \$\endgroup\$
    – Toothbrush
    Apr 10, 2014 at 18:37
  • \$\begingroup\$ Awesome! I'll give it a go a little later. \$\endgroup\$ Apr 10, 2014 at 19:32
  • \$\begingroup\$ 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. \$\endgroup\$ Apr 11, 2014 at 1:34
6
\$\begingroup\$

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

}
\$\endgroup\$
6
  • \$\begingroup\$ 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. \$\endgroup\$
    – plannapus
    Apr 7, 2014 at 11:38
  • \$\begingroup\$ Thanks for the info, fix'd it. I was kinda absent minded \$\endgroup\$ Apr 7, 2014 at 11:39
  • \$\begingroup\$ Funny idea. Welcome to Code Golf! \$\endgroup\$
    – plannapus
    Apr 7, 2014 at 11:41
  • \$\begingroup\$ Pleased to meet you all :) \$\endgroup\$ Apr 7, 2014 at 11:50
  • \$\begingroup\$ Just some code cleanliness advice... maybe switch on lastMove and return lastMove = Move.CurrMove? \$\endgroup\$ Apr 11, 2014 at 19:04
5
\$\begingroup\$

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++;
        }
    }
}
\$\endgroup\$
5
\$\begingroup\$

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;
    }
}
\$\endgroup\$
5
\$\begingroup\$

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;
        }
    }
}
\$\endgroup\$
4
\$\begingroup\$

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;
    }
}
\$\endgroup\$
4
\$\begingroup\$

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;
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ I strongly recommend that you make your static variables private. You don't want other wolves modifying your variables, do you? \$\endgroup\$
    – Rainbolt
    Apr 8, 2014 at 22:06
  • \$\begingroup\$ Yes, I misread the part where you said other wolves cannot modify files created by a Wolf class. \$\endgroup\$
    – Averroes
    Apr 8, 2014 at 22:31
4
\$\begingroup\$

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();
        }
    }
}
\$\endgroup\$
10
  • \$\begingroup\$ 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? \$\endgroup\$
    – Rainbolt
    Apr 9, 2014 at 3:37
  • \$\begingroup\$ 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 ;) \$\endgroup\$ Apr 9, 2014 at 5:48
  • 5
    \$\begingroup\$ public Mirror() { super('M'); } <- I think this is specifically disallowed in the rules. \$\endgroup\$
    – plannapus
    Apr 9, 2014 at 8:16
  • \$\begingroup\$ @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. \$\endgroup\$
    – Zaq
    Apr 9, 2014 at 20:19
  • \$\begingroup\$ @Zaq CamoWolf is a not considered a valid wolf because it uses super('S') \$\endgroup\$ Apr 9, 2014 at 20:40
4
\$\begingroup\$

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;
    }
}
\$\endgroup\$
4
\$\begingroup\$

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);
        }
    }
}
\$\endgroup\$
3
\$\begingroup\$

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;
    }
}
\$\endgroup\$
3
  • 7
    \$\begingroup\$ I can't include this if your Wolf isn't represented by a 'W'. The spec says the Wolf is "Represented by 'W'." \$\endgroup\$
    – Rainbolt
    Apr 6, 2014 at 16:49
  • 1
    \$\begingroup\$ @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”? \$\endgroup\$ Apr 7, 2014 at 16:36
  • 9
    \$\begingroup\$ @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. \$\endgroup\$
    – Rainbolt
    Apr 7, 2014 at 18:13
3
\$\begingroup\$

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

}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ This 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. \$\endgroup\$
    – Rainbolt
    Apr 23, 2014 at 3:08
  • \$\begingroup\$ 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. \$\endgroup\$
    – Cel Skeggs
    Aug 23, 2015 at 4:02
3
\$\begingroup\$

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;
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – Moogie
    Apr 14, 2014 at 5:37
  • \$\begingroup\$ @Moogie thanks for the fix :) \$\endgroup\$ Apr 14, 2014 at 7:01
3
\$\begingroup\$

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;
            }
        }
    }
}
\$\endgroup\$
2
\$\begingroup\$

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


    }
\$\endgroup\$
2
\$\begingroup\$

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;
        }
    }
}
\$\endgroup\$
2
\$\begingroup\$

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));
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ 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 \$\endgroup\$
    – Rainbolt
    May 13, 2014 at 15:08
  • \$\begingroup\$ @Rusher If you could put all code on github it would be much easier to fix bugs. \$\endgroup\$
    – mleko
    May 13, 2014 at 15:32
  • \$\begingroup\$ 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. \$\endgroup\$
    – mike32
    May 14, 2014 at 6:55
2
\$\begingroup\$

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).

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

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.

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

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 :-)

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

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.

\$\endgroup\$
1
2

Your Answer

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

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