class PickedMoveStepScoreDiffStatisticListener<Solution_, Score_ extends Score<Score_>>
            extends PhaseLifecycleListenerAdapter<Solution_> {
        private Score_ oldStepScore = null;
        private final ScoreDefinition<Score_> scoreDefinition;
        private final Map<Tags, List<AtomicReference<Number>>> tagsToMoveScoreMap = new ConcurrentHashMap<>();

        public PickedMoveStepScoreDiffStatisticListener(ScoreDefinition<Score_> scoreDefinition) {
            this.scoreDefinition = scoreDefinition;
        }

        @Override
        public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
            if (phaseScope instanceof LocalSearchPhaseScope) {
                oldStepScore = phaseScope.getStartingScore();
            }
        }

        @Override
        public void phaseEnded(AbstractPhaseScope<Solution_> phaseScope) {<FILL_FUNCTION_BODY>}

        @Override
        public void stepEnded(AbstractStepScope<Solution_> stepScope) {
            if (stepScope instanceof LocalSearchStepScope) {
                localSearchStepEnded((LocalSearchStepScope<Solution_>) stepScope);
            }
        }

        @SuppressWarnings("unchecked")
        private void localSearchStepEnded(LocalSearchStepScope<Solution_> stepScope) {
            String moveType = stepScope.getStep().getSimpleMoveTypeDescription();
            Score_ newStepScore = (Score_) stepScope.getScore();
            Score_ stepScoreDiff = newStepScore.subtract(oldStepScore);
            oldStepScore = newStepScore;

            SolverMetric.registerScoreMetrics(SolverMetric.PICKED_MOVE_TYPE_STEP_SCORE_DIFF,
                    stepScope.getPhaseScope().getSolverScope().getMonitoringTags()
                            .and("move.type", moveType),
                    scoreDefinition,
                    tagsToMoveScoreMap,
                    stepScoreDiff);
        }
    }

            if (phaseScope instanceof LocalSearchPhaseScope) {
                oldStepScore = null;
            }
        
<no_super_class>