Beispiel #1
0
def complete_column(carry_out: int, Carry_Out_Dig: PyValue, sum_dig: int,
                    Sum_Dig: PyValue, digits_in: List[int], Leading_Digits):
    """
  If Sum_Dig (the variable representing the digit in the sum for this column) is not yet instantiated,
  instantiate it to sum_dig  (if that digit is available). If Sum_Dig is already instantiated, ensure
  it is consistent with the sum_dig. Instantiate Carry_Out_Dig to carry_out.
  """
    # Is Sum_Dig uninstantiated? If so, instantiate it to sum_digit if possible.
    # Then instantiate Carry_Out_Dig, and return (yield) digits_in with sum_digit removed.
    if not Sum_Dig.is_instantiated():
        if sum_dig not in digits_in:
            # sum_dig is not available in digits_in. Fail, i.e., return instead of yield.
            return

        # sum_dig is available in digits_in. Give it to Sum_Dig as long as this does not give
        # 0 to one of the leading digits.
        if not (sum_dig == 0 and Sum_Dig in Leading_Digits):
            for _ in unify_pairs([(Carry_Out_Dig, carry_out),
                                  (Sum_Dig, sum_dig)]):
                # Remove sum_digit from digits_in
                i = digits_in.index(sum_dig)
                yield digits_in[:i] + digits_in[i + 1:]

    # If Sum_Dig is instantiated, is it equal to sum_digit?
    # If so, instantiate Carry_Out_Dig and return the current digits_in.
    elif sum_dig == Sum_Dig.get_py_value():
        for _ in unify(Carry_Out_Dig, carry_out):
            yield digits_in
Beispiel #2
0
    def test_cryptarithmetic(self):
        import cryptarithmetic
        t1, t2, s, _Z, Blank = 'SEND', 'MORE', 'MONEY', PyValue(0), PyValue(' ')
        (Carries, T1, T2, Sum, Leading_Digits) = cryptarithmetic.set_up_puzzle(t1, t2, s, _Z)
        next(cryptarithmetic.solve(Carries, T1, T2, Sum, Leading_Digits))
        (t1_out, t2_out, tot_out) = (cryptarithmetic.solution_to_string(T, _Z, Blank) for T in [T1[1:], T2[1:], Sum[1:]])
        assert (t1_out, t2_out, tot_out) == (' 9567', ' 1085', '10652')

        
Beispiel #3
0
def solution_to_string(
    PV_Digits: List[PyValue], _Z=PyValue('a'), Blank=PyValue('b')) -> str:
    """
  Convert a list of PyValue digits to a single number represented as a string.
  Replace _Z with Blank.
  """
    PyDigits = PyValue.get_py_values([(Blank if PV is _Z else PV)
                                      for PV in PV_Digits])
    return ''.join(map(str, PyDigits))
Beispiel #4
0
def set_up_puzzle(t1: str, t2: str, sum: str, _Z: PyValue) -> \
                  Optional[Tuple[List[PyValue], List[PyValue], List[PyValue], List[PyValue], List[PyValue]]]:
    """
  Convert the initial string representation to (uninstantiated) PyValues.
  t1 and t2 are the numbers to be added. sum is the sum.
  _Z is PyValue(0). It will be replaced by leading blanks.
  """
    Var_Letters = sorted(list(set(t1 + t2 + sum)))
    if len(Var_Letters) > 10:
        print(f'Too many variables: {Var_Letters}')
        return
    Vars_Dict = {V: PyValue() for V in Var_Letters}
    length = len(sum) + 1
    T1 = (length - len(t1)) * [_Z] + letters_to_vars(t1, Vars_Dict)
    T2 = (length - len(t2)) * [_Z] + letters_to_vars(t2, Vars_Dict)
    Sum = (length - len(sum)) * [_Z] + letters_to_vars(sum, Vars_Dict)
    # Leading_Digits are the variables that should not be assigned 0.
    Leading_Digits = letters_to_vars({t1[0], t2[0], sum[0]}, Vars_Dict)
    Carries = [PyValue() for _ in range(length - 1)] + [PyValue(0)]
    return (Carries, T1, T2, Sum, Leading_Digits)
Beispiel #5
0
def connected(S1: PyValue, Line_Dist: PyValue, S2: PyValue):
    # S1: Union[PyValue, Var], Line_Dist: Var, S2: Union[PyValue, Var
    """
  Are stations S1 and S2 connected on the same train line?
  If so, which line is it, and how many stations are between them?
  Line_Dist will be unified with (line, count_of_stations)
  """
    # print(f'-> connected({S1}, {Line_Dist}, {S2})?')
    Line = PyValue()
    # Can use either forall or nested for _ in has_station's
    # for _ in forall([lambda: has_station(Line, S1),
    #                  lambda: has_station(Line, S2)]):
    for _ in has_station(Line, S1):
        for _ in has_station(Line, S2):
            # Ensure that S1 != S2
            if S1 != S2:
                line = Line.get_py_value()
                stations = lines[line]
                pos1 = stations.index(S1.get_py_value())
                pos2 = stations.index(S2.get_py_value())
                yield from unify(Line_Dist, (line, abs(pos1 - pos2)))
Beispiel #6
0
def solve_crypto(t1: str, t2: str, sum: str):
    _Z = PyValue(0)
    (Carries, T1, T2, Sum, Leading_Digits) = set_up_puzzle(t1, t2, sum, _Z)
    want_more = None
    Blank = PyValue(' ')
    for _ in solve(Carries, T1, T2, Sum, Leading_Digits):
        # We have a solution.
        # Replace the leading _Z zeros with blanks and convert each number to a string.
        # We can discard T1[0], T2[0], and Sum[0] because we know they will be 0.
        (t1_out, t2_out, tot_out) = (solution_to_string(T, _Z, Blank)
                                     for T in [T1[1:], T2[1:], Sum[1:]])
        print()
        print(f'  {t1}  -> {t1_out}')
        print(f'+ {t2}  -> {t2_out}')
        print(f'{"-" * (len(sum)+1)}     {"-" * len(sum)}')
        print(f' {sum}  -> {tot_out}')
        ans = input('\nLook for more solutions? (y/n) > ').lower()
        want_more = ans[0] if len(ans) > 0 else 'n'
        if want_more != 'y':
            break
    if want_more == 'y':
        print('No more solutions.')
Beispiel #7
0
def best_route(Start: PyValue, End: PyValue):
    """
  A Route will be: [Station, (Line, Dist), Station, (Line, Dist), ..., Station].
  Ignoring Dist, this will be the sequence of stations and lines to take from Start to End.
  The Dist components are the number of intermediate stations on the associated line.

  The best route uses the fewest lines, and of routes using the same number of lines, the fewest total Dist values.
  """
    # Look for routes that use the fewest lines.
    for i in range(1, len(lines) + 1):
        # i is the number of lines used.
        # The middle sequence is the intermediate lines and stations.
        # One line involves one intermediate element, the line used.
        # Two lines involves 3 intermediate elements, the two lines used and the transfer station.
        # Etc.
        # The "+1" above allows i to take on the value len(lines), in which case we are using all the lines.
        intermediate = (PyValue() for _ in range(2 * i - 1))
        legs = [Start, *intermediate, End]
        # If route(*legs) succeeds, route will instantiate legs to
        #         [Station, (Line, int), Station, (Line, int), ... , Station]
        # Once legs is instantiated, must extract the py_values so that the collection
        # of routes remains instantiated after the list comprehension terminates.
        route_options = [[elt.get_py_value() for elt in legs]
                         for _ in route(*legs)]
        # Once we find an i so that there is at least one route from Start to End using i lines,
        # find the best of them and quit--by using return at the bottom.
        # No point in looking for routes that use more lines.
        if route_options:
            routes_with_totals: Iterator[Tuple[List[str], int]] = map(
                sum_distances, route_options)
            best_option: Tuple[List[str],
                               int] = min(routes_with_totals,
                                          key=lambda routeDist: routeDist[1])
            yield best_option
            # return prevents backtracking, i.e., looking for longer routes.
            # Has an effect similar to a cut (!) at the end of a clause in Prolog.
            # break would work as well since it jumps out of the for-loop and returns.
            return
Beispiel #8
0
def place_n_queens(board_width: int):
    """
  Generate and display all solutions to the n-queens problem.

  A Placement is a list of n PyValue integers. The rth integer indicates where the queen is to be
  placed in the rth row. I.e., Placement[r] is the position of the queen in row r.

  """
    start = timer()
    # Create the entire list of PyValue variables.
    placement = [PyValue() for _ in range(board_width)]
    solutionNbr = 0
    # place_remaining_queens will instantiate the PyValue variables one by one.
    for _ in place_remaining_queens(placement):
        solutionNbr += 1
        solution_display = layout([c.get_py_value() for c in placement],
                                  board_width)
        print(f'\n{solutionNbr}.\n{solution_display}')
        end = timer()
        print(f'time: {round(end-start, 3)}')
        inp = input('\nMore? (y, or n)? > ').lower()
        if inp != 'y':
            break
        start = timer()
Beispiel #9
0
            f'intermediate station{add_plural_s(stations_passed - 1)}{transfer_stns}.'
        )

    for (s1, s2) in [
        ("Takatsuki", "Yamashina"),  # Direct
        ("Takatsuki", "Kyoto"),  # Direct
        ("Yamashina", "Sakamoto"),  # Direct
        ("Yamashina", "Ishiyama"),  # Direct
        ("Otsukyo", "Sakamoto"),  # Direct
        ("Otsukyo", "Hamaotsu"),  # One-change
        ("Otsukyo", "Ano"),  # One-change
        ("Takatsuki", "Otsukyo"),  # One-change
        ("Yamashina", "Ano"),  # One-change
        ("Takatsuki", "Ano"),  # Two-changes
        ("Hamaotsu", "Otsu"),  # Two-changes
        ("Zeze", "Takatsuki"),  # Two-changes
        ("Zeze", "Kusatsu"),  # Two-changes
    ]:

        (S1, S2) = (PyValue(s1), PyValue(s2))
        # Use Route in Prolog style to pass back the route.
        # In this case it's simply a basket in which the best route is conveyed.
        # Route = Var( )
        for Route in best_route(S1, S2):
            print(f'\nA route from {S1} to {S2} that uses fewest lines, ',
                  end='')
            print(
                f'and of those the one that passes the fewest intermediate stations:'
            )
            print_route(*Route)
 def clue_2(self, Students: SuperSequence):
     """ 2. Amy studies either Philosophy or English. """
     # Create Major as a local logic variable.
     Major = PyValue()
     for _ in member(Student(name='Amy', major=Major), Students):
         yield from member(Major, self.ListType(['Philosophy', 'English']))