All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zerotoheroes.hsgameparser.metadata.GameParser Maven / Gradle / Ivy

Go to download

Parse Hearthstone games to extract meaningful data (metadata, matchup, amazing plays...)

The newest version!
package com.zerotoheroes.hsgameparser.metadata;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;

import com.zerotoheroes.hsgameentities.enums.GameTag;
import com.zerotoheroes.hsgameentities.enums.PlayState;
import com.zerotoheroes.hsgameentities.replaydata.GameData;
import com.zerotoheroes.hsgameentities.replaydata.GameHelper;
import com.zerotoheroes.hsgameentities.replaydata.HearthstoneReplay;
import com.zerotoheroes.hsgameentities.replaydata.entities.FullEntity;
import com.zerotoheroes.hsgameentities.replaydata.entities.PlayerEntity;
import com.zerotoheroes.hsgameentities.replaydata.gameactions.TagChange;
import com.zerotoheroes.hsgameparser.db.Card;
import com.zerotoheroes.hsgameparser.db.CardsList;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GameParser {

	private static CardsList cardsList;

	public GameParser() throws Exception {
		if (cardsList == null) {
			cardsList = CardsList.create();
			log.debug("Created cards list with " + cardsList.getCards().size() + " cards");
		}
	}

	public GameMetaData getMetaData(HearthstoneReplay replay) {
		log.debug("retrieving metadata for " + replay);

		GameMetaData meta = new GameMetaData();

		GameHelper helper = new GameHelper();
		helper.setGame(replay.getGames().get(0));

		// Find out the last turn number
		List turnChanges = helper.getFlatData().stream().filter(d -> (d instanceof TagChange))
				.map(p -> (TagChange) p).filter(t -> t.getEntity() == 1 && t.getName() == GameTag.TURN.getIntValue())
				.collect(Collectors.toList());
		TagChange lastTurn = turnChanges.get(turnChanges.size() - 1);
		int numberOfTurns = (int) Math.ceil(lastTurn.getValue() / 2.0);
		meta.setNumberOfTurns(numberOfTurns);

		// Game duration
		List timestampedData = helper.getFlatData().stream()
				.filter(d -> !StringUtils.isEmpty(d.getTimestamp())).collect(Collectors.toList());
		Collections.sort(timestampedData, (o1, o2) -> o1.getTimestamp().compareTo(o2.getTimestamp()));

		// Get the first and last moments
		Date first = parseDate(timestampedData.get(0).getTimestamp());
		Date last = parseDate(timestampedData.get(timestampedData.size() - 1).getTimestamp());
		meta.setDurationInSeconds((int) ((last.getTime() - first.getTime()) / 1000));

		// Win status
		// Get the main player. The first one being the "current player" is us
		// The data will be ordered enough for us, as the tag is part of an
		// Action block
		PlayerEntity player = helper.getMainPlayer();
		int ourEntityId = player.getId();
		// Now find the tag change that tells us if we won
		List tagChanges = helper.filterGameData(TagChange.class);
		Optional winner = tagChanges.stream().filter(
				t -> t.getName() == GameTag.PLAYSTATE.getIntValue() && t.getValue() == PlayState.WON.getIntValue())
				.findFirst();

		String winStatus = "unknown";
		if (winner.isPresent()) {
			if (ourEntityId == winner.get().getEntity()) {
				winStatus = "won";
			}
			else {
				winStatus = "lost";
			}
		}
		// No winner means a tie or a disconnect
		else {
			Optional tied = tagChanges.stream().filter(
					t -> t.getName() == GameTag.PLAYSTATE.getIntValue() && t.getValue() == PlayState.TIED.getIntValue())
					.findFirst();
			if (tied.isPresent()) {
				winStatus = "tied";
			}
		}
		meta.setWinStatus(winStatus);
		
		// Filter player data
		List players = helper.getPlayers();
		PlayerEntity player1 = players.stream().filter(p -> p.getId() == ourEntityId).findFirst().get();
		PlayerEntity player2 = players.stream().filter(p -> p.getId() != ourEntityId).findFirst().get();
		
		meta.setPlayerName(player1.getName());
		meta.setPlayerClass(getPlayerClass(replay, player1));
		meta.setOpponentName(player2.getName());
		meta.setOpponentClass(getPlayerClass(replay, player2));

		// Find if we're on the coin or on the play
		// The first player to draw 4 cards is on the coin
		TagChange drawFourCardsTag = tagChanges.stream().filter(
				t -> t.getName() == GameTag.NUM_CARDS_DRAWN_THIS_TURN.getIntValue() && t.getValue() == 4)
				.findFirst().get();
		int coinPlayerId = drawFourCardsTag.getEntity();
		System.out.println("coin player id " + coinPlayerId +  " our " + ourEntityId);
		meta.setPlayCoin(coinPlayerId == ourEntityId ? "coin" : "play");
		
		log.debug("retrieved meta " + meta);

		return meta;
	}

	private Date parseDate(String timestamp) {
		Date result = null;
		// Try various formats
		try {
			result = new SimpleDateFormat("HH:mm:ss").parse(timestamp);
		}
		catch (ParseException e) {
			try {
				result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX").parse(timestamp);
			}
			catch (ParseException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return result;
	}

	private String getPlayerClass(HearthstoneReplay replay, PlayerEntity player) {
		List data = replay.getGames().get(0).getData();

		int playerEntityId = player.getTags().stream().filter(t -> t.getName() == GameTag.HERO_ENTITY.getIntValue())
				.findFirst().get().getValue();
		FullEntity playerEntity = data.stream().filter(d -> (d instanceof FullEntity)).map(e -> (FullEntity) e)
				.filter(e -> e.getId() == playerEntityId).findFirst().get();

		Card playerCard = cardsList.getCards().stream()
				.filter(c -> playerEntity.getCardId().equalsIgnoreCase(c.getId())).findFirst().get();

		return playerCard.getPlayerClass().toLowerCase();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy