Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 218 additions & 0 deletions src/main/java/rlbotexample/input/ModernBallPrediction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package rlbotexample.input;

import rlbot.cppinterop.RLBotDll;
import rlbot.cppinterop.RLBotInterfaceException;
import rlbot.flat.BallPrediction;
import rlbot.flat.Physics;
import rlbot.flat.PredictionSlice;
import rlbotexample.input.ball.BallData;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class ModernBallPrediction {

public final int tickRate; // close to 60 or 120
public final float tickFrequency; // close to 1/60 or 1/120
public final List<ModernPredictionFrame> frames;

private ModernBallPrediction(List<ModernPredictionFrame> frames, float tickFrequency) {
if (frames == null)
frames = new ArrayList<>();
float latencyCompensation = RLConstants.gameLatencyCompensation;
if (latencyCompensation > 0) {
List<ModernPredictionFrame> newFrames = new ArrayList<>(frames.size());
for (ModernPredictionFrame frame : frames) {
if (frame.relativeTime < latencyCompensation)
continue;

frame.adjustForLatencyCompensation(latencyCompensation);
newFrames.add(frame);
}
frames = newFrames;
}
frames = Collections.unmodifiableList(frames);

this.frames = frames;
this.tickFrequency = tickFrequency;
this.tickRate = Math.round(1 / tickFrequency);
}

public static ModernBallPrediction from(List<ModernPredictionFrame> frames, float tickFrequency) {
return new ModernBallPrediction(frames, tickFrequency);
}

public static ModernBallPrediction empty() {
return new ModernBallPrediction(null, RLConstants.tickFrequency);
}

private static ModernBallPrediction from(BallPrediction ballPrediction) {
assert ballPrediction.slicesLength() > 0 : "RLBot Ball Prediction has no frames";

List<ModernPredictionFrame> frames = new ArrayList<>(ballPrediction.slicesLength());

float startTime = ballPrediction.slices(0).gameSeconds();
float lastTime = startTime;
float averageDt = 0;

for (int i = 0; i < ballPrediction.slicesLength(); i++) {
PredictionSlice slice = ballPrediction.slices(i);
frames.add(new ModernPredictionFrame(slice.gameSeconds() - startTime, slice));

averageDt += slice.gameSeconds() - lastTime;
lastTime = slice.gameSeconds();
}

averageDt /= ballPrediction.slicesLength();

return new ModernBallPrediction(frames, averageDt);
}

public static ModernBallPrediction get() {
return get(BallPredictionType.RLBOT);
}

public static ModernBallPrediction get(BallPredictionType ballPredictionType) {
switch (ballPredictionType) {
case RLBOT:
try {
return ModernBallPrediction.from(RLBotDll.getBallPrediction());
} catch (RLBotInterfaceException e) {
System.err.println("Could not get RLBot ball Prediction!");
throw new RuntimeException(e);
}
}

throw new IllegalStateException("Ball Prediction Type '" + ballPredictionType.name() + "' not recognized");
}

/*public void draw(AdvancedRenderer renderer, Color color, float length) {
if (this.frames.size() == 0)
return;
if (length <= 0)
length = this.relativeTimeOfLastFrame();

float time = 0;
float lastAbsTime = this.frames.get(0).absoluteTime;
Vector3 lastPos = this.frames.get(0).ballData.position;
while (time < length) {
Optional<YangPredictionFrame> frame = this.getFrameAfterRelativeTime(time);
if (!frame.isPresent())
break;

if (Math.floor(lastAbsTime) < Math.floor(frame.get().absoluteTime)) {
renderer.drawLine3d(color.brighter(), frame.get().ballData.position, frame.get().ballData.position.add(0, 0, 50));
}
lastAbsTime = frame.get().absoluteTime;
time = frame.get().relativeTime;
ImmutableBallData ball = frame.get().ballData;

if (ball.makeMutable().isInAnyGoal()) {
renderer.drawCentered3dCube(color.brighter().brighter(), ball.position, 50);
renderer.drawString3d(String.format("Goal! (%.1f)", time), Color.WHITE, ball.position.add(0, 0, 150), 1, 1);
renderer.drawLine3d(color, lastPos, ball.position);
break;
}

if (lastPos.distance(ball.position) < 50)
continue;
renderer.drawLine3d(color, lastPos, ball.position);
lastPos = ball.position;
}
}*/

public float relativeTimeOfLastFrame() {
if (this.frames.size() == 0)
return -1;
return this.frames.get(this.frames.size() - 1).relativeTime;
}

public ModernBallPrediction trim(float relativeStartTime, float relativeEndTime) {
if (relativeEndTime < relativeStartTime)
throw new IllegalArgumentException("Relative end time smaller than relative start time");
if (relativeEndTime - relativeStartTime == 0)
return ModernBallPrediction.empty();

return new ModernBallPrediction(this.getFramesBetweenRelative(relativeStartTime, relativeEndTime), this.tickFrequency);
}

public Optional<ModernPredictionFrame> getFrameAtRelativeTime(float relativeTime) {
return this.frames
.stream()
.filter((f) -> f.relativeTime >= relativeTime)
.findFirst();
}

public Optional<ModernPredictionFrame> getFrameAfterRelativeTime(float relativeTime) {
return this.frames
.stream()
.filter((f) -> f.relativeTime > relativeTime)
.findFirst();
}

public Optional<ModernPredictionFrame> getFrameAtAbsoluteTime(float absolute) {
return this.frames
.stream()
.filter((f) -> f.absoluteTime >= absolute)
.findFirst();
}

public List<ModernPredictionFrame> getFramesBeforeRelative(float relativeTime) {
return this.frames
.stream()
.filter((f) -> f.relativeTime < relativeTime)
.collect(Collectors.toList());
}

public ModernBallPrediction getBeforeRelative(float relativeTime) {
return new ModernBallPrediction(getFramesBeforeRelative(relativeTime), this.tickFrequency);
}

public List<ModernPredictionFrame> getFramesAfterRelative(float relativeTime) {
return this.frames
.stream()
.filter((f) -> f.relativeTime > relativeTime)
.collect(Collectors.toList());
}

public List<ModernPredictionFrame> getFramesBetweenRelative(float start, float end) {
return this.frames
.stream()
.filter((f) -> f.relativeTime > start && f.relativeTime < end)
.collect(Collectors.toList());
}

public boolean hasFrames() {
return this.frames.size() > 0;
}

enum BallPredictionType {
RLBOT
}

public static class ModernPredictionFrame {
public final float absoluteTime;
public final BallData ballData;
public float relativeTime;

public ModernPredictionFrame(float absoluteTime, float relativeTime, BallData ballData) {
this.absoluteTime = absoluteTime;
this.relativeTime = relativeTime;
this.ballData = ballData;
}

public ModernPredictionFrame(float relativeTime, PredictionSlice predictionSlice) {
this.relativeTime = relativeTime;
this.absoluteTime = predictionSlice.gameSeconds();
Physics physics = predictionSlice.physics();
this.ballData = new BallData(physics);
}

public void adjustForLatencyCompensation(float offset) {
relativeTime -= offset;
}
}
}
43 changes: 43 additions & 0 deletions src/main/java/rlbotexample/input/RLConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package rlbotexample.input;

import rlbotexample.vector.Vector2;
import rlbotexample.vector.Vector3;

public class RLConstants {

public static final float carElevation = 17.01f;

public static final float goalDistance = 5120f; // Distance from center to goal
public static final float goalCenterToPost = 892.755f;
public static final float goalHeight = 642.775f;

public static final float arenaHeight = 2044;
public static final float arenaWidth = 8192;
public static final float arenaHalfWidth = arenaWidth / 2;
public static final float arenaLength = 10240;
public static final float arenaHalfLength = arenaLength / 2;

public static Vector3 gravity = new Vector3(0, 0, -650);

public static float gameLatencyCompensation = 0.f;

public static int tickRate = 120;
public static float tickFrequency = 1f / tickRate;

public static int simulationTickRate = 60;
public static float simulationTickFrequency = 1f / simulationTickRate;

public static boolean isPosNearWall(Vector2 pos, float tolerance) {
float x = (float) Math.abs(pos.x) + tolerance;
float y = (float) Math.abs(pos.y);

if (x >= 2700 - tolerance && x <= 3850 + tolerance) {
// https://www.wolframalpha.com/input/?i=Line+between+%283850%2C+3850%29+%282700%2C+4950%29
float val = 173250f / 23f - (22f * x) / 23f;
if (val <= y + tolerance)
return true;
}

return x >= 3820 || y + tolerance >= 4920;
}
}
9 changes: 9 additions & 0 deletions src/main/java/rlbotexample/input/ball/BallData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import rlbot.flat.BallInfo;
import rlbot.flat.Physics;
import rlbotexample.vector.Vector3;

/**
Expand All @@ -24,4 +25,12 @@ public BallData(final BallInfo ball) {
this.hasBeenTouched = ball.latestTouch() != null;
this.latestTouch = this.hasBeenTouched ? new BallTouch(ball.latestTouch()) : null;
}

public BallData(final Physics ballPhysics) {
this.position = new Vector3(ballPhysics.location());
this.velocity = new Vector3(ballPhysics.velocity());
this.spin = new Vector3(ballPhysics.angularVelocity());
this.hasBeenTouched = false;
this.latestTouch = null;
}
}
6 changes: 6 additions & 0 deletions src/main/java/rlbotexample/input/car/CarData.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package rlbotexample.input.car;


import rlbot.flat.Rotator;
import rlbotexample.vector.Matrix3x3;
import rlbotexample.vector.Vector3;

/**
Expand All @@ -20,6 +22,8 @@ public class CarData {
/** The orientation of the car */
public final CarOrientation orientation;

public final Matrix3x3 matOrientation;

/** Boost ranges from 0 to 100 */
public final double boost;

Expand Down Expand Up @@ -52,5 +56,7 @@ public CarData(rlbot.flat.PlayerInfo playerInfo, float elapsedSeconds) {
this.team = playerInfo.team();
this.hasWheelContact = playerInfo.hasWheelContact();
this.elapsedSeconds = elapsedSeconds;
Rotator r = playerInfo.physics().rotation();
this.matOrientation = Matrix3x3.eulerToRotation(new Vector3(r.pitch(), r.yaw(), r.roll()));
}
}
45 changes: 45 additions & 0 deletions src/main/java/rlbotexample/util/Tuple.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package rlbotexample.util;

import java.io.Serializable;
import java.util.Objects;

public class Tuple<K, V> implements Serializable {

private K key;
private V value;

public Tuple(K key, V value) {
this.key = key;
this.value = value;
}

public K getKey() {
return key;
}

public V getValue() {
return value;
}

@Override
public String toString() {
return key + "=" + value;
}

@Override
public int hashCode() {
return key.hashCode() * 13 + (value == null ? 0 : value.hashCode());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Tuple) {
Tuple pair = (Tuple) o;
if (!Objects.equals(key, pair.key)) return false;
if (!Objects.equals(value, pair.value)) return false;
return true;
}
return false;
}
}
Loading