Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/caspar ai #6820

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ java {

allprojects {
repositories {
google()
mavenCentral()
}
}
Expand Down
8 changes: 5 additions & 3 deletions megamek/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ dependencies {

implementation 'com.ibm.icu:icu4j:77.1'

implementation 'org.tensorflow:tensorflow-core-platform:1.0.0'

runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:4.0.5'

testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.1'
Expand Down Expand Up @@ -478,13 +480,13 @@ tasks.register('unitFileMigrationTool', JavaExec) {
mainClass = 'megamek.utilities.UnitFileMigrationTool'
}

tasks.register('testAi', JavaExec) {
tasks.register('runAiGame', JavaExec) {
dependsOn jar
description = 'Test AI'
description = 'Start a game from a save and load all players as AIss'
group = 'utility'
classpath = sourceSets.main.runtimeClasspath
mainClass = 'megamek.utilities.QuickGameRunner'
args(project.hasProperty("testAiArgs") ? project.property("testAiArgs").split(' ') : "")
args(project.hasProperty("testAiArgs") ? project.property("testAiArgs").split(" ") : "")

jvmArgs = mmJvmOptions
}
1 change: 1 addition & 0 deletions megamek/data/ai/brains/default/fingerprint.pb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ñž¬¥ÕðŒº¥Š…ƒ®¿•Œ©f‘€¨›ˆ”¸Ó¶ ô÷Ü¿æô£‰L(̹ŽÇ¸ÍýÔ2
31 changes: 31 additions & 0 deletions megamek/data/ai/brains/default/keras_metadata.pb

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions megamek/data/ai/brains/default/min_max_feature_normalization.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
feature,min,max
0,0.0,3.0
1,0.0,1.7
2,-1.0,1.0
3,0.48,1.0
4,0.0,1.0
5,0.0,58.52349955359813
6,0.0,64.0
7,0.0,1.0
8,0.0,1.0
9,0.0,12.0
10,0.0,17.0
11,0.0,1.0
12,-1.0,293.0
13,-0.16666666666666666,48.833333333333336
14,-0.16666666666666666,48.833333333333336
15,-0.1,0.1
16,0.0,84.0
17,0.3333333333333333,1.0
18,0.0,1.0
19,0.0,800.7000000000048
20,0.0,27.0
21,0.0,0.8625
22,0.0,1.0
23,0.0,0.99
24,0.0,1.0
25,0.36,0.76
26,0.0,6.0
27,0.0,1.0
28,0.0,73.40980860893181
29,0.0,1.0
30,0.0,1.0
31,-7.5103256576259945,17.2
32,0.0,0.7272727272727273
33,0.0,2.5298129391644855
34,0.0,1.516082016182458
35,0.0,0.8571428571428571
36,0.0,0.711111111111111
37,0.0,1.0
38,0.0,0.9176470588235294
39,0.0,1.0
40,0.42293749999999997,0.6427499999999999
41,0.0,459.0
42,0.0,1.0
43,0.0,46.87216658103186
44,0.0,27.0
45,-1.0,25.0
46,-1.0,97.0
47,0.0,1.0
48,0.0,1.0
49,0.0,1.0
50,0.0,1.0
51,0.0,1.0
52,0.0,1.0
53,0.0,1.0
54,0.0,1.0
Binary file added megamek/data/ai/brains/default/saved_model.pb
Binary file not shown.
300 changes: 300 additions & 0 deletions megamek/data/ai/brains/default/test_inputs.csv

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
9 changes: 8 additions & 1 deletion megamek/src/megamek/ai/dataset/ActionAndState.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
package megamek.ai.dataset;


import megamek.common.Entity;

import java.util.List;

/**
Expand All @@ -37,4 +39,9 @@
* @param boardUnitState state of the board when the action is performed
* @author Luana Coppio
*/
public record ActionAndState(int round, UnitAction unitAction, List<UnitState> boardUnitState){}
public record ActionAndState(int round, UnitAction unitAction, List<UnitState> boardUnitState) {
public Entity getEntity() {
var unitStateOpt = boardUnitState.stream().filter(unitState -> unitState.id() == unitAction.id()).findFirst();
return unitStateOpt.map(UnitState::entity).orElse(null);
}
}
67 changes: 41 additions & 26 deletions megamek/src/megamek/ai/dataset/UnitAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
*/
package megamek.ai.dataset;

import megamek.common.Pathing;
import megamek.client.ui.SharedUtility;
import megamek.common.Coords;
import megamek.common.CubeCoords;
import megamek.common.Entity;
import megamek.common.Game;
import megamek.common.MovePath;
import megamek.common.MoveStep;

Expand Down Expand Up @@ -57,50 +60,62 @@
* @author Luana Coppio
*/
public record UnitAction(int id, int teamId, int playerId, String chassis, String model, int facing, int fromX, int fromY, int toX, int toY, int hexesMoved, int distance, int mpUsed,
int maxMp, double mpP, double heatP, double armorP, double internalP, boolean jumping, boolean prone,
boolean legal, double chanceOfFailure, List<MovePath.MoveStepType> steps, boolean bot) {
int maxMp, double mpP, double heatP, double armorP, double internalP, boolean jumping, boolean prone,
boolean legal, double chanceOfFailure, List<MovePath.MoveStepType> steps, boolean bot) {

public static UnitAction fromMovePath(MovePath movePath) {
Entity entity = movePath.getEntity();
double chanceOfFailure = SharedUtility.getPSRList(movePath).stream().map(psr -> psr.getValue() / 36d).reduce(0.0, (a, b) -> a * b);
var steps = movePath.getStepVector().stream().map(MoveStep::getType).toList();
return new UnitAction(
entity.getId(),
entity.getOwner() != null ? entity.getOwner().getTeam() : -1,
entity.getOwner() != null ? entity.getOwner().getId() : -1,
entity.getChassis(),
entity.getModel(),
movePath.getFinalFacing(),
movePath.getStartCoords() != null ? movePath.getStartCoords().getX() : -1,
movePath.getStartCoords() != null ? movePath.getStartCoords().getY() : -1,
movePath.getFinalCoords() != null ? movePath.getFinalCoords().getX() : -1,
movePath.getFinalCoords() != null ? movePath.getFinalCoords().getY() : -1,
movePath.getHexesMoved(),
movePath.getDistanceTravelled(),
movePath.getMpUsed(),
movePath.getMaxMP(),
movePath.getMaxMP() > 0 ? (double) movePath.getMpUsed() / movePath.getMaxMP() : 0.0,
entity.getHeatCapacity() > 0 ? entity.getHeat() / (double) entity.getHeatCapacity() : 0.0,
entity.getArmorRemainingPercent(),
entity.getInternalRemainingPercent(),
movePath.isJumping(),
movePath.getFinalProne(),
movePath.isMoveLegal(),
chanceOfFailure,
steps,
entity.getOwner().isBot()
entity.getId(),
entity.getOwner() != null ? entity.getOwner().getTeam() : -1,
entity.getOwner() != null ? entity.getOwner().getId() : -1,
entity.getChassis(),
entity.getModel(),
movePath.getFinalFacing(),
movePath.getStartCoords() != null ? movePath.getStartCoords().getX() : -1,
movePath.getStartCoords() != null ? movePath.getStartCoords().getY() : -1,
movePath.getFinalCoords() != null ? movePath.getFinalCoords().getX() : -1,
movePath.getFinalCoords() != null ? movePath.getFinalCoords().getY() : -1,
movePath.getHexesMoved(),
movePath.getDistanceTravelled(),
movePath.getMpUsed(),
movePath.getMaxMP(),
movePath.getMaxMP() > 0 ? (double) movePath.getMpUsed() / movePath.getMaxMP() : 0.0,
entity.getHeatCapacity() > 0 ? entity.getHeat() / (double) entity.getHeatCapacity() : 0.0,
entity.getArmorRemainingPercent(),
entity.getInternalRemainingPercent(),
movePath.isJumping(),
movePath.getFinalProne(),
movePath.isMoveLegal(),
chanceOfFailure,
steps,
entity.getOwner().isBot()
);
}

public Coords currentPosition() {
return new Coords(fromX, fromY);
}

public CubeCoords currentCubePosition() {
return currentPosition().toCube();
}

public CubeCoords finalCubePosition() {
return finalPosition().toCube();
}

public boolean isHuman() {
return !bot;
}

public Coords finalPosition() {
return new Coords(toX, toY);
}

public Pathing movePath(Entity entity) {
return new UnitPath(this, entity);
}
}
12 changes: 6 additions & 6 deletions megamek/src/megamek/ai/dataset/UnitActionField.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@ public String getHeaderName() {
*/
public static String getHeaderLine() {
return Arrays.stream(values())
.map(UnitActionField::getHeaderName)
.collect(Collectors.joining("\t"));
.map(UnitActionField::getHeaderName)
.collect(Collectors.joining("\t"));
}

/**
* Builds the TSV header line (joined by tabs) by iterating over all enum constants.
*/
public static String getPartialHeaderLine(int startsAt, int endsAt) {
return Arrays.stream(values())
.skip(startsAt)
.limit(endsAt - startsAt)
.map(UnitActionField::getHeaderName)
.collect(Collectors.joining("\t"));
.skip(startsAt)
.limit(endsAt - startsAt)
.map(UnitActionField::getHeaderName)
.collect(Collectors.joining("\t"));
}
}
10 changes: 3 additions & 7 deletions megamek/src/megamek/ai/dataset/UnitActionSerde.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,8 @@
*/
package megamek.ai.dataset;

import megamek.common.MovePath;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
Expand All @@ -42,7 +38,7 @@
public class UnitActionSerde extends TsvSerde<UnitAction> {

private final DecimalFormat LOG_DECIMAL =
new DecimalFormat("0.00", DecimalFormatSymbols.getInstance());
new DecimalFormat("0.00", DecimalFormatSymbols.getInstance());

@Override
public String toTsv(UnitAction obj) {
Expand Down Expand Up @@ -73,8 +69,8 @@ public String toTsv(UnitAction obj) {

// For STEPS, join the list of MoveStepType values with a space.
row[UnitActionField.STEPS.ordinal()] = obj.steps().stream()
.map(Enum::name)
.collect(Collectors.joining(" "));
.map(Enum::name)
.collect(Collectors.joining(" "));

return String.join("\t", row);
}
Expand Down
112 changes: 112 additions & 0 deletions megamek/src/megamek/ai/dataset/UnitPath.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
*
* This file is part of MegaMek.
*
* MegaMek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPL),
* version 2 or (at your option) any later version,
* as published by the Free Software Foundation.
*
* MegaMek is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* A copy of the GPL should have been included with this project;
* if not, see <https://www.gnu.org/licenses/>.
*
* NOTICE: The MegaMek organization is a non-profit group of volunteers
* creating free software for the BattleTech community.
*
* MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
* of The Topps Company, Inc. All Rights Reserved.
*
* Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
* InMediaRes Productions, LLC.
*/
package megamek.ai.dataset;

import megamek.common.Pathing;
import megamek.common.Coords;
import megamek.common.CubeCoords;
import megamek.common.Entity;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class UnitPath implements Pathing {

private final UnitAction unitAction;
private final Entity entity;
private final Set<Coords> coords;

public UnitPath(UnitAction unitAction, Entity entity) {
this.unitAction = unitAction;
this.entity = entity;
this.coords = new HashSet<>();
List<CubeCoords> cubeCoordsLine = unitAction.currentCubePosition().lineTo(unitAction.finalCubePosition());
cubeCoordsLine.forEach(c -> coords.add(c.toOffset()));
}

@Override
public Coords getStartCoords() {
return unitAction.currentPosition();
}

@Override
public Coords getFinalCoords() {
return unitAction.finalPosition();
}

@Override
public int getFinalFacing() {
return unitAction.facing();
}

@Override
public Entity getEntity() {
return entity;
}

@Override
public Set<Coords> getCoordsSet() {
return coords;
}

@Override
public int getHexesMoved() {
return unitAction.hexesMoved();
}

@Override
public int getDistanceTravelled() {
return unitAction.distance();
}

@Override
public boolean hasWaypoint() {
return false;
}

@Override
public Coords getWaypoint() {
return null;
}

@Override
public boolean isJumping() {
return unitAction.jumping();
}

@Override
public boolean getFinalProne() {
return unitAction.prone();
}

@Override
public int getMpUsed() {
return unitAction.mpUsed();
}
}
Loading