## lib/examples/reindeers.ex

``````defmodule CPSolver.Examples.Reindeers do
@doc """
Santa always leaves plans for his elves to determine the order in which the
reindeer will pull his sleigh. This year, for the European leg of his
journey, his elves are working to the following schedule, which will form a
single line of nine reindeer.

Here are the rules:

Comet behind Rudolph, Prancer and Cupid
Blitzen behind Cupid
Blitzen in front of Donder, Vixen and Dancer
Cupid in front of Comet, Blitzen and Vixen
Donder behind Vixen, Dasher and Prancer
Rudolph behind Prancer
Rudolph in front of Donder, Dancer and Dasher
Vixen in front of Dancer and Comet
Dancer behind Donder, Rudolph and Blitzen
Prancer in front of Cupid, Donder and Blitzen
Dasher behind Prancer
Dasher in front of Vixen, Dancer and Blitzen
Donder behind Comet and Cupid
Cupid in front of Rudolph and Dancer
Vixen behind Rudolph, Prancer and Dasher.

"""
alias CPSolver.IntVariable, as: Variable
alias CPSolver.Constraint.Less
alias CPSolver.Constraint.AllDifferent

def solve(opts \\ []) do
{:ok, res} = CPSolver.solve_sync(model(), opts)

res.solutions
|> hd
|> then(fn solution -> Enum.zip(res.variables, solution) end)
|> print()

{:ok, res}
end

def model() do
reindeers = [
Blitzen,
Comet,
Cupid,
Dancer,
Dasher,
Donder,
Prancer,
Rudolph,
Vixen
]

domain = 1..length(reindeers)

positions =
[blitzen, comet, cupid, dancer, dasher, donder, prancer, rudolph, vixen] =
Enum.map(reindeers, fn name -> Variable.new(domain, name: name) end)

rules =
behind(comet, [rudolph, prancer, cupid]) ++
behind(blitzen, [cupid]) ++
in_front_of(blitzen, [donder, vixen, dancer]) ++
in_front_of(cupid, [comet, blitzen, vixen]) ++
behind(donder, [vixen, dasher, prancer]) ++
behind(rudolph, [prancer]) ++
in_front_of(rudolph, [donder, dancer, dasher]) ++
in_front_of(vixen, [dancer, comet]) ++
behind(dancer, [donder, rudolph, blitzen]) ++
in_front_of(prancer, [cupid, donder, blitzen]) ++
behind(dasher, [prancer]) ++
in_front_of(dasher, [vixen, dancer, blitzen]) ++
behind(donder, [comet, cupid]) ++
in_front_of(cupid, [rudolph, dancer]) ++
behind(vixen, [rudolph, prancer, dasher])

%{
variables: positions,
## AllDifferent is optional
constraints: [AllDifferent.new(positions) | rules]
}
end

defp behind(reindeer, list) do
Enum.map(list, fn r -> Less.new(reindeer, r) end)
end

defp in_front_of(reindeer, list) do
Enum.map(list, fn r -> Less.new(r, reindeer) end)
end

def order(solution) do
solution
|> Enum.sort_by(fn {_r, place} -> place end)
|> Enum.map(fn {r, _place} -> r end)
end

def print(solution) do
solution
|> order
|> Enum.map_join(" ", fn name -> inspect(name) end)
|> then(fn str -> IO.ANSI.green() <> " -> #{str} ->" <> IO.ANSI.reset() end)
|> IO.puts()
end
end
``````