In [1]:
from functools import partial, reduce
from itertools import takewhile
import operator as op

from pyknow import *


class Number(Fact):
    pass


class Letter(Fact):
    pass


class Combination(Fact):
    pass

until_none = partial(takewhile, lambda x: x is not None)


def check_combination(d=None, t=None, l=None, r=None, a=None,
                      e=None, n=None, b=None, o=None, g=None):
    """Check a partial solution."""

    left_1 = list(until_none(reversed([g, e, r, a, l, d])))
    left_2 = list(until_none(reversed([d, o, n, a, l, d])))
    right = list(until_none(reversed([r, o, b, e, r, t])))

    a = sum((l[0] * 10 ** e) + (l[1] * 10 ** e)
            for e, l in enumerate(zip(left_1, left_2)))
    b = sum((l * 10 ** e) for e, l in enumerate(right))

    if len(left_1) < 6:
        a = a % 10**len(right)

    return a == b


def C(name, neq=""):
    """
    Ex: name="c", neq="tar" => W("c") & ~W("t") & ~W("a") & ~W("r")

    """
    if neq:
        return W(name) & reduce(op.and_, [~W(c) for c in neq])
    else:
        return W(name)


class WordGame(KnowledgeEngine):
    @Rule()
    def startup(self):
        print("The problem is")
        print("    GERALD")
        print("  + DONALD")
        print("    ------")
        print("  = ROBERT")
        print()

        for n in range(10):
            self.declare(Number(n))

        for l in set("GERALD" + "DONALD" + "ROBERT"):
            self.declare(Letter(l))

    @Rule(Number(MATCH.n),
          Letter(MATCH.l))
    def generate_combinations(self, n, l):
        self.declare(Combination(l, n))

    @Rule(Combination("D", C("d")),
          Combination("T", C("t", neq="d")),
          TEST(check_combination),
          Combination("L", C("l", neq="dt")),
          Combination("R", C("r", neq="dtl")),
          TEST(check_combination),
          Combination("A", C("a", neq="dtlr")),
          Combination("E", C("e", neq="dtlra")),
          TEST(check_combination),
          Combination("N", C("n", neq="dtlrae")),
          Combination("B", C("b", neq="dtlraen")),
          TEST(check_combination),
          Combination("O", C("o", neq="dtlraenb")),
          Combination("G", C("g", neq="dtlraenbo")),
          TEST(check_combination))
    def find_solution(self, g, e, r, a, l, d, o, n, b, t):
        print("A solution is:")
        print("  G =", g)
        print("  E =", e)
        print("  R =", r)
        print("  A =", a)
        print("  L =", l)
        print("  D =", d)
        print("  O =", o)
        print("  N =", n)
        print("  B =", b)
        print("  T =", t)
        print()
        print("    ", g, e, r, a, l, d)
        print("  + ", d, o, n, a, l, d)
        print("    ", "------")
        print("  = ", r, o, b, e, r, t)
        print()

In [2]:
wg = WordGame()
wg.reset()
wg.run()


The problem is
    GERALD
  + DONALD
    ------
  = ROBERT

A solution is:
  G = 1
  E = 9
  R = 7
  A = 4
  L = 8
  D = 5
  O = 2
  N = 6
  B = 3
  T = 0

     1 9 7 4 8 5
  +  5 2 6 4 8 5
     ------
  =  7 2 3 9 7 0