[[cloudBalancingScoreConfiguration]]
= Score configuration
:imagesdir: ../../..

OptaPlanner searches for the solution with the highest ``Score``.
This example uses a ``HardSoftScore``, which means OptaPlanner looks for the solution with no hard constraints broken (fulfill hardware requirements) and as little as possible soft constraints broken (minimize maintenance cost).

image::QuickStart/PlainJava/CloudBalancingScoreConfiguration/scoreComparisonCloudBalancing.png[align="center"]

Of course, OptaPlanner needs to be told about these domain-specific score constraints.
There are several ways to implement such a score function:

* <<cloudBalancingEasyJavaScoreConfiguration, Easy Java>>
* ConstraintStreams
* Incremental Java
* <<cloudBalancingDroolsScoreConfiguration, Drools>>


[[cloudBalancingEasyJavaScoreConfiguration]]
== Easy Java score configuration

One way to define a score function is to implement the interface `EasyScoreCalculator` in plain Java.

[source,xml,options="nowrap"]
----
  <scoreDirectorFactory>
    <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
  </scoreDirectorFactory>
----

Just implement the `calculateScore(Solution)` method to return a `HardSoftScore` instance.

.CloudBalancingEasyScoreCalculator.java
====
[source,java,options="nowrap"]
----
public class CloudBalancingEasyScoreCalculator
    implements EasyScoreCalculator<CloudBalance, HardSoftScore> {

    /**
     * A very simple implementation. The double loop can easily be removed by using Maps as shown in
     * {@link CloudBalancingMapBasedEasyScoreCalculator#calculateScore(CloudBalance)}.
     */
    @Override
    public HardSoftScore calculateScore(CloudBalance cloudBalance) {
        int hardScore = 0;
        int softScore = 0;
        for (CloudComputer computer : cloudBalance.getComputerList()) {
            int cpuPowerUsage = 0;
            int memoryUsage = 0;
            int networkBandwidthUsage = 0;
            boolean used = false;

            // Calculate usage
            for (CloudProcess process : cloudBalance.getProcessList()) {
                if (computer.equals(process.getComputer())) {
                    cpuPowerUsage += process.getRequiredCpuPower();
                    memoryUsage += process.getRequiredMemory();
                    networkBandwidthUsage += process.getRequiredNetworkBandwidth();
                    used = true;
                }
            }

            // Hard constraints
            int cpuPowerAvailable = computer.getCpuPower() - cpuPowerUsage;
            if (cpuPowerAvailable < 0) {
                hardScore += cpuPowerAvailable;
            }
            int memoryAvailable = computer.getMemory() - memoryUsage;
            if (memoryAvailable < 0) {
                hardScore += memoryAvailable;
            }
            int networkBandwidthAvailable = computer.getNetworkBandwidth() - networkBandwidthUsage;
            if (networkBandwidthAvailable < 0) {
                hardScore += networkBandwidthAvailable;
            }

            // Soft constraints
            if (used) {
                softScore -= computer.getCost();
            }
        }
        return HardSoftScore.of(hardScore, softScore);
    }

}
----
====

Even if we optimize the code above to use ``Map``s to iterate through the `processList` only once, _it is still slow_ because it does not do <<incrementalScoreCalculation,incremental score calculation>>.
To fix that, either use constraint streams, incremental Java score calculation or Drools score calculation.


[[cloudBalancingDroolsScoreConfiguration]]
== Drools score configuration

Drools score calculation uses incremental calculation.
Each score constraint is written as one or more score rules.

*Prerequisite*
To use the Drools rule engine as a score function, simply add a `scoreDrl` resource in the classpath:

[source,xml,options="nowrap"]
----
  <scoreDirectorFactory>
    <scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingConstraints.drl</scoreDrl>
  </scoreDirectorFactory>
----

. We want to make sure that all computers have enough CPU, RAM and network bandwidth to support all their processes, so we make these hard constraints:
+
.cloudBalancingConstraints.drl - Hard Constraints
====
[source,options="nowrap"]
----
...

import org.optaplanner.examples.cloudbalancing.domain.CloudBalance;
import org.optaplanner.examples.cloudbalancing.domain.CloudComputer;
import org.optaplanner.examples.cloudbalancing.domain.CloudProcess;

global HardSoftScoreHolder scoreHolder;

// ############################################################################
// Hard constraints
// ############################################################################

rule "requiredCpuPowerTotal"
    when
        $computer : CloudComputer($cpuPower : cpuPower)
        accumulate(
            CloudProcess(
                computer == $computer,
                $requiredCpuPower : requiredCpuPower);
            $requiredCpuPowerTotal : sum($requiredCpuPower);
            $requiredCpuPowerTotal > $cpuPower
        )
    then
        scoreHolder.addHardConstraintMatch(kcontext, $cpuPower - $requiredCpuPowerTotal);
end

rule "requiredMemoryTotal"
    ...
end

rule "requiredNetworkBandwidthTotal"
    ...
end
----
====

. If those constraints are met, we want to minimize the maintenance cost, so we add that as a soft constraint:
+
.cloudBalancingConstraints.drl - Soft Constraints
====
[source,options="nowrap"]
----
// ############################################################################
// Soft constraints
// ############################################################################

rule "computerCost"
    when
        $computer : CloudComputer($cost : cost)
        exists CloudProcess(computer == $computer)
    then
        scoreHolder.addSoftConstraintMatch(kcontext, - $cost);
end
----
====
