Water Balloon Wars

12

3

This king-of-the-hill game is a strategy game in which you must throw around a water balloon and avoid getting splashed by water. The goal is to obtain the most points. You will be given a map of the field and the location of the water balloon. You can either return that you want to hit the water balloon (if you are close enough) in a certain direction or that you want to move in a certain direction.

Specifically: The water balloon will start at (0, 0) 30 units high and drop. If the water balloon hits the ground, a player will randomly be chosen to lose 4 points, with more weight given to those who are closer to the balloon. In addition, the player who last hit the balloon will earn 3 points. Therefore, if you hit the balloon straight down, you will most likely lose 1 point.

You will write a class that extends Player. You are required to implement the constructor. The constructor will look like:

public Player1() {
    super(/* Some numbers */ 3, 3, 4)
}

These numbers are doubles. The first number represents the player's speed, the second represents strength, and the third represents luck. The numbers must add up to 10 or less and no number may be less than or equal to zero.

Second, you must implement the move method. This is an example move method:

@Override
protected Action move(Map<Player, Point2D> map, Balloon b) {
    // Get my own location
    Point2D myself = map.get(this);
    // If I'm close enough to the balloon
    // then hit the balloon
    if (myself.distanceSq(b.getLocation()) <= 16) {
        double d = (r.nextDouble() - 0.5) * 3;
        // Random y direction, z direction is what's left 
        return new Hit(0, d, Math.sqrt(9 - d*d));
    } else {
        double diffX = b.getLocation().getX() - myself.getX(),
                diffY = b.getLocation().getY() - myself.getY();
        // Move towards the balloon
        return new Movement(Math.signum(diffX)*3/Math.sqrt(2), Math.signum(diffY)*3/Math.sqrt(2));
    }
}

There are a number of important things here. First, notice that the field is passed as a Map<Player, Point2D>. The field is infinite - there is no limit to how far you can go. It is not a 2-dimensional array or anything like that. In addition, this means that you will have non-integer coordinates as your location. This is perfectly okay.

Another consequence is that players and the balloon may overlap. In fact, two players may be in the exact same location!

The balloon has a certain velocity and direction. In general, it will fall at a rate of 3 units/step. It also moves in an x direction and y direction. When you return a Hit, you pass the x, y, and z directions that you are pushing the balloon. You cannot hit a balloon whose height is greater than 10 or whose distance from you (only on two dimensions) is greater than 4. In addition, if it is true that x^2 + y^2 + z^2 > s^2 where s is your strength, and x, y, and z are the directions that you hit, your action is discarded. The force of your hit is amplified by a random number between 0 and luck (which means it could go down if your luck is low).

Similarly, you can return a Movement with the x and y coordinates that you are moving (note that you cannot jump in the air). If x^2 + y^2 > s^2 where s is your speed, your action is discarded.

If the water balloon hits the ground then a random player is chosen, with more weight given to those who are closest -- but less weight to those who have more luck. The player that is chosen loses 4 points.

Controller: https://github.com/prakol16/water-balloon-wars/tree/master

The game lasts 1000 steps. At the end, there will be a file called log.out. Copy and paste the data into this fiddle to view the game: https://jsfiddle.net/prankol57/s2x776dt/embedded/result/

Or even better, view it in 3D: http://www.brianmacintosh.com/waterballoonwars (thanks to BMac)

The player with the highest sum of scores after 100 (may be more, but not less) games wins.

If you wish to submit a solution, you may want to read the really specific details at https://github.com/prakol16/water-balloon-wars/tree/master.

Edit 3/8:

These are the final scores for now (1000 iterations, without including players 1 and 2). If you edit your post, you can comment, and I will redo the scores:

{
    class players.BackAndForth=-75.343,
    class players.Hydrophobe=-0.800,
    class players.KeepAway=-53.064,
    class players.Weakling=39.432,
    class players.Repeller=21.238,
    class players.LuckyLoser=-30.055,
    class players.AngryPenguin=-49.310
}

The winner was Weakling with an average of 39 points. 2nd place was Repeller with 21 points.

soktinpk

Posted 2015-02-26T23:48:32.657

Reputation: 4 080

1What happens when you hit the balloon? How does it move? What if multiple people hit it? – Keith Randall – 2015-02-27T05:27:17.500

The animation with the jsfiddle is really nice! – CommonGuy – 2015-02-27T07:14:14.823

By the way, you should make the methods in the Player class final, otherwise submissions can override them. – CommonGuy – 2015-02-27T07:19:28.030

2You inverted speed and strength in the Player constructor. – Thrax – 2015-02-27T08:43:57.603

@KeithRandall The dirX, dirY, and dirZ (amplified by your luck) are simply added to the velocities of the balloon. If multiple people hit it (somewhat unlikely) then the player who might get three points is decided on luck (see specific details) – soktinpk – 2015-02-27T22:19:16.567

@Thrax Thanks I mixed it up in the question. Fixed now. – soktinpk – 2015-02-27T22:40:10.160

Answers

7

BackAndForth

This bot tries to get close and hit the balloon until its height is too low and it tries to get away.

package players;

import java.awt.geom.Point2D;
import java.util.Map;

import balloon.Action;
import balloon.Balloon;
import balloon.Player;
import balloon.Action.Hit;
import balloon.Action.Movement;

public class BackAndForth extends Player {

    static int round = 0;
    static int speed = 3;
    static int strength = 1;
    static boolean hit = false;
    static double previousHeight = 30.0;

    public BackAndForth() {
        super(speed, strength, 10 - speed - strength);
    }

    @Override
    protected Action move(Map<Player, Point2D> map, Balloon b) {

        round++;

        Point2D me = map.get(this);
        Point2D balloon = b.getLocation();

        double distanceX = balloon.getX() - me.getX();
        double distanceY = balloon.getY() - me.getY();
        double distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));

        double maxX = speed * distanceX / distance;
        double maxY = speed * distanceY / distance;

        if (previousHeight < b.getHeight())
            hit = false;

        if (hit || b.getHeight() < 3) {
            previousHeight = b.getHeight();
            return new Movement(-maxX, -maxY);
        } else {
            if (distance < 4 && b.getHeight() < 10) {
                hit = true;
                return new Hit(0, 0, strength);
            } else {
                if (Math.pow(distance, 2) <= Math.pow(speed, 2)) {
                    return new Movement(distanceX, distanceY);
                } else {
                    return new Movement(maxX, maxY);
                }
            }
        }

    }

}

Thrax

Posted 2015-02-26T23:48:32.657

Reputation: 1 893

it looks like your bot performs illegal moves and thus does nothing when it does. – Moogie – 2015-03-06T11:26:56.683

@soktinpk I fixed my submission, it should do better now. Thanks Moogie too! – Thrax – 2015-03-11T13:32:44.477

I am still finding that your bot is asking for movement beyond what is possible. I have put in an edit of your post for review. Basically you were using the balloon's position as movement. – Moogie – 2015-03-13T07:48:56.633

@Moogie Right, thanks a lot! – Thrax – 2015-03-13T07:52:18.570

Glad to help. Your bot is pretty good at getting positive scores. well done! – Moogie – 2015-03-13T10:01:31.463

7

Simulator

I hope this is okay, since it isn't actually an entry. I really liked the idea of the visual simulator and I wanted to create my own that would make it a little easier to see everything at once (full 3D).

2/28 9:06AM PST: update with follow controls, colors

3/4 8:47AM PST: update with a slider for simulation speed, and made starting a new game actually work without refreshing the page (use Ctrl-F5 to reload cached script)

Online ThreeJS Visualizer

enter image description here

BMac

Posted 2015-02-26T23:48:32.657

Reputation: 2 118

3+1000 That is amazing. Thank you – soktinpk – 2015-02-28T16:53:23.863

Don't you mean Ctrl+F5, not Shift+F5? – Timtech – 2015-02-28T20:23:53.617

Looks like both work in Chrome. – BMac – 2015-02-28T21:30:06.743

5

AngryPenguin

This penguin is angry because he can't fly up to the balloon, so he tries to hit the balloon into the face of people standing around him.

package players;

import java.awt.geom.Point2D;
import java.util.Map;
import java.util.Map.Entry;

import balloon.Action;
import balloon.Action.Hit;
import balloon.Action.Movement;
import balloon.Balloon;
import balloon.Player;

public class AngryPenguin extends Player {
    private static final double HIT_Z = 3;
    public AngryPenguin() {
        super(4, 4, 2);
    }

    @Override
    protected Action move(Map<Player, Point2D> map, Balloon balloon) {
        Point2D myself = map.get(this);

        double distanceX = balloon.getLocation().getX() - myself.getX();
        double distanceY = balloon.getLocation().getY() - myself.getY();
        double distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));

        if (balloon.getHeight() < 2) {
            double[] xy = shrink(distanceX, distanceY, Math.pow(getSpeed(),2));
            return new Movement(-xy[0], -xy[1]);
        } else if (distance <= 4 && balloon.getHeight() <= 10) {
            double lowestDistance = Double.MAX_VALUE;
            Point2D nearestPlayerLoc = null;
            for (Entry<Player, Point2D> e : map.entrySet()) {
                if (e.getKey() != this) {
                    double d = e.getValue().distanceSq(myself);
                    if (d < lowestDistance) {
                        lowestDistance = d;
                        nearestPlayerLoc = e.getValue();
                    }
                }
            }
            double dX = nearestPlayerLoc.getX() - myself.getX();
            double dY = nearestPlayerLoc.getY() - myself.getY();
            double d = Math.pow(getStrength() - HIT_Z, 2);
            double[] xy = shrink(dX, dY, d);
            return new Hit(xy[0], xy[1], -HIT_Z);
        } else {
            double[] xy = shrink(distanceX, distanceY, Math.pow(Math.min(getSpeed(), distance), 2));
            return new Movement(xy[0], xy[1]);          
        }
    }

    private double[] shrink(double x, double y, double totalPow) {
        double[] xy = new double[2];
        double ratio = y == 0 ? 0 : 
                       x == 0 ? 1 : Math.abs(x) / Math.abs(y);
        if (ratio > 1)
            ratio = 1/ratio;
        xy[1] = totalPow * ratio;
        xy[0] = totalPow - xy[1];
        xy[0] = x < 0 ? -Math.sqrt(xy[0]) : Math.sqrt(xy[0]);
        xy[1] = y < 0 ? -Math.sqrt(xy[1]) : Math.sqrt(xy[1]);
        return xy;
    }

}

CommonGuy

Posted 2015-02-26T23:48:32.657

Reputation: 4 684

This is the one to beat. – Kevin Workman – 2015-03-03T19:47:04.633

5

Weakling

This bot can only touch the balloon as it is so weak, instead it just relies on its high luck. It therefore performs similarly to LuckyLoser (from which this bot is inspired).

He seems to out perform all the current bots including Repeller.

package players;
import java.awt.geom.Point2D;
import java.util.Map;
import balloon.Action;
import balloon.Balloon;
import balloon.Player;
import balloon.Action.Hit;
import balloon.Action.Movement;

public class Weakling extends Player {

    static final private double STRENGTH = Double.MIN_VALUE;
    static final private double SPEED = 1.5;
    static final private double LUCK = 8.5;
    public Weakling() {
        super(SPEED,STRENGTH,LUCK);
    }

    protected Action move(Map<Player, Point2D> map, Balloon b) {
        Point2D start = map.get(this);
        Point2D balloon = b.getLocation();
        double distance = start.distance(balloon)+Double.MIN_VALUE;
        if(distance<=4 && b.getHeight()<=10){

            // just touch it :P
            return new Hit(0,0,0);
        }
        double x = start.getX()-balloon.getX();
        double y = start.getY()-balloon.getY();
        x /= distance;
        y /= distance;

        // move to directly underneath balloon
        x*=Math.min(SPEED, distance);
        y*=Math.min(SPEED, distance);
        return new Movement(-x, -y);
    }
}

EDIT: reduced speed in favour of luck

Moogie

Posted 2015-02-26T23:48:32.657

Reputation: 1 505

3

Lucky Loser

This bot relies on its high luck score. If it is not near the balloon, it runs towards the balloon. Once near the balloon, if there are at least 2 other players in range of the balloon, he'll spike it to the ground. Otherwise, he'll knock it straight up.

package players;
import java.awt.geom.Point2D;
import java.util.Map;

import balloon.Action;
import balloon.Balloon;
import balloon.Player;
import balloon.Action.Hit;
import balloon.Action.Movement;

public class LuckyLoser extends Player {
    public LuckyLoser() {
        super(1,1,8);
    }

    protected Action move(Map<Player, Point2D> map, Balloon b) {
        Point2D start = map.get(this);
        Point2D bLocation = b.getLocation();
        double distance = start.distance(bLocation);
        if(distance<=4){
            boolean foundMe = false;
            int numPlayersInRange=0;
            for(Point2D point:map.values()){
                if( !foundMe && point.equals(start))
                {
                    foundMe=true;
                    continue;
                }
                if(point.distance(bLocation)<=4)
                    numPlayersInRange++;                
            }
            if(numPlayersInRange>1)
                return new Hit(0,0,-1);
            else
                return new Hit(0,0,1);
        }
        double x = start.getX()-bLocation.getX();
        double y = start.getY()-bLocation.getY();
        x /= distance;
        y /= distance;
        return new Movement(-x, -y);
    }
}

EDIT: Fixed movement bug which actually made me run away not towards the balloon >_< Now I just run straight towards the balloon if I can't hit it.

Fongoid

Posted 2015-02-26T23:48:32.657

Reputation: 971

3

Hydrophobe

This is one of the simplest possible bot possible but as it is competitive I will post it.

Strategy: well... this bots hates water so it just goes away.

As the bot will be splashed very rarely it will score a little under 0 point in average. The sum of all bots' scores are -1 * [balloon hitting ground] so Hydrophobe will probably score above average.

package players;
import java.awt.geom.Point2D;
import java.util.Map;
import balloon.*;

public class Hydrophobe extends Player {
    public Hydrophobe() {super(8, 1, 1);}
    @Override
    protected Action move(Map<Player, Point2D> map, Balloon balloon) {
        return new Action.Movement(5.65,5.65);
    }
}

randomra

Posted 2015-02-26T23:48:32.657

Reputation: 19 909

3

KeepAway

This player chases the balloon as long as its height is > 2. As soon as it can hit the balloon, it hits the balloon away from the closest player. When the balloon's height is < 2, this player runs away.

package players;

import java.awt.geom.Point2D;
import java.util.Map;

import balloon.Action;
import balloon.Balloon;
import balloon.Player;
import balloon.Action.Hit;
import balloon.Action.Movement;

public class KeepAway extends Player{

    public KeepAway() {
        super(5, 3, 2);
    }

    @Override
    protected Action move(Map<Player, Point2D> map, Balloon b) {

        Point2D myself = map.get(this);

        //if balloon is high up, run towards it
        if(b.getHeight() > 2){
            Point2D closest = getClosestPlayer(map);

            boolean canHit = b.getHeight() <= 10 && myself.distance(b.getLocation()) <= 4;

            //hit it when you can
            if(canHit){

                Point2D normHit = normalize(new Point2D.Double(myself.getX() - closest.getX(), myself.getY() - closest.getY()));
                Point2D forceHit = new Point2D.Double(normHit.getX() * getStrength(), normHit.getY() * getStrength());

                return new Hit(forceHit.getX(), forceHit.getY(), 0);
            }
            //if you can't hit it, keep running towards it
            else {

                Point2D normRun = normalize(new Point2D.Double(myself.getX() - b.getLocation().getX(), myself.getY() - b.getLocation().getY()));
                Point2D forceRun = new Point2D.Double(-normRun.getX() * getSpeed(), -normRun.getY() * getSpeed());
                return new Movement(forceRun.getX(), forceRun.getY());
            }
        }
        //if the balloon is low, run away
        else{
            Point2D normRun = normalize(new Point2D.Double(myself.getX() - b.getLocation().getX(), myself.getY() - b.getLocation().getY()));
            Point2D forceRun = new Point2D.Double(normRun.getX() * getSpeed(), normRun.getY() * getSpeed());
            return new Movement(forceRun.getX(), forceRun.getY());
        }

    }

    private Point2D getClosestPlayer(Map<Player, Point2D> map){

        double minDistance = Double.MAX_VALUE;
        Point2D closestPoint = null;
        Point2D myPoint = map.get(this);

        for(Player p : map.keySet()){
            if(this != p){

                if(myPoint.distance(map.get(p)) < minDistance){
                    minDistance = myPoint.distance(map.get(p));
                    closestPoint = map.get(p);
                }
            }
        }

        return closestPoint;
    }

    private Point2D normalize(Point2D p){
        double d = p.distance(0, 0);

        if(d == 0){
            return new Point2D.Double(0, 0);
        }

        return new Point2D.Double(p.getX()/d, p.getY()/d);
    }

}

Edit: I was playing with Player1 and Player2 included. This player wins in that case, but loses when I take them out. Booooo.

Kevin Workman

Posted 2015-02-26T23:48:32.657

Reputation: 308

3

Repeller

This bot relies has only one real move and that is to keep on pushing the balloon away from itself. i.e. repells the balloon.

It seems to perform well against the current crop of bots (LuckyLoser, AngryPenguin, Hydrophobe, BackAndForth ) nearly always winning. However Hydrophobe, by inaction, is always ready to win if the other bots all manage to get negative score :P

package players;
import java.awt.geom.Point2D;
import java.util.Map;

import balloon.Action;
import balloon.Balloon;
import balloon.Player;
import balloon.Action.Hit;
import balloon.Action.Movement;

public class Repeller extends Player {

    static final private double STRENGTH = 3.5;
    static final private double SPEED = 2.5;
    static final private double LUCK = 4;
    public Repeller() {
        super(SPEED,STRENGTH,LUCK);
    }

    protected Action move(Map<Player, Point2D> map, Balloon b) {
        Point2D start = map.get(this);
        Point2D balloon = b.getLocation();
        double distance = start.distance(balloon)+Double.MIN_VALUE;
        if(distance<=4 && b.getHeight()<=10){
            double x = start.getX()-balloon.getX();
            double y = start.getY()-balloon.getY();
            x /= distance;
            y /= distance;
            x*=STRENGTH;
            y*=STRENGTH;

            // push the balloon away with all our strength
            return new Hit(-x,-y,0);
        }
        double x = start.getX()-balloon.getX();
        double y = start.getY()-balloon.getY();
        x /= distance;
        y /= distance;

        // if we are directly underneath then move away from balloon
        distance=distance<1?-1:distance;

        // if we are just off of directly underneath then stay put
        distance=distance<2?0:distance;

        // move to the desired location
        x*=Math.min(SPEED, distance);
        y*=Math.min(SPEED, distance);
        return new Movement(-x, -y);
    }
}

Moogie

Posted 2015-02-26T23:48:32.657

Reputation: 1 505