12
\$\begingroup\$

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.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ What happens when you hit the balloon? How does it move? What if multiple people hit it? \$\endgroup\$ Feb 27, 2015 at 5:27
  • \$\begingroup\$ The animation with the jsfiddle is really nice! \$\endgroup\$ Feb 27, 2015 at 7:14
  • \$\begingroup\$ By the way, you should make the methods in the Player class final, otherwise submissions can override them. \$\endgroup\$ Feb 27, 2015 at 7:19
  • 2
    \$\begingroup\$ You inverted speed and strength in the Player constructor. \$\endgroup\$
    – Thrax
    Feb 27, 2015 at 8:43
  • \$\begingroup\$ @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) \$\endgroup\$
    – soktinpk
    Feb 27, 2015 at 22:19

8 Answers 8

7
\$\begingroup\$

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

\$\endgroup\$
3
  • 3
    \$\begingroup\$ +1000 That is amazing. Thank you \$\endgroup\$
    – soktinpk
    Feb 28, 2015 at 16:53
  • \$\begingroup\$ Don't you mean Ctrl+F5, not Shift+F5? \$\endgroup\$
    – Timtech
    Feb 28, 2015 at 20:23
  • \$\begingroup\$ Looks like both work in Chrome. \$\endgroup\$
    – BMac
    Feb 28, 2015 at 21:30
7
\$\begingroup\$

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

    }

}
\$\endgroup\$
5
  • \$\begingroup\$ it looks like your bot performs illegal moves and thus does nothing when it does. \$\endgroup\$
    – Moogie
    Mar 6, 2015 at 11:26
  • \$\begingroup\$ @soktinpk I fixed my submission, it should do better now. Thanks Moogie too! \$\endgroup\$
    – Thrax
    Mar 11, 2015 at 13:32
  • \$\begingroup\$ 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. \$\endgroup\$
    – Moogie
    Mar 13, 2015 at 7:48
  • \$\begingroup\$ @Moogie Right, thanks a lot! \$\endgroup\$
    – Thrax
    Mar 13, 2015 at 7:52
  • \$\begingroup\$ Glad to help. Your bot is pretty good at getting positive scores. well done! \$\endgroup\$
    – Moogie
    Mar 13, 2015 at 10:01
5
\$\begingroup\$

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

}
\$\endgroup\$
1
  • \$\begingroup\$ This is the one to beat. \$\endgroup\$ Mar 3, 2015 at 19:47
5
\$\begingroup\$

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

\$\endgroup\$
3
\$\begingroup\$

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

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.

\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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

Your Answer

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

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