public Set<HotColdLocation> signal(@Nonnull final WorldPoint worldPoint, @Nonnull final HotColdTemperature temperature, @Nullable final HotColdTemperatureChange temperatureChange)
	{
		// when the strange device reads a temperature, that means that the center of the final dig location
		// is a range of squares away from the player's current location (Chebyshev AKA Chess-board distance)
		int maxSquaresAway = temperature.getMaxDistance();
		int minSquaresAway = temperature.getMinDistance();

		// maxDistanceArea encompasses all of the points that are within the max possible distance from the player
		final Rectangle maxDistanceArea = new Rectangle(
			worldPoint.getX() - maxSquaresAway,
			worldPoint.getY() - maxSquaresAway,
			2 * maxSquaresAway + 1,
			2 * maxSquaresAway + 1);
		// minDistanceArea encompasses all of the points that are within the min possible distance from the player
		final Rectangle minDistanceArea = new Rectangle(
			worldPoint.getX() - minSquaresAway,
			worldPoint.getY() - minSquaresAway,
			2 * minSquaresAway + 1,
			2 * minSquaresAway + 1);

		// eliminate from consideration dig spots that lie entirely within the min range or entirely outside of the max range
		possibleLocations.removeIf(entry -> minDistanceArea.contains(entry.getRect()) || !maxDistanceArea.intersects(entry.getRect()));

		// if a previous world point has been recorded, we can consider the warmer/colder result from the strange device
		if (lastWorldPoint != null && temperatureChange != null)
		{
			switch (temperatureChange)
			{
				case COLDER:
					// eliminate spots that are warmer or same temperature
					possibleLocations.removeIf(location ->
					{
						final WorldPoint locationPoint = location.getWorldPoint();
						return locationPoint.distanceTo2D(worldPoint) <= locationPoint.distanceTo2D(lastWorldPoint);
					});
					break;
				case WARMER:
					// eliminate spots that are colder or same temperature
					possibleLocations.removeIf(location ->
					{
						final WorldPoint locationPoint = location.getWorldPoint();
						return locationPoint.distanceTo2D(worldPoint) >= locationPoint.distanceTo2D(lastWorldPoint);
					});
					break;
				case SAME:
					// eliminate spots which are colder or warmer (as they would not yield a SAME temperature change)
					possibleLocations.removeIf(location ->
					{
						final WorldPoint locationPoint = location.getWorldPoint();
						return locationPoint.distanceTo2D(worldPoint) != locationPoint.distanceTo2D(lastWorldPoint);
					});
			}
		}

		lastWorldPoint = worldPoint;
		return getPossibleLocations();
	}
@Test
	public void testBeginnerCowFieldNarrowing()
	{
		// Start with Cow field north of Lumbridge and Northeast of Al Kharid mine locations remaining
		final Set<HotColdLocation> startingLocations = EnumSet.of(
			HotColdLocation.LUMBRIDGE_COW_FIELD,
			HotColdLocation.NORTHEAST_OF_AL_KHARID_MINE
		);
		HotColdSolver solver = new HotColdSolver(startingLocations);

		assertEquals(startingLocations, solver.signal(new WorldPoint(3313, 3208, 0), HotColdTemperature.WARM, null));
		assertEquals(Sets.immutableEnumSet(HotColdLocation.LUMBRIDGE_COW_FIELD), solver.signal(new WorldPoint(3312, 3208, 0), HotColdTemperature.WARM, HotColdTemperatureChange.WARMER));
	}