lib/examples/send_more_money.ex

defmodule CPSolver.Examples.SendMoreMoney do
  @moduledoc """
  The classic "cryptarithmetic" (https://en.wikipedia.org/wiki/Verbal_arithmetic) problem.
  Solve the following (each letter is a separate digit):

  SEND + MORE = MONEY

  """

  alias CPSolver.IntVariable, as: Variable
  alias CPSolver.Model
  alias CPSolver.Constraint.Sum
  alias CPSolver.Constraint.AllDifferent.FWC, as: AllDifferent
  import CPSolver.Variable.View.Factory

  def model() do
    letters = [S, E, N, D, M, O, R, Y]

    variables =
      [s, e, n, d, m, o, r, y] =
      Enum.map(letters, fn letter ->
        d = (letter in [S, M] && 1..9) || 0..9
        Variable.new(d, name: letter)
      end)

    sum_constraint =
      Sum.new(y, [
        d,
        mul(n, -90),
        mul(e, 91),
        mul(s, 1000),
        mul(r, 10),
        mul(o, -900),
        mul(m, -9_000)
      ])

    Model.new(variables, [sum_constraint, AllDifferent.new(variables)])
  end

  def solve() do
    {:ok, res} = CPSolver.solve(model(), stop_on: {:max_solutions, 1})
    Enum.zip(res.variables, hd(res.solutions))
  end

  def check_solution([s, e, n, d, m, o, r, y] = _solution) do
    1000 * s + 100 * e + 10 * n + d +
      1000 * m + 100 * o + 10 * r + e ==
      10_000 * m + 1000 * o + 100 * n + 10 * e + y
  end
end