express_delivery.check_solution()   F
last analyzed

Complexity

Conditions 16

Size

Total Lines 41
Code Lines 38

Duplication

Lines 14
Ratio 34.15 %

Importance

Changes 0
Metric Value
cc 16
eloc 38
nop 3
dl 14
loc 41
rs 2.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like express_delivery.check_solution() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import heapq
2
from collections import defaultdict
3
from itertools import permutations
4
5
6
def translate_route(path):
7
    result = []
8
    for i in zip(path, path[1:]):
9
        if i[0][0] < i[1][0]:
10
            result.append('D')
11
        elif i[0][0] > i[1][0]:
12
            result.append('U')
13
        elif i[0][1] < i[1][1]:
14
            result.append('R')
15
        else:
16
            result.append('L')
17
    return ''.join(result)
18
19
20 View Code Duplication
def shortest_path(graph, start, end):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
21
    queue = [(0, start, [])]
22
    seen = set()
23
    while True:
24
        (cost, v, path) = heapq.heappop(queue)
25
        if v not in seen:
26
            path = path + [v]
27
            seen.add(v)
28
            if v == end:
29
                break
30
            for (next_item, c) in graph[v].items():
31
                heapq.heappush(queue, (cost + c, next_item, path))
32
    return translate_route(path)
33
34
35
def build_topology(field_map):
36
    result = defaultdict(dict)
37
    cols = len(field_map[0])
38
    rows = len(field_map)
39
40
    for y in range(rows - 1):
41
        for x in range(cols - 1):
42
            if field_map[y][x] != 'W':
43
                if field_map[y + 1][x] != 'W':
44
                    result[(y, x)][(y + 1, x)] = 1
45
                    result[(y + 1, x)][(y, x)] = 1
46
                if field_map[y][x + 1] != 'W':
47
                    result[(y, x)][(y, x + 1)] = 1
48
                    result[(y, x + 1)][(y, x)] = 1
49
    # last col
50
    for y in range(rows - 1):
51
        if field_map[y][cols - 1] != 'W':
52
            if field_map[y + 1][cols - 1] != 'W':
53
                result[(y, cols - 1)][(y + 1, cols - 1)] = 1
54
                result[(y + 1, cols - 1)][(y, cols - 1)] = 1
55
    # last row
56
    for x in range(cols - 1):
57
        if field_map[rows - 1][x] != 'W':
58
            if field_map[rows - 1][x] != 'W':
59
                result[(rows - 1, x)][(rows - 1, x + 1)] = 1
60
                result[(rows - 1, x + 1)][(rows - 1, x)] = 1
61
    return result
62
63
64
def find_spots(target, field_map):
65
    target_index = [i for i, v in enumerate(''.join(field_map)) if v == target]
66
    return [(i // len(field_map[0]), i % len(field_map[0])) for i in target_index]
67
68
69
def checkio(field_map):
70
    start_point = find_spots('S', field_map)[0]
71
    end_point = find_spots('E', field_map)[0]
72
    boxes = find_spots('B', field_map)
73
    graph = build_topology(field_map)
74
75
    direct_route = shortest_path(graph, start_point, end_point)
76
    unpack_routes = {}
77
    for i in permutations(boxes, 2):
78
        unpack_routes[i] = [
79
            shortest_path(graph, start_point, i[0]),
80
            'B' + shortest_path(graph, i[0], i[1]) + 'B',
81
            shortest_path(graph, i[1], end_point),
82
        ]
83
    unpack_routes_metric = [
84
        len(i[0]) * 2 + len(i[1]) + len(i[2]) * 2
85
        for i in sorted(unpack_routes.values())
86
    ]
87
    if len(direct_route) * 2 < min(unpack_routes_metric):
88
        route = direct_route
89
    else:
90
        index = unpack_routes_metric.index(min(unpack_routes_metric))
91
        route = ''.join(sorted(unpack_routes.values())[index])
92
    return route
93
94
95
if __name__ == '__main__':  # pragma: no cover
96
    print("Example:")
97
    print(checkio(["S...", "....", "B.WB", "..WE"]))
98
99
    # This part is using only for self-checking and not necessary for auto-testing
100
    ACTIONS = {"L": (0, -1), "R": (0, 1), "U": (-1, 0), "D": (1, 0), "B": (0, 0)}
101
102
    def check_solution(func, max_time, field):
103
        max_row, max_col = len(field), len(field[0])
104
        s_row, s_col = 0, 0
105
        total_time = 0
106
        hold_box = True
107
        route = func(field[:])
108
        for step in route:
109
            if step not in ACTIONS:
110
                print("Unknown action {0}".format(step))
111
                return False
112 View Code Duplication
            if step == "B":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
113
                if hold_box:
114
                    if field[s_row][s_col] == "B":
115
                        hold_box = False
116
                        total_time += 1
117
                        continue
118
                    else:
119
                        print("Stephan broke the cargo")
120
                        return False
121
                else:
122
                    if field[s_row][s_col] == "B":
123
                        hold_box = True
124
                    total_time += 1
125
                    continue
126
            n_row, n_col = s_row + ACTIONS[step][0], s_col + ACTIONS[step][1]
127
            total_time += 2 if hold_box else 1
128
            if 0 > n_row or n_row >= max_row or 0 > n_col or n_row >= max_col:
129
                print("We've lost Stephan.")
130
                return False
131
            if field[n_row][n_col] == "W":
132
                print("Stephan fell in water.")
133
                return False
134
            s_row, s_col = n_row, n_col
135
            if field[s_row][s_col] == "E" and hold_box:
136
                if total_time <= max_time:
137
                    return True
138
                else:
139
                    print("You can deliver the cargo faster.")
140
                    return False
141
        print("The cargo is not delivered")
142
        return False
143
144
    assert check_solution(checkio, 12, ["S...", "....", "B.WB", "..WE"]), "1st Example"
145
    assert check_solution(checkio, 11, ["S...", "....", "B..B", "..WE"]), "2nd example"
146
    print("Coding complete? Click 'Check' to earn cool rewards!")
147