rectangles.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from itertools import combinations
from operator import attrgetter


class Rectangle:
    CORNER = '+'
    HORIZONTAL = '-'
    VERTICAL = '|'

    # Valid characters for horizontal and vertical sides
    # These are allowed to include corners
    VALID_HORIZONTAL_CHARS = frozenset(CORNER + HORIZONTAL)
    VALID_VERTICAL_CHARS = frozenset(CORNER + VERTICAL)

    def __init__(self, chars):
        self._chars = chars

    @property
    def has_sides(self):
        """Make sure rectangle has all its sides"""
        horizontals = set(self._chars[0] + self._chars[-1])
        verticals = set(
            ''.join(line[0] + line[-1] for line in self._chars)
        )
        return (
            horizontals <= self.VALID_HORIZONTAL_CHARS and
            verticals <= self.VALID_VERTICAL_CHARS
        )

    @classmethod
    def get(cls, tl, tr, bl, br, lines):
        """
        Try to make a rectangle from 4 coordinates

        If tl, tr, bl, br line up as a non-zero rectangle,
        return a corresponding Rectangle object
        Otherwise, return None
        """
        corners_line_up = (
            tl[0] == tr[0] and bl[0] == br[0] and
            tl[1] == bl[1] and tr[1] == br[1]
        )
        if corners_line_up:
            top, left = tl
            bottom, right = br
            if top < bottom and left < right:
                return Rectangle(
                    [row[left:right+1]
                     for row in lines[top:bottom+1]]
                )

def count(lines=''):
    # Find all corner positions
    corners = (
        (row_num, col_num)
        for row_num, line in enumerate(lines)
        for col_num, char in enumerate(line)
        if char == Rectangle.CORNER
    )

    # Search combinations of corners to find rectangles
    rects = (
        Rectangle.get(*points, lines)
        for points in combinations(corners, 4)
    )

    # Get the number this which have complete sides
    return sum(r.has_sides for r in rects if r)

Comments


You're not logged in right now. Please login via GitHub to comment