Advertisement
tt-77

[ВИ] Кутии (неинформирано)

May 24th, 2019
482
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 35.00 KB | None | 0 0
  1. import sys
  2. import math
  3. import random
  4. import bisect
  5. from sys import maxsize as infinity
  6.  
  7. """
  8. Дефинирање на класа за структурата на проблемот кој ќе го решаваме со пребарување.
  9. Класата Problem е апстрактна класа од која правиме наследување за дефинирање на основните
  10. карактеристики на секој проблем што сакаме да го решиме
  11. """
  12.  
  13.  
  14. class Problem:
  15.     def __init__(self, initial, goal=None):
  16.         self.initial = initial
  17.         self.goal = goal
  18.  
  19.     def successor(self, state):
  20.         """За дадена состојба, врати речник од парови {акција : состојба}
  21.        достапни од оваа состојба. Ако има многу следбеници, употребете
  22.        итератор кој би ги генерирал следбениците еден по еден, наместо да
  23.        ги генерирате сите одеднаш.
  24.  
  25.        :param state: дадена состојба
  26.        :return:  речник од парови {акција : состојба} достапни од оваа
  27.                  состојба
  28.        :rtype: dict
  29.        """
  30.         raise NotImplementedError
  31.  
  32.     def actions(self, state):
  33.         """За дадена состојба state, врати листа од сите акции што може да
  34.        се применат над таа состојба
  35.  
  36.        :param state: дадена состојба
  37.        :return: листа на акции
  38.        :rtype: list
  39.        """
  40.         raise NotImplementedError
  41.  
  42.     def result(self, state, action):
  43.         """За дадена состојба state и акција action, врати ја состојбата
  44.        што се добива со примена на акцијата над состојбата
  45.  
  46.        :param state: дадена состојба
  47.        :param action: дадена акција
  48.        :return: резултантна состојба
  49.        """
  50.         raise NotImplementedError
  51.  
  52.     def goal_test(self, state):
  53.         """Врати True ако state е целна состојба. Даденава имплементација
  54.        на методот директно ја споредува state со self.goal, како што е
  55.        специфицирана во конструкторот. Имплементирајте го овој метод ако
  56.        проверката со една целна состојба self.goal не е доволна.
  57.  
  58.        :param state: дадена состојба
  59.        :return: дали дадената состојба е целна состојба
  60.        :rtype: bool
  61.        """
  62.         return state == self.goal
  63.  
  64.     def path_cost(self, c, state1, action, state2):
  65.         """Врати ја цената на решавачкиот пат кој пристигнува во состојбата
  66.        state2 од состојбата state1 преку акцијата action, претпоставувајќи
  67.        дека цената на патот до состојбата state1 е c. Ако проблемот е таков
  68.        што патот не е важен, оваа функција ќе ја разгледува само состојбата
  69.        state2. Ако патот е важен, ќе ја разгледува цената c и можеби и
  70.        state1 и action. Даденава имплементација му доделува цена 1 на секој
  71.        чекор од патот.
  72.  
  73.        :param c: цена на патот до состојбата state1
  74.        :param state1: дадена моментална состојба
  75.        :param action: акција која треба да се изврши
  76.        :param state2: состојба во која треба да се стигне
  77.        :return: цена на патот по извршување на акцијата
  78.        :rtype: float
  79.        """
  80.         return c + 1
  81.  
  82.     def value(self):
  83.         """За проблеми на оптимизација, секоја состојба си има вредност.
  84.        Hill-climbing и сличните алгоритми се обидуваат да ја максимизираат
  85.        оваа вредност.
  86.  
  87.        :return: вредност на состојба
  88.        :rtype: float
  89.        """
  90.         raise NotImplementedError
  91.  
  92.  
  93. """
  94. Дефинирање на класата за структурата на јазел од пребарување.
  95. Класата Node не се наследува
  96. """
  97.  
  98.  
  99. class Node:
  100.     def __init__(self, state, parent=None, action=None, path_cost=0):
  101.         """Креирај јазол од пребарувачкото дрво, добиен од parent со примена
  102.        на акцијата action
  103.  
  104.        :param state: моментална состојба (current state)
  105.        :param parent: родителска состојба (parent state)
  106.        :param action: акција (action)
  107.        :param path_cost: цена на патот (path cost)
  108.        """
  109.         self.state = state
  110.         self.parent = parent
  111.         self.action = action
  112.         self.path_cost = path_cost
  113.         self.depth = 0  # search depth
  114.         if parent:
  115.             self.depth = parent.depth + 1
  116.  
  117.     def __repr__(self):
  118.         return "<Node %s>" % (self.state,)
  119.  
  120.     def __lt__(self, node):
  121.         return self.state < node.state
  122.  
  123.     def expand(self, problem):
  124.         """Излистај ги јазлите достапни во еден чекор од овој јазол.
  125.  
  126.        :param problem: даден проблем
  127.        :return: листа на достапни јазли во еден чекор
  128.        :rtype: list(Node)
  129.        """
  130.  
  131.         return [self.child_node(problem, action)
  132.                 for action in problem.actions(self.state)]
  133.  
  134.     def child_node(self, problem, action):
  135.         """Дете јазел
  136.  
  137.        :param problem: даден проблем
  138.        :param action: дадена акција
  139.        :return: достапен јазел според дадената акција
  140.        :rtype: Node
  141.        """
  142.         next_state = problem.result(self.state, action)
  143.         return Node(next_state, self, action,
  144.                     problem.path_cost(self.path_cost, self.state,
  145.                                       action, next_state))
  146.  
  147.     def solution(self):
  148.         """Врати ја секвенцата од акции за да се стигне од коренот до овој јазол.
  149.  
  150.        :return: секвенцата од акции
  151.        :rtype: list
  152.        """
  153.         return [node.action for node in self.path()[1:]]
  154.  
  155.     def solve(self):
  156.         """Врати ја секвенцата од состојби за да се стигне од коренот до овој јазол.
  157.  
  158.        :return: листа од состојби
  159.        :rtype: list
  160.        """
  161.         return [node.state for node in self.path()[0:]]
  162.  
  163.     def path(self):
  164.         """Врати ја листата од јазли што го формираат патот од коренот до овој јазол.
  165.  
  166.        :return: листа од јазли од патот
  167.        :rtype: list(Node)
  168.        """
  169.         x, result = self, []
  170.         while x:
  171.             result.append(x)
  172.             x = x.parent
  173.         result.reverse()
  174.         return result
  175.  
  176.     """Сакаме редицата од јазли кај breadth_first_search или
  177.    astar_search да не содржи состојби - дупликати, па јазлите што
  178.    содржат иста состојба ги третираме како исти. [Проблем: ова може
  179.    да не биде пожелно во други ситуации.]"""
  180.  
  181.     def __eq__(self, other):
  182.         return isinstance(other, Node) and self.state == other.state
  183.  
  184.     def __hash__(self):
  185.         return hash(self.state)
  186.  
  187.  
  188. """
  189. Дефинирање на помошни структури за чување на листата на генерирани, но непроверени јазли
  190. """
  191.  
  192.  
  193. class Queue:
  194.     """Queue е апстрактна класа / интерфејс. Постојат 3 типа:
  195.        Stack(): Last In First Out Queue (стек).
  196.        FIFOQueue(): First In First Out Queue (редица).
  197.        PriorityQueue(order, f): Queue во сортиран редослед (подразбирливо,од најмалиот кон
  198.                                 најголемиот јазол).
  199.    """
  200.  
  201.     def __init__(self):
  202.         raise NotImplementedError
  203.  
  204.     def append(self, item):
  205.         """Додади го елементот item во редицата
  206.  
  207.        :param item: даден елемент
  208.        :return: None
  209.        """
  210.         raise NotImplementedError
  211.  
  212.     def extend(self, items):
  213.         """Додади ги елементите items во редицата
  214.  
  215.        :param items: дадени елементи
  216.        :return: None
  217.        """
  218.         raise NotImplementedError
  219.  
  220.     def pop(self):
  221.         """Врати го првиот елемент од редицата
  222.  
  223.        :return: прв елемент
  224.        """
  225.         raise NotImplementedError
  226.  
  227.     def __len__(self):
  228.         """Врати го бројот на елементи во редицата
  229.  
  230.        :return: број на елементи во редицата
  231.        :rtype: int
  232.        """
  233.         raise NotImplementedError
  234.  
  235.     def __contains__(self, item):
  236.         """Проверка дали редицата го содржи елементот item
  237.  
  238.        :param item: даден елемент
  239.        :return: дали queue го содржи item
  240.        :rtype: bool
  241.        """
  242.         raise NotImplementedError
  243.  
  244.  
  245. class Stack(Queue):
  246.     """Last-In-First-Out Queue."""
  247.  
  248.     def __init__(self):
  249.         self.data = []
  250.  
  251.     def append(self, item):
  252.         self.data.append(item)
  253.  
  254.     def extend(self, items):
  255.         self.data.extend(items)
  256.  
  257.     def pop(self):
  258.         return self.data.pop()
  259.  
  260.     def __len__(self):
  261.         return len(self.data)
  262.  
  263.     def __contains__(self, item):
  264.         return item in self.data
  265.  
  266.  
  267. class FIFOQueue(Queue):
  268.     """First-In-First-Out Queue."""
  269.  
  270.     def __init__(self):
  271.         self.data = []
  272.  
  273.     def append(self, item):
  274.         self.data.append(item)
  275.  
  276.     def extend(self, items):
  277.         self.data.extend(items)
  278.  
  279.     def pop(self):
  280.         return self.data.pop(0)
  281.  
  282.     def __len__(self):
  283.         return len(self.data)
  284.  
  285.     def __contains__(self, item):
  286.         return item in self.data
  287.  
  288.  
  289. class PriorityQueue(Queue):
  290.     """Редица во која прво се враќа минималниот (или максималниот) елемент
  291.    (како што е определено со f и order). Оваа структура се користи кај
  292.    информирано пребарување"""
  293.     """"""
  294.  
  295.     def __init__(self, order=min, f=lambda x: x):
  296.         """
  297.        :param order: функција за подредување, ако order е min, се враќа елементот
  298.                      со минимална f(x); ако order е max, тогаш се враќа елементот
  299.                      со максимална f(x).
  300.        :param f: функција f(x)
  301.        """
  302.         assert order in [min, max]
  303.         self.data = []
  304.         self.order = order
  305.         self.f = f
  306.  
  307.     def append(self, item):
  308.         bisect.insort_right(self.data, (self.f(item), item))
  309.  
  310.     def extend(self, items):
  311.         for item in items:
  312.             bisect.insort_right(self.data, (self.f(item), item))
  313.  
  314.     def pop(self):
  315.         if self.order == min:
  316.             return self.data.pop(0)[1]
  317.         return self.data.pop()[1]
  318.  
  319.     def __len__(self):
  320.         return len(self.data)
  321.  
  322.     def __contains__(self, item):
  323.         return any(item == pair[1] for pair in self.data)
  324.  
  325.     def __getitem__(self, key):
  326.         for _, item in self.data:
  327.             if item == key:
  328.                 return item
  329.  
  330.     def __delitem__(self, key):
  331.         for i, (value, item) in enumerate(self.data):
  332.             if item == key:
  333.                 self.data.pop(i)
  334.  
  335.  
  336. """
  337. Неинформирано пребарување во рамки на дрво.
  338. Во рамки на дрвото не разрешуваме јамки.
  339. """
  340.  
  341.  
  342. def tree_search(problem, fringe):
  343.     """ Пребарувај низ следбениците на даден проблем за да најдеш цел.
  344.  
  345.    :param problem: даден проблем
  346.    :param fringe:  празна редица (queue)
  347.    :return: Node
  348.    """
  349.     fringe.append(Node(problem.initial))
  350.     while fringe:
  351.         node = fringe.pop()
  352.         print(node.state)
  353.         if problem.goal_test(node.state):
  354.             return node
  355.         fringe.extend(node.expand(problem))
  356.     return None
  357.  
  358.  
  359. def breadth_first_tree_search(problem):
  360.     """Експандирај го прво најплиткиот јазол во пребарувачкото дрво.
  361.  
  362.    :param problem: даден проблем
  363.    :return: Node
  364.    """
  365.     return tree_search(problem, FIFOQueue())
  366.  
  367.  
  368. def depth_first_tree_search(problem):
  369.     """Експандирај го прво најдлабокиот јазол во пребарувачкото дрво.
  370.  
  371.    :param problem:даден проблем
  372.    :return: Node
  373.    """
  374.     return tree_search(problem, Stack())
  375.  
  376.  
  377. """
  378. Неинформирано пребарување во рамки на граф
  379. Основната разлика е во тоа што овде не дозволуваме јамки,
  380. т.е. повторување на состојби
  381. """
  382.  
  383.  
  384. def graph_search(problem, fringe):
  385.     """Пребарувај низ следбениците на даден проблем за да најдеш цел.
  386.     Ако до дадена состојба стигнат два пата, употреби го најдобриот пат.
  387.  
  388.    :param problem: даден проблем
  389.    :param fringe: празна редица (queue)
  390.    :return: Node
  391.    """
  392.     closed = set()
  393.     fringe.append(Node(problem.initial))
  394.     while fringe:
  395.         node = fringe.pop()
  396.         if problem.goal_test(node.state):
  397.             return node
  398.         if node.state not in closed:
  399.             closed.add(node.state)
  400.             fringe.extend(node.expand(problem))
  401.     return None
  402.  
  403.  
  404. def breadth_first_graph_search(problem):
  405.     """Експандирај го прво најплиткиот јазол во пребарувачкиот граф.
  406.  
  407.    :param problem: даден проблем
  408.    :return: Node
  409.    """
  410.     return graph_search(problem, FIFOQueue())
  411.  
  412.  
  413. def depth_first_graph_search(problem):
  414.     """Експандирај го прво најдлабокиот јазол во пребарувачкиот граф.
  415.  
  416.    :param problem: даден проблем
  417.    :return: Node
  418.    """
  419.     return graph_search(problem, Stack())
  420.  
  421.  
  422. def depth_limited_search(problem, limit=50):
  423.     def recursive_dls(node, problem, limit):
  424.         """Помошна функција за depth limited"""
  425.         cutoff_occurred = False
  426.         if problem.goal_test(node.state):
  427.             return node
  428.         elif node.depth == limit:
  429.             return 'cutoff'
  430.         else:
  431.             for successor in node.expand(problem):
  432.                 result = recursive_dls(successor, problem, limit)
  433.                 if result == 'cutoff':
  434.                     cutoff_occurred = True
  435.                 elif result is not None:
  436.                     return result
  437.         if cutoff_occurred:
  438.             return 'cutoff'
  439.         return None
  440.  
  441.     return recursive_dls(Node(problem.initial), problem, limit)
  442.  
  443.  
  444. def iterative_deepening_search(problem):
  445.     for depth in range(sys.maxsize):
  446.         result = depth_limited_search(problem, depth)
  447.         if result is not 'cutoff':
  448.             return result
  449.  
  450.  
  451. def uniform_cost_search(problem):
  452.     """Експандирај го прво јазолот со најниска цена во пребарувачкиот граф."""
  453.     return graph_search(problem, PriorityQueue(min, lambda a: a.path_cost))
  454.  
  455.  
  456. """
  457. Информирано пребарување во рамки на граф
  458. """
  459.  
  460.  
  461. def memoize(fn, slot=None):
  462.     """ Запамети ја пресметаната вредност за која била листа од
  463.    аргументи. Ако е специфициран slot, зачувај го резултатот во
  464.    тој slot на првиот аргумент. Ако slot е None, зачувај ги
  465.    резултатите во речник.
  466.  
  467.    :param fn: зададена функција
  468.    :param slot: име на атрибут во кој се чуваат резултатите од функцијата
  469.    :return: функција со модификација за зачувување на резултатите
  470.    """
  471.     if slot:
  472.         def memoized_fn(obj, *args):
  473.             if hasattr(obj, slot):
  474.                 return getattr(obj, slot)
  475.             else:
  476.                 val = fn(obj, *args)
  477.                 setattr(obj, slot, val)
  478.                 return val
  479.     else:
  480.         def memoized_fn(*args):
  481.             if args not in memoized_fn.cache:
  482.                 memoized_fn.cache[args] = fn(*args)
  483.             return memoized_fn.cache[args]
  484.  
  485.         memoized_fn.cache = {}
  486.     return memoized_fn
  487.  
  488.  
  489. def best_first_graph_search(problem, f):
  490.     """Пребарувај низ следбениците на даден проблем за да најдеш цел. Користи
  491.     функција за евалуација за да се одлучи кој е сосед најмногу ветува и
  492.     потоа да се истражи. Ако до дадена состојба стигнат два пата, употреби
  493.     го најдобриот пат.
  494.  
  495.    :param problem: даден проблем
  496.    :param f: дадена функција за евристика
  497.    :return: Node or None
  498.    """
  499.     f = memoize(f, 'f')
  500.     node = Node(problem.initial)
  501.     if problem.goal_test(node.state):
  502.         return node
  503.     frontier = PriorityQueue(min, f)
  504.     frontier.append(node)
  505.     explored = set()
  506.     while frontier:
  507.         node = frontier.pop()
  508.         if problem.goal_test(node.state):
  509.             return node
  510.         explored.add(node.state)
  511.         for child in node.expand(problem):
  512.             if child.state not in explored and child not in frontier:
  513.                 frontier.append(child)
  514.             elif child in frontier:
  515.                 incumbent = frontier[child]
  516.                 if f(child) < f(incumbent):
  517.                     del frontier[incumbent]
  518.                     frontier.append(child)
  519.     return None
  520.  
  521.  
  522. def greedy_best_first_graph_search(problem, h=None):
  523.     """ Greedy best-first пребарување се остварува ако се специфицира дека f(n) = h(n).
  524.  
  525.    :param problem: даден проблем
  526.    :param h: дадена функција за евристика
  527.    :return: Node or None
  528.    """
  529.     h = memoize(h or problem.h, 'h')
  530.     return best_first_graph_search(problem, h)
  531.  
  532.  
  533. def astar_search(problem, h=None):
  534.     """ A* пребарување е best-first graph пребарување каде f(n) = g(n) + h(n).
  535.  
  536.    :param problem: даден проблем
  537.    :param h: дадена функција за евристика
  538.    :return: Node or None
  539.    """
  540.     h = memoize(h or problem.h, 'h')
  541.     return best_first_graph_search(problem, lambda n: n.path_cost + h(n))
  542.  
  543.  
  544. def recursive_best_first_search(problem, h=None):
  545.     """Recursive best first search - ја ограничува рекурзијата
  546.    преку следење на f-вредноста на најдобриот алтернативен пат
  547.    од било кој јазел предок (еден чекор гледање нанапред).
  548.  
  549.    :param problem: даден проблем
  550.    :param h: дадена функција за евристика
  551.    :return: Node or None
  552.    """
  553.     h = memoize(h or problem.h, 'h')
  554.  
  555.     def RBFS(problem, node, flimit):
  556.         if problem.goal_test(node.state):
  557.             return node, 0  # (втората вредност е неважна)
  558.         successors = node.expand(problem)
  559.         if len(successors) == 0:
  560.             return None, infinity
  561.         for s in successors:
  562.             s.f = max(s.path_cost + h(s), node.f)
  563.         while True:
  564.             # Подреди ги според најниската f вредност
  565.             successors.sort(key=lambda x: x.f)
  566.             best = successors[0]
  567.             if best.f > flimit:
  568.                 return None, best.f
  569.             if len(successors) > 1:
  570.                 alternative = successors[1].f
  571.             else:
  572.                 alternative = infinity
  573.             result, best.f = RBFS(problem, best, min(flimit, alternative))
  574.             if result is not None:
  575.                 return result, best.f
  576.  
  577.     node = Node(problem.initial)
  578.     node.f = h(node)
  579.     result, bestf = RBFS(problem, node, infinity)
  580.     return result
  581.  
  582.  
  583. """
  584. Пребарување низ проблем дефиниран како конечен граф
  585. """
  586.  
  587.  
  588. def distance(a, b):
  589.     """Растојание помеѓу две (x, y) точки."""
  590.     return math.hypot((a[0] - b[0]), (a[1] - b[1]))
  591.  
  592.  
  593. class Graph:
  594.     def __init__(self, dictionary=None, directed=True):
  595.         self.dict = dictionary or {}
  596.         self.directed = directed
  597.         if not directed:
  598.             self.make_undirected()
  599.         else:
  600.             # додади празен речник за линковите на оние јазли кои немаат
  601.             # насочени врски и не се дадени како клучеви во речникот
  602.             nodes_no_edges = list({y for x in self.dict.values()
  603.                                    for y in x if y not in self.dict})
  604.             for node in nodes_no_edges:
  605.                 self.dict[node] = {}
  606.  
  607.     def make_undirected(self):
  608.         """Ориентираниот граф претвори го во неориентиран со додавање
  609.        на симетричните ребра."""
  610.         for a in list(self.dict.keys()):
  611.             for (b, dist) in self.dict[a].items():
  612.                 self.connect1(b, a, dist)
  613.  
  614.     def connect(self, node_a, node_b, distance_val=1):
  615.         """Додади ребро од A до B со дадено растојание, а додади го и
  616.        обратното ребро (од B до A) ако графот е неориентиран."""
  617.         self.connect1(node_a, node_b, distance_val)
  618.         if not self.directed:
  619.             self.connect1(node_b, node_a, distance_val)
  620.  
  621.     def connect1(self, node_a, node_b, distance_val):
  622.         """Додади ребро од A до B со дадено растојание, но само во
  623.        едната насока."""
  624.         self.dict.setdefault(node_a, {})[node_b] = distance_val
  625.  
  626.     def get(self, a, b=None):
  627.         """Врати растојание придружено на ребро или пак врати речник
  628.        чии елементи се од обликот {јазол : растојание}.
  629.        .get(a,b) го враќа растојанието или пак враќа None
  630.        .get(a) враќа речник со елементи од обликот {јазол : растојание},
  631.            кој може да биде и празен – {}."""
  632.         links = self.dict.get(a)
  633.         if b is None:
  634.             return links
  635.         else:
  636.             return links.get(b)
  637.  
  638.     def nodes(self):
  639.         """Врати листа од јазлите во графот."""
  640.         return list(self.dict.keys())
  641.  
  642.  
  643. def UndirectedGraph(dictionary=None):
  644.     """Изгради Graph во кој што секое ребро (вклучувајќи ги и идните
  645.    ребра) е двонасочно."""
  646.     return Graph(dictionary=dictionary, directed=False)
  647.  
  648.  
  649. def RandomGraph(nodes=list(range(10)), min_links=2, width=400, height=300,
  650.                 curvature=lambda: random.uniform(1.1, 1.5)):
  651.     """Construct a random graph, with the specified nodes, and random links.
  652.    The nodes are laid out randomly on a (width x height) rectangle.
  653.    Then each node is connected to the min_links nearest neighbors.
  654.    Because inverse links are added, some nodes will have more connections.
  655.    The distance between nodes is the hypotenuse times curvature(),
  656.    where curvature() defaults to a random number between 1.1 and 1.5."""
  657.     g = UndirectedGraph()
  658.     g.locations = {}
  659.     # Build the cities
  660.     for node in nodes:
  661.         g.locations[node] = (random.randrange(width), random.randrange(height))
  662.     # Build roads from each city to at least min_links nearest neighbors.
  663.     for i in range(min_links):
  664.         for node in nodes:
  665.             if len(g.get(node)) < min_links:
  666.                 here = g.locations[node]
  667.  
  668.                 def distance_to_node(n):
  669.                     if n is node or g.get(node, n):
  670.                         return math.inf
  671.                     return distance(g.locations[n], here)
  672.  
  673.                 neighbor = nodes.index(min(nodes, key=distance_to_node))
  674.                 d = distance(g.locations[neighbor], here) * curvature()
  675.                 g.connect(node, neighbor, int(d))
  676.     return g
  677.  
  678.  
  679. class GraphProblem(Problem):
  680.     """Проблем на пребарување на граф од еден до друг јазол."""
  681.  
  682.     def __init__(self, initial, goal, graph):
  683.         super().__init__(initial, goal)
  684.         self.graph = graph
  685.  
  686.     def actions(self, state):
  687.         """Акциите кај јазол во граф се едноставно - неговите соседи."""
  688.         return list(self.graph.get(state).keys())
  689.  
  690.     def result(self, state, action):
  691.         """Резултат на одењето кон сосед е самиот тој сосед."""
  692.         return action
  693.  
  694.     def path_cost(self, c, state1, action, state2):
  695.         return c + (self.graph.get(state1, state2) or math.inf)
  696.  
  697.     def h(self, node):
  698.         """Функцијата h е праволиниско растојание од состојбата зачувана
  699.        во тековниот јазол до целната состојба."""
  700.         locs = getattr(self.graph, 'locations', None)
  701.         if locs:
  702.             return int(distance(locs[node.state], locs[self.goal]))
  703.         else:
  704.             return math.inf
  705.  
  706.  
  707. # Kutii - neinformirano prebaruvanje
  708.  
  709. # poziciite kade sto treba da gi doneseme kutiite
  710. tochki = ((2, 3), (2, 5), (2, 7))
  711.  
  712.  
  713. def all_different(state):
  714.     """pomosna funkcija za proverka dali chovecheto ili bilo koi kutii se naogjaat na ista pozicija
  715.  
  716.        :type state tuple
  717.        :rtype bool
  718.    """
  719.     x1, x2, x3, x4 = state
  720.  
  721.     # return x1 != x2 != x3 != x4 - ne raboti tocno vaka, mora site posebno da gi proverime
  722.  
  723.     return x1 != x2 and x1 != x3 and x1 != x4 and x2 != x3 and x2 != x4 and x3 != x4
  724.  
  725.  
  726. def is_valid(state):
  727.     """pomosna funkcija za proverka dali e validna sostojbata
  728.     vrakja True ako site se na razlicni pozicii i ne se nadvor od tablata
  729.  
  730.     :type state tuple
  731.     :rtype bool
  732.     """
  733.  
  734.     if all_different(state):
  735.         c_row, c_col = state[0]
  736.         b1_row, b1_col = state[1]
  737.         b2_row, b2_col = state[2]
  738.         b3_row, b3_col = state[3]
  739.  
  740.         if c_row < 0 or b1_row < 0 or b2_row < 0 or b3_row < 0:
  741.             return False
  742.  
  743.         if c_col < 0 or b1_col < 0 or b2_col < 0 or b3_col < 0:
  744.             return False
  745.  
  746.         if c_row > 7 or b1_row > 7 or b2_row > 7 or b3_row > 7:
  747.             return False
  748.  
  749.         if c_col > 9 or b1_col > 9 or b2_col > 9 or b3_col > 9:
  750.             return False
  751.  
  752.         return True
  753.  
  754.     return False
  755.  
  756.  
  757. class Kutii(Problem):
  758.  
  759.     def __init__(self, initial, goal):
  760.         super().__init__(initial, goal)
  761.  
  762.     def goal_test(self, state):
  763.         if all_different(state):
  764.             return (state[1] in tochki) and (state[2] in tochki) and (state[3] in tochki)
  765.  
  766.         return False
  767.  
  768.     def successor(self, state):
  769.         successors = dict()
  770.  
  771.         choveche, b1, b2, b3 = state
  772.         c_row, c_col = choveche
  773.         b1_row, b1_col = b1
  774.         b2_row, b2_col = b2
  775.         b3_row, b3_col = b3
  776.  
  777.         # Gore
  778.         c_row_new = c_row - 1
  779.         c_col_new = c_col
  780.         choveche_new = (c_row_new, c_col_new)
  781.  
  782.         # ako chovecheto ne udri vo kutija koga ke se pridvizi togas dodadi kako akcija GoreC ako e validna sostojbata
  783.         if all_different((choveche_new, b1, b2, b3)):
  784.             state_new = (choveche_new, b1, b2, b3)
  785.             if is_valid(state_new):
  786.                 successors['GoreC'] = state_new
  787.         else:
  788.             # ako udril vo nekoja kutija, pomesti ja i taa kutija nagore pa dodadi akcija GoreCK ako e validna
  789.             b1_new = (b1_row, b1_col)
  790.             b2_new = (b2_row, b2_col)
  791.             b3_new = (b3_row, b3_col)
  792.  
  793.             if choveche_new == b1 and (b1 not in tochki):
  794.                 b1_row_new = b1_row - 1
  795.                 b1_col_new = b1_col
  796.                 b1_new = (b1_row_new, b1_col_new)
  797.  
  798.             if choveche_new == b2 and (b2 not in tochki):
  799.                 b2_row_new = b2_row - 1
  800.                 b2_col_new = b2_col
  801.                 b2_new = (b2_row_new, b2_col_new)
  802.  
  803.             if choveche_new == b3 and (b3 not in tochki):
  804.                 b3_row_new = b3_row - 1
  805.                 b3_col_new = b3_col
  806.                 b3_new = (b3_row_new, b3_col_new)
  807.  
  808.             state_new = (choveche_new, b1_new, b2_new, b3_new)
  809.             if is_valid(state_new):
  810.                 successors['GoreCK'] = state_new
  811.  
  812.         # Dolu
  813.         c_row_new = c_row + 1
  814.         c_col_new = c_col
  815.         choveche_new = (c_row_new, c_col_new)
  816.  
  817.         # ako chovecheto ne udri vo kutija koga ke se pridvizi togas dodadi kako akcija DoluC ako e validna sostojbata
  818.         if all_different((choveche_new, b1, b2, b3)):
  819.             state_new = (choveche_new, b1, b2, b3)
  820.             if is_valid(state_new):
  821.                 successors['DoluC'] = state_new
  822.         else:
  823.             # ako udril vo nekoja kutija, pomesti ja i taa kutija nadolu pa dodadi akcija DoluCK ako e validna
  824.             b1_new = (b1_row, b1_col)
  825.             b2_new = (b2_row, b2_col)
  826.             b3_new = (b3_row, b3_col)
  827.  
  828.             if choveche_new == b1 and (b1 not in tochki):
  829.                 b1_row_new = b1_row + 1
  830.                 b1_col_new = b1_col
  831.                 b1_new = (b1_row_new, b1_col_new)
  832.  
  833.             if choveche_new == b2 and (b2 not in tochki):
  834.                 b2_row_new = b2_row + 1
  835.                 b2_col_new = b2_col
  836.                 b2_new = (b2_row_new, b2_col_new)
  837.  
  838.             if choveche_new == b3 and (b3 not in tochki):
  839.                 b3_row_new = b3_row + 1
  840.                 b3_col_new = b3_col
  841.                 b3_new = (b3_row_new, b3_col_new)
  842.  
  843.             state_new = (choveche_new, b1_new, b2_new, b3_new)
  844.             if is_valid(state_new):
  845.                 successors['DoluCK'] = state_new
  846.  
  847.         # Levo
  848.         c_row_new = c_row
  849.         c_col_new = c_col - 1
  850.         choveche_new = (c_row_new, c_col_new)
  851.  
  852.         # ako chovecheto ne udri vo kutija koga ke se pridvizi togas dodadi kako akcija LevoC ako e validna sostojbata
  853.         if all_different((choveche_new, b1, b2, b3)):
  854.             state_new = (choveche_new, b1, b2, b3)
  855.             if is_valid(state_new):
  856.                 successors['LevoC'] = state_new
  857.         else:
  858.             # ako udril vo nekoja kutija, pomesti ja i taa kutija levo pa dodadi akcija LevoCK ako e validna
  859.             b1_new = (b1_row, b1_col)
  860.             b2_new = (b2_row, b2_col)
  861.             b3_new = (b3_row, b3_col)
  862.  
  863.             if choveche_new == b1 and (b1 not in tochki):
  864.                 b1_row_new = b1_row
  865.                 b1_col_new = b1_col - 1
  866.                 b1_new = (b1_row_new, b1_col_new)
  867.  
  868.             if choveche_new == b2 and (b2 not in tochki):
  869.                 b2_row_new = b2_row
  870.                 b2_col_new = b2_col - 1
  871.                 b2_new = (b2_row_new, b2_col_new)
  872.  
  873.             if choveche_new == b3 and (b3 not in tochki):
  874.                 b3_row_new = b3_row
  875.                 b3_col_new = b3_col - 1
  876.                 b3_new = (b3_row_new, b3_col_new)
  877.  
  878.             state_new = (choveche_new, b1_new, b2_new, b3_new)
  879.             if is_valid(state_new):
  880.                 successors['LevoCK'] = state_new
  881.  
  882.         # Desno
  883.         c_row_new = c_row
  884.         c_col_new = c_col + 1
  885.         choveche_new = (c_row_new, c_col_new)
  886.  
  887.         # ako chovecheto ne udri vo kutija koga ke se pridvizi togas dodadi kako akcija DesnoC ako e validna sostojbata
  888.         if all_different((choveche_new, b1, b2, b3)):
  889.             state_new = (choveche_new, b1, b2, b3)
  890.             if is_valid(state_new):
  891.                 successors['DesnoC'] = state_new
  892.         else:
  893.             # ako udril vo nekoja kutija, pomesti ja i taa kutija desno pa dodadi akcija DesnoCK ako e validna
  894.             b1_new = (b1_row, b1_col)
  895.             b2_new = (b2_row, b2_col)
  896.             b3_new = (b3_row, b3_col)
  897.  
  898.             if choveche_new == b1 and (b1 not in tochki):
  899.                 b1_row_new = b1_row
  900.                 b1_col_new = b1_col + 1
  901.                 b1_new = (b1_row_new, b1_col_new)
  902.  
  903.             if choveche_new == b2 and (b2 not in tochki):
  904.                 b2_row_new = b2_row
  905.                 b2_col_new = b2_col + 1
  906.                 b2_new = (b2_row_new, b2_col_new)
  907.  
  908.             if choveche_new == b3 and (b3 not in tochki):
  909.                 b3_row_new = b3_row
  910.                 b3_col_new = b3_col + 1
  911.                 b3_new = (b3_row_new, b3_col_new)
  912.  
  913.             state_new = (choveche_new, b1_new, b2_new, b3_new)
  914.             if is_valid(state_new):
  915.                 successors['DesnoCK'] = state_new
  916.  
  917.         return successors
  918.  
  919.     def actions(self, state):
  920.         return self.successor(state).keys()
  921.  
  922.     def result(self, state, action):
  923.         possible = self.successor(state)
  924.         return possible[action]
  925.  
  926.  
  927. if __name__ == "__main__":
  928.     man_row = int(input())
  929.     man_column = int(input())
  930.     b1_row = int(input())
  931.     b1_column = int(input())
  932.     b2_row = int(input())
  933.     b2_column = int(input())
  934.     b3_row = int(input())
  935.     b3_column = int(input())
  936.  
  937.     Initial = ((man_row, man_column), (b1_row, b1_column), (b2_row, b2_column), (b3_row, b3_column))
  938.  
  939.     kutii_problem = Kutii(Initial, None)
  940.  
  941.     answer = breadth_first_graph_search(kutii_problem)
  942.  
  943.     print(answer.solution())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement