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: slightly better serializer for unit actions and state, added attack action too #6834

Merged
merged 16 commits into from
Apr 14, 2025
Merged
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
117 changes: 117 additions & 0 deletions megamek/src/megamek/ai/dataset/BoardData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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 3 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 java.util.ArrayList;
import java.util.List;

import megamek.common.Board;
import megamek.common.Hex;

/**
* Flexible container for board data using a map-based approach with enum keys.
* @author Luana Coppio
*/
public class BoardData extends EntityDataMap<BoardData.Field> {

/**
* Enum defining all available board data fields.
*/
public enum Field {
BOARD_NAME,
WIDTH,
HEIGHT,
HEX_DATA; // Special field that contains all hex data
}

/**
* Nested class to represent a single row of hex data
*/
public static class HexRow {
private final int rowIndex;
private final List<Hex> hexes;

public HexRow(int rowIndex, List<Hex> hexes) {
this.rowIndex = rowIndex;
this.hexes = new ArrayList<>(hexes);
}

public int getRowIndex() {
return rowIndex;
}

public List<Hex> getHexes() {
return new ArrayList<>(hexes);
}
}

/**
* Creates an empty BoardData.
*/
public BoardData() {
super(Field.class);
}

/**
* Creates a BoardData from a Board.
* @param board The board to extract data from
* @return A populated BoardData
*/
public static BoardData fromBoard(Board board) {
BoardData data = new BoardData();

// Basic board information
data.put(Field.BOARD_NAME, board.getMapName())
.put(Field.WIDTH, board.getWidth())
.put(Field.HEIGHT, board.getHeight());

// Extract hex data by row
List<HexRow> hexRows = new ArrayList<>();
for (int y = 0; y < board.getHeight(); y++) {
List<Hex> rowHexes = new ArrayList<>();
for (int x = 0; x < board.getWidth(); x++) {
rowHexes.add(board.getHex(x, y));
}
hexRows.add(new HexRow(y, rowHexes));
}

data.put(Field.HEX_DATA, hexRows);

return data;
}

/**
* Gets the board's hex rows.
* @return List of HexRow objects containing the board's hexes
*/
@SuppressWarnings("unchecked")
public List<HexRow> getHexRows() {
return (List<HexRow>) get(Field.HEX_DATA);
}
}
89 changes: 89 additions & 0 deletions megamek/src/megamek/ai/dataset/BoardDataSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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 3 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 java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import megamek.ai.dataset.BoardData.Field;
import megamek.ai.dataset.BoardData.HexRow;

/**
* <p>Serializer for BoardData to TSV format.</p>
* @author Luana Coppio
*/
public class BoardDataSerializer extends EntityDataSerializer<Field, BoardData> {

public BoardDataSerializer() {
super(Field.class);
}

@Override
public String serialize(BoardData data) {
List<String> lines = new ArrayList<>();

// First line: Main board data
lines.add(getHeaderLine());

String mainData = String.join("\t",
String.valueOf(data.get(Field.BOARD_NAME)),
String.valueOf(data.get(Field.WIDTH)),
String.valueOf(data.get(Field.HEIGHT)));
lines.add(mainData);

// Get the hex data for serialization
List<HexRow> hexRows = data.getHexRows();
if (hexRows != null && !hexRows.isEmpty()) {
// Add the column header row (COL_0, COL_1, etc.)
int width = ((Integer) data.get(Field.WIDTH));
String colHeader = IntStream.range(0, width)
.mapToObj(i -> "COL_" + i)
.collect(Collectors.joining("\t"));
lines.add(colHeader);

// Add each row of hex data
for (HexRow row : hexRows) {
StringBuilder sb = new StringBuilder("ROW_").append(row.getRowIndex()).append("\t");
sb.append(row.getHexes().stream()
.map(hex -> hex == null ? "" : hex.toString())
.collect(Collectors.joining("\t")));
lines.add(sb.toString());
}
}

return String.join("\n", lines);
}

@Override
public String getHeaderLine() {
return String.join("\t", Field.BOARD_NAME.name(), Field.WIDTH.name(), Field.HEIGHT.name());
}
}
123 changes: 123 additions & 0 deletions megamek/src/megamek/ai/dataset/EntityDataMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* 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 3 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 java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

/**
* Abstract base class for data maps that use enum fields.
* @param <F> The enum type representing field names
* @author Luana Coppio
*/
public abstract class EntityDataMap<F extends Enum<F>> {

// The class of the enum type F, used for creating EnumMap
private final Class<F> fieldEnumClass;

// Use EnumMap for type safety with our enum fields
private final Map<F, Object> data;

// Keep track of insertion order separately
private final List<F> fieldOrder = new ArrayList<>();

/**
* Creates an empty EntityDataMap.
* @param fieldEnumClass The class of the enum type F
*/
protected EntityDataMap(Class<F> fieldEnumClass) {
this.fieldEnumClass = fieldEnumClass;
this.data = new EnumMap<>(fieldEnumClass);
}

/**
* Adds a field to the data map.
* @param field The field enum
* @param value The field value
* @return This EntityDataMap for method chaining
*/
public EntityDataMap<F> put(F field, Object value) {
if (!data.containsKey(field)) {
fieldOrder.add(field);
}
data.put(field, value);
return this;
}

/**
* Gets a field value from the data map.
* @param field The field enum
* @return The field value, or null if not present
*/
public Object get(F field) {
return data.get(field);
}

/**
* Gets a field value with type casting.
* @param <T> The expected type
* @param field The field enum
* @param type The class of the expected type
* @return The field value cast to the expected type, or null if not present
*/
@SuppressWarnings("unchecked")
public <T> T get(F field, Class<T> type) {
Object value = data.get(field);
if (type.isInstance(value)) {
return (T) value;
}
return null;
}

/**
* Gets all fields and values in the map.
* @return The underlying map of data
*/
public Map<F, Object> getAllFields() {
return new EnumMap<>(data);
}

/**
* Gets the ordered list of field enums.
* @return List of fields in insertion order
*/
public List<F> getFieldOrder() {
return new ArrayList<>(fieldOrder);
}

/**
* Gets the class of the field enum.
* @return The enum class
*/
public Class<F> getFieldEnumClass() {
return fieldEnumClass;
}
}
Loading