I am working on a program that does a Breadth First Search on a series of connected links, and should output a print statement telling me what links to cut. The links to cut will block a certain agent from going to its present location, to one of the exit gateways scattered around the graph.
The image shows exactly what the program does, and what it shouldn't do at the same time. The green dotted links are those that I am printing, which will cut the path to the red coloured bobnet agent to the light blue coloured exit gateways.
Here is the mapping of the inputs for my program:
Initialization input: Line 1: 3 integers N L E
Next L lines: 2 integers per line (N1, N2), indicating a link between the nodes indexed N1 and N2 in the network.
Next E lines: 1 integer EI representing the index of an exit gateway node.
So what happens in my program is that, as you can see, the program checks the location of the agent, and cuts the most probable link next to it. However, there seems to be a more efficient program, which instead of finishing in 39 moves like in my case, ends with less, and I want to ask for help finding what is wrong with my program to effectively manage to make it more efficient:
import sys
from collections import deque
class Binder:
def __init__(self, id, pre_binder):
self.id = id
self.pre_binder = pre_binder
class Game:
def __init__(self):
# n: the total number of nodes in the level, including the gateways
# l: the number of links
# e: the number of exit gateways
n, l, e = [int(j) for j in input().split()]
self.graph = {i: [] for i in range(n)}
for _ in range(l):
# n1: N1 and N2 defines a link between these nodes
n1, n2 = [int(j) for j in input().split()]
self.graph[n1].append(n2)
self.graph[n2].append(n1)
self.exit_gateways = [int(input()) for _ in range(e)] # the index of a gateway node
def breadth_first_search(self, root):
print(f"Graph nodes: {self.graph}", file=sys.stderr)
visited, queue = set(), deque([Binder(root, None)])
visited.add(root)
while queue:
binder = queue.popleft()
vertex = binder.id
print(f"Bobnet Location: {self.graph[vertex]}", file=sys.stderr)
for neighbor in self.graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
new_binder = Binder(neighbor, binder)
queue.append(new_binder)
if neighbor in self.exit_gateways:
return Binder(neighbor, binder)
def trace_and_cut_link(result_binder):
current_binder = result_binder
while current_binder.pre_binder is not None:
previous_binder = current_binder.pre_binder
link_to_cut = (previous_binder.id, current_binder.id)
print(f'Cutting link: {link_to_cut}', file=sys.stderr)
current_binder = previous_binder
print(link_to_cut[0], link_to_cut[1])
game = Game()
while True:
node_index = int(input()) # The index of the node on which the Bobnet agent is positioned this turn
result_binder = game.breadth_first_search(node_index)
trace_and_cut_link(result_binder)
Game information:
Graph nodes: {0: [2, 10, 9, 5, 12, 11, 17, 7, 13, 14, 6, 3, 4, 15, 1, 16, 8],
1: [2, 17, 0, 37],
2: [0, 37, 1, 3, 35],
3: [34, 0, 2, 4],
4: [5, 33, 0, 3],
5: [4, 0, 6],
6: [5, 0, 7],
7: [8, 0, 6],
8: [7, 9, 0],
9: [0, 10, 8],
10: [0, 11, 9],
11: [10, 0, 12],
12: [0, 11, 13],
13: [14, 0, 12],
14: [13, 0, 15],
15: [16, 14, 0],
16: [17, 15, 0],
17: [16, 0, 1],
18: [26, 24, 23, 22, 27, 25, 21, 19, 20],
19: [27, 20, 18],
20: [19, 21, 18],
21: [29, 22, 18, 20],
22: [18, 23, 21, 36],
23: [18, 24, 35, 22, 37],
24: [18, 23, 25, 37],
25: [26, 24, 18],
26: [18, 25, 27],
27: [19, 18, 26],
28: [36, 32, 34, 31, 35, 29, 33, 30],
29: [21, 30, 28, 36],
30: [31, 29, 28],
31: [30, 28, 32],
32: [28, 33, 31],
33: [32, 4, 34, 28],
34: [3, 35, 28, 33],
35: [37, 34, 23, 28, 36, 2],
36: [28, 22, 35, 29],
37: [35, 2, 23, 1, 24]}
Block the Agent!
Agent is at position 37
Standard Output Stream:
37 35
Game information:
Link [37-35] severed
Agent moved from 37 to 2
Standard Output Stream:
2 0
Game information:
Link [2-0] severed
Agent moved from 2 to 35
Standard Output Stream:
35 28
Game information:
Link [35-28] severed
Agent moved from 35 to 23
Thank you in advance.
There are at least two improvements possible:
Don't only report that an edge ("link") is cut, also remove it from your graph, otherwise you risk to report the same edge in one of the next rounds. And when you do remove it, don't forget to remove it in both directions.
Instead of cutting the edge near the agent, cut the edge that is closest to the exit gateway.
For the example graph that would ensure you only cut 35 edges as then the exit gateways are completely detached from the rest of the graph.
If these improvements are not enough, then here is an additional idea:
Then check which edge in these paths is shared most often, and cut that edge. For example, if you have this situation:
*------exit
/ /
*-------*------*------*
/ /
agent------*
...then you should find 4 different paths from agent to exit, and the edge at the center will be in all four paths. So that is the most frequently used edge and should be cut.