Search in sources :

Example 1 with ScreenGroup

use of net.hearthstats.game.ScreenGroup in project HearthStats.net-Uploader by HearthStats.

the class ScreenAnalyser method identifyScreen.

/**
   * <p>
   * Identifies the screen in the given image. If possible it will perform an
   * 'exact' match, meaning that it all the primary pixels specified in
   * {@link Screen} were within range. An exact match is very accurate, however
   * sometimes an exact match is not possible due to effects (eg partial
   * effects) or obstructing objects (eg a card being dragged over certain
   * pixels) so a partial match is performed. If a partial match doesn't
   * identify a screen with high enough confidence, no screen is returned.
   * </p>
   * <p>
   * It is considered normal that some screens can't be identified. Only the
   * screens that indicate important events have been defined in Screen so far;
   * some Hearthstone screens can't be identified but this is silently ignored
   * by the HearthstoneAnalyser which expects some unknown screens.
   * </p>
   * 
   * @param image
   *          The image to identify a Hearthstone screen from.
   * @param previousScreen
   *          The last screen that was identified; optional, but specify this to
   *          narrow down the search, reduce the risk of false positives (eg
   *          jumping out of a game unexpectedly) and generally make the
   *          analysis faster.
   * @return The Screen that was identified, or null if no screen could be
   *         identified with reasonable confidence.
   */
public Screen identifyScreen(BufferedImage image, Screen previousScreen) {
    log.trace("Identifying screen");
    // ProgramHelpers may return a null image if the window is minimised or still loading, so ignore those
    if (image == null) {
        return null;
    }
    if (expectedWidth != image.getWidth() || expectedHeight != image.getHeight()) {
        pixelMap = calculatePixelPositions(image.getWidth(), image.getHeight());
        expectedWidth = image.getWidth();
        expectedHeight = image.getHeight();
    }
    // If we have a previous screen, check only those screens which follow from
    // this one
    EnumSet<Screen> possibleScreens;
    if (previousScreen == null) {
        possibleScreens = EnumSet.allOf(Screen.class);
    } else {
        possibleScreens = previousScreen.nextScreens;
        if (possibleScreens.size() == 0) {
            throw new IllegalStateException("Unable to identify screen because previous screen " + previousScreen + " has no nextScreens parameter");
        }
    }
    Screen match = null;
    // most likely one to match, of course!
    if (previousScreen != null) {
        if (checkForExactMatch(image, previousScreen)) {
            // This screen matches
            log.trace("Exact match on previous screen {}", previousScreen);
            match = previousScreen;
        }
    }
    // only
    if (match == null) {
        for (Screen screen : possibleScreens) {
            if (checkForExactMatch(image, screen)) {
                // This screen matches
                if (log.isDebugEnabled()) {
                    if (match == null) {
                        log.trace("Exact match on new screen {}", screen);
                    } else {
                        log.warn("More that one screen matched! Matched screen {}, but have already matched {}", screen, match);
                    }
                }
                match = screen;
                // efficiency
                if (!log.isDebugEnabled())
                    break;
            }
        }
    }
    if (match == null) {
        // A check of the primary pixels did not find an exact match, so try for a
        // partial match
        log.debug("Did not find exact screen match, attempting partial match");
        Map<Screen, PartialResult> screenMatchesMap = new HashMap<>();
        int maxMatchedCount = 0;
        int maxUnmatchedCount = 0;
        Screen bestMatch = null;
        EnumSet<Screen> possibleScreensIncludingPrevious = EnumSet.copyOf(possibleScreens);
        if (previousScreen != null) {
            possibleScreensIncludingPrevious.add(previousScreen);
        }
        for (Screen screen : possibleScreensIncludingPrevious) {
            PartialResult partialResult = checkForPartialMatch(image, screen);
            if (partialResult.matchedCount >= maxMatchedCount) {
                maxMatchedCount = partialResult.matchedCount;
                bestMatch = screen;
            }
            if (partialResult.unmatchedCount > maxUnmatchedCount) {
                maxUnmatchedCount = partialResult.unmatchedCount;
            }
            log.debug("Test of screen {} matched={} unmatched={}", screen, partialResult.matchedCount, partialResult.unmatchedCount);
            screenMatchesMap.put(screen, partialResult);
        }
        // - has fewer unmatched pixels than any other screen
        assert (bestMatch != null);
        PartialResult bestMatchResult = screenMatchesMap.get(bestMatch);
        boolean acceptBestMatch = true;
        if (bestMatchResult.unmatchedCount > 2) {
            log.debug("Partial match failed because best match {} has {} unmatched pixels", bestMatch, bestMatchResult.unmatchedCount);
            acceptBestMatch = false;
        } else {
            // Check whether other screens are too close to the best-matched screen,
            // but ignore any screens considered to be equivalent (ie the playing
            // screen for each board is considered equivalent)
            ScreenGroup ignoreGroup;
            if (bestMatch.group == ScreenGroup.MATCH_PLAYING || bestMatch.group == ScreenGroup.MATCH_END) {
                ignoreGroup = bestMatch.group;
            } else {
                ignoreGroup = null;
            }
            for (Screen screen : possibleScreens) {
                if (screen != bestMatch && (ignoreGroup == null || screen.group != ignoreGroup)) {
                    // This screen is not the best match, and it's not from the same
                    // group (for those groups considered equivalent) so we need to
                    // ensure it's not too close to the best match
                    PartialResult currentResult = screenMatchesMap.get(screen);
                    if (bestMatchResult.matchedCount <= currentResult.matchedCount) {
                        log.debug("Partial match failed because best match {} has {} matched pixels whereas {} has {}", bestMatch, bestMatchResult.matchedCount, screen, currentResult.matchedCount);
                        acceptBestMatch = false;
                        break;
                    } else if (bestMatchResult.unmatchedCount >= currentResult.unmatchedCount) {
                        log.debug("Partial match failed because best match {} has {} unmatched pixels whereas {} has {}", bestMatch, bestMatchResult.unmatchedCount, screen, currentResult.unmatchedCount);
                        acceptBestMatch = false;
                        break;
                    }
                }
            }
        }
        if (acceptBestMatch) {
            log.trace("Partial match on screen {}", bestMatch);
            match = bestMatch;
        }
    }
    return match;
}
Also used : HashMap(java.util.HashMap) Screen(net.hearthstats.game.Screen) ScreenGroup(net.hearthstats.game.ScreenGroup)

Aggregations

HashMap (java.util.HashMap)1 Screen (net.hearthstats.game.Screen)1 ScreenGroup (net.hearthstats.game.ScreenGroup)1