Wojny z balonami wodnymi

12

Ta gra na wzgórzu to gra strategiczna, w której musisz rzucić balonem z wodą i unikać rozpryskiwania się przez wodę. Celem jest zdobycie jak największej liczby punktów. Otrzymasz mapę pola i lokalizację balonu z wodą. Możesz albo wrócić, że chcesz uderzyć balonem z wodą (jeśli jesteś wystarczająco blisko) w określonym kierunku lub że chcesz ruszyć w określonym kierunku.

W szczególności: Balon wodny rozpocznie się przy wysokości (0, 0)30 jednostek i spadnie. Jeśli balon wodny uderzy o ziemię, gracz zostanie losowo wybrany, aby stracić 4 punkty, przy czym większa waga zostanie przyznana tym, którzy są bliżej balonu. Ponadto gracz, który ostatni uderzył w balon, zdobędzie 3 punkty. Dlatego jeśli uderzysz balon prosto w dół, najprawdopodobniej stracisz 1 punkt.

Napiszecie klasę, która się rozszerza Player. Musisz wdrożyć konstruktor. Konstruktor będzie wyglądał następująco:

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

Te liczby to doubles. Pierwsza liczba oznacza prędkość gracza, druga oznacza siłę, a trzecia oznacza szczęście. Liczby muszą się sumować do 10 lub mniej i żadna liczba nie może być mniejsza lub równa zero.

Po drugie, musisz zaimplementować movemetodę. To jest przykładowa movemetoda:

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

Jest tu wiele ważnych rzeczy. Najpierw zauważ, że pole jest przekazywane jako Map<Player, Point2D>. Pole jest nieskończone - nie ma ograniczenia co do odległości. To nie jest dwuwymiarowa tablica ani nic takiego. Ponadto oznacza to, że jako lokalizację będziesz mieć współrzędne inne niż całkowite. To jest całkowicie w porządku.

Inną konsekwencją jest to, że gracze i balon mogą się nakładać. W rzeczywistości dwóch graczy może znajdować się dokładnie w tej samej lokalizacji!

Balon ma określoną prędkość i kierunek. Zasadniczo spadnie w tempie 3 jednostek / krok. Porusza się także w xkierunku i ykierunku. Kiedy wracasz a Hit, mijasz kierunki x, y i z, które pchasz balon. Nie może trafić balon, którego wysokość jest większa niż 10 lub którego odległość od Ciebie (tylko w dwóch wymiarach) jest większa niż 4. Ponadto, jeśli prawdą jest, że x^2 + y^2 + z^2 > s^2tam gdzie sjest wasza siła, i x, yi zsą kierunki, które uderzają Twoje działanie zostanie odrzucone. Siła twojego trafienia jest wzmacniana przez losową liczbę pomiędzy 0i luck(co oznacza, że ​​może spaść, jeśli twoje szczęście jest mało).

Podobnie możesz zwrócić a Movementza pomocą współrzędnych xi y, które poruszasz (pamiętaj, że nie możesz skakać w powietrzu). Jeśli x^2 + y^2 > s^2gdzie sjest twoja prędkość, twoje działanie jest odrzucane.

Jeśli balon wodny uderzy o ziemię, wybierany jest losowy gracz, z większą wagą dla najbliższych - ale mniejszą dla tych, którzy mają więcej szczęścia. Wybrany gracz traci 4 punkty.

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

Gra trwa 1000 kroków. Na końcu będzie plik o nazwie log.out. Skopiuj i wklej dane do tego skrzypca, aby wyświetlić grę: https://jsfiddle.net/prankol57/s2x776dt/embedded/result/

Lub jeszcze lepiej, zobacz w 3D: http://www.brianmacintosh.com/waterballoonwars (dzięki BMac)

Gracz z najwyższą sumą wyników po 100 (może być więcej, ale nie mniej) grach wygrywa.

Jeśli chcesz przesłać rozwiązanie, możesz przeczytać naprawdę szczegółowe informacje na stronie https://github.com/prakol16/water-balloon-wars/tree/master .

Edytuj 3/8 :

Na razie są to końcowe wyniki (1000 iteracji, bez uwzględnienia graczy 1 i 2). Jeśli edytujesz swój post, możesz komentować, a ja powtórzę wyniki:

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

Zwycięzca uzyskał Weaklingśrednio 39 punktów. Drugie miejsce zajęło Repeller21 punktów.

soktinpk
źródło
1
Co się stanie, gdy uderzysz balon? Jak się porusza? Co jeśli trafi w to wiele osób?
Keith Randall
Animacja z jsfiddle jest naprawdę fajna!
CommonGuy
Nawiasem mówiąc, powinieneś uczynić metody klasy Player ostatecznymi, w przeciwnym razie zgłoszenia mogą je zastąpić.
CommonGuy
2
Odwróciłeś się speedi strengthw konstruktorze odtwarzacza.
Thrax
@KeithRandall dirX, dirYoraz dirZ(wzmacniany przez szczęścia) są po prostu dodaje się do prędkości balonu. Jeśli trafi go wiele osób (nieco mało prawdopodobne), gracz, który może zdobyć trzy punkty, decyduje o szczęściu (zobacz szczegółowe informacje)
soktinpk

Odpowiedzi:

7

Symulator

Mam nadzieję, że to w porządku, ponieważ tak naprawdę nie jest to wpis. Bardzo podobał mi się pomysł symulatora wizualnego i chciałem stworzyć własny, który sprawiłby, że trochę łatwiej było zobaczyć wszystko na raz (pełne 3D).

2/28 9:06 PST : aktualizacja z kontrolkami follow, kolorami

3/4 08:47 PST : aktualizacja za pomocą suwaka dla prędkości symulacji i sprawiła, że ​​uruchomienie nowej gry faktycznie działa bez odświeżania strony (użyj Ctrl-F5, aby ponownie załadować buforowany skrypt)

Wizualizator ThreeJS online

wprowadź opis zdjęcia tutaj

BMac
źródło
3
+1000 To jest niesamowite. Dziękuję
soktinpk
Nie masz na myśli Ctrl + F5, a nie Shift + F5?
Timtech
Wygląda na to, że oba działają w Chrome.
BMac
7

Tam i z powrotem

Ten bot próbuje się zbliżyć i uderzyć balonem, dopóki jego wysokość nie będzie zbyt niska i spróbuje uciec.

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
źródło
wygląda na to, że twój bot wykonuje nielegalne ruchy, a tym samym nic nie robi.
Moogie
@soktinpk Naprawiłem przesyłanie, teraz powinno być lepiej. Dzięki też Moogie!
Thrax,
Nadal uważam, że twój bot prosi o ruch wykraczający poza to, co jest możliwe. Wprowadziłem edycję Twojego postu do sprawdzenia. Zasadniczo używałeś pozycji balonu jako ruchu.
Moogie,
@Moogie Racja, wielkie dzięki!
Thrax,
Miło, że mogłem pomóc. Twój bot jest całkiem dobry w uzyskiwaniu pozytywnych wyników. dobra robota!
Moogie
5

AngryPenguin

Ten pingwin jest zły, ponieważ nie może polecieć do balonu, więc próbuje uderzyć balonem w twarz stojących wokół niego ludzi.

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
źródło
To jest ten do pokonania.
Kevin Workman
5

Słabeusz

Ten bot może tylko dotknąć balonu, ponieważ jest tak słaby, zamiast tego polega na swoim szczęściu. Dlatego działa podobnie do LuckyLoser (z którego ten bot jest zainspirowany).

Wydaje się, że wykonuje wszystkie obecne boty, w tym 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);
    }
}

EDYCJA: zmniejszona prędkość na korzyść szczęścia

Moogie
źródło
3

Hydrofob

Jest to jeden z najprostszych możliwych botów, ale ponieważ jest konkurencyjny, opublikuję go.

Strategia: cóż ... ten bot nienawidzi wody, więc po prostu znika.

Ponieważ bot będzie rozpryskiwany bardzo rzadko, uzyska średnio nieco poniżej 0 punktów. Suma wszystkich wyników botów wynosi -1 * [balon uderza w ziemię], więc Hydrophobe prawdopodobnie uzyska wynik powyżej średniej.

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
źródło
3

Trzymaj się z daleka

Gracz ten goni balon, dopóki jego wysokość wynosi> 2. Gdy tylko trafi w balon, uderza w balon z dala od najbliższego gracza. Gdy wysokość balonu wynosi <2, ten gracz ucieka.

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

}

Edycja: Grałem z Player1 i Player2 włącznie. W takim przypadku ten gracz wygrywa, ale przegrywa, gdy go wyjmuję. Booooo.

Kevin Workman
źródło
3

Szczęśliwy przegrany

Ten bot opiera się na wysokim wyniku szczęścia. Jeśli nie znajduje się w pobliżu balonu, biegnie w kierunku balonu. Gdy znajdzie się w pobliżu balonu, jeśli w zasięgu balonu będzie co najmniej 2 innych graczy, wyrzuci go na ziemię. W przeciwnym razie podrzuci go prosto.

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

EDYCJA: Naprawiono błąd ruchu, który sprawiał, że uciekałem nie w stronę balonu> _ <Teraz biegnę prosto w kierunku balonu, jeśli nie mogę go trafić.

Fongoid
źródło
3

Odstraszacz

Ten bot polega tylko na jednym prawdziwym ruchu i polega na tym, aby odepchnąć balon od siebie. tzn. odpycha balon.

Wygląda na to, że dobrze sobie radzi z obecną liczbą botów (LuckyLoser, AngryPenguin, Hydrophobe, BackAndForth) prawie zawsze wygrywając. Jednak Hydrophobe, przez bezczynność, jest zawsze gotowy na zwycięstwo, jeśli wszystkim innym botom uda się uzyskać wynik ujemny: 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
źródło