For the complete program the Rectangle
class was used in and as a practical example of its usage:
# The robots have requested your help setting up a new base on the
# island. They need you to define the visibility of a building from the
# southern edge of the base. To help you out, you have been given a map
# of the buildings in the complex. The map is an orthogonal projection
# of each of the buildings onto a horizontal plane. It is oriented on a
# rectangular coordinate system so that the positive x-axis points east
# and the positive y-axis points north. No two buildings in the map
# overlap or touch. Each of the buildings have perfectly rectangular
# sides and are aligned from north to south and from east to west. The
# map is a list of buildings. Every building is presented as the list
# with coordinate of south-west corner, coordinate of north-east corner
# and height - [Xsw, Ysw, Xne, Yne, height]. We need to determinate how
# many of the buildings are visible from the area just south of the base
# (excluding the angle of vision, just using projection.) See the
# illustration below.
# Input: Building coordinates and heights as a list of lists. The
# coordinates are integers. The heights are integers or floats.
# Output:The quantity of visible buildings as an integer.
# Example:
# checkio([
# [1, 1, 4, 5, 3.5],
# [2, 6, 4, 8, 5],
# [5, 1, 9, 3, 6],
# [5, 5, 6, 6, 8],
# [7, 4, 10, 6, 4],
# [5, 7, 10, 8, 3]
# ]) == 5 #"First"
# checkio([
# [1, 1, 11, 2, 2],
# [2, 3, 10, 4, 1],
# [3, 5, 9, 6, 3],
# [4, 7, 8, 8, 2]
# ]) == 2 #"Second"
# assert checkio([
# [1, 1, 3, 3, 6],
# [5, 1, 7, 3, 6],
# [9, 1, 11, 3, 6],
# [1, 4, 3, 6, 6],
# [5, 4, 7, 6, 6],
# [9, 4, 11, 6, 6],
# [1, 7, 11, 8, 3.25]
# ]) == 4 #"Third"
# How it is used: This concept is useful for image recognition systems
# and graphical systems. When rendering of 3D model you should determine
# the visibility of the surfaces. This concept also can be applied in
# architecture and city planning, allowing you to plan out which sides
# of a building will receive sunlight, or if a building will block
# natural light in another building.
# Precondition: 0 < |buildings| < 10> 10
# ∀ x ∈ coordinate : x is an integer; 0 ≤ x ≤10
# ∀ h ∈ heights : x is an integer or a float; 0 < h ≤20
################################################################################
from itertools import combinations, product, starmap, tee
from pprint import pprint
from random import randint
################################################################################
TESTS = {
"0. Basics": [
#First
{
"input":
[
[1, 1, 4, 5, 3.5],
[2, 6, 4, 8, 5],
[5, 1, 9, 3, 6],
[5, 5, 6, 6, 8],
[7, 4, 10, 6, 4],
[5, 7, 10, 8, 3]
],
"answer": 5,
"explanation": [5, 1, 3, 4, 0, 2]
},
#Second
{
"input":
[
[1, 1, 11, 2, 2],
[2, 3, 10, 4, 1],
[3, 5, 9, 6, 3],
[4, 7, 8, 8, 2]
],
"answer": 2
},
#Third
{
"input":
[
[1, 1, 3, 3, 6],
[5, 1, 7, 3, 6],
[9, 1, 11, 3, 6],
[1, 4, 3, 6, 6],
[5, 4, 7, 6, 6],
[9, 4, 11, 6, 6],
[1, 7, 11, 8, 3.25]
],
"answer": 4
},
#Alone
{
"input":
[
[0, 0, 1, 1, 10]
],
"answer": 1
},
#Shadow
{
"input":
[
[2, 2, 3, 3, 4],
[2, 5, 3, 6, 4]
],
"answer": 1
},
],
"1. Extra": [
#H1
{
"input":
[
[1, 1, 3, 3, 20],
[3, 4, 5, 6, 10],
[5, 1, 7, 3, 20],
[1, 7, 7, 9, 20]
],
"answer": 4
},
#H2
{
"input":
[
[1, 1, 3, 3, 20],
[3, 4, 5, 6, 20],
[5, 1, 7, 3, 20],
[1, 7, 7, 9, 20]
],
"answer": 3
},
#H3
{
"input":
[
[0, 1, 1, 2, 2.5],
[0, 3, 1, 4, 3.5],
[0, 5, 1, 6, 1.5],
[3, 0, 4, 2, 30],
[5, 0, 6, 2, 2],
[7, 0, 8, 2, 2],
[4, 3, 8, 4, 2],
[4, 5, 5, 6, 1],
[7, 5, 8, 6, 3]
],
"answer": 7
},
#H4
{
"input":
[
[0, 0, 10, 1, 10],
[3, 3, 4, 4, 1],
[5, 5, 6, 6, 1],
[7, 7, 8, 8, 1]
],
"answer": 1
},
],
"2. Random": [
#Half-Random
{
"input":
[
[0, 0, 10, 1, 10],
[3, 3, 4, 4, randint(1, 9)],
[5, 5, 6, 6, randint(1, 9)],
],
"answer": 1
},
#Half-Random
{
"input":
[
[1, 1, 2, 2, 1],
[randint(3, 5), randint(3, 5), randint(6, 8), randint(6, 8), 1]
],
"answer": 2
},
]
}
################################################################################
def test():
for category, tests in sorted(TESTS.items()):
for test in tests:
i, a = test['input'], test['answer']
o = checkio(i)
if o != a:
print('Category:', category)
print(' Input:')
pprint(i, indent=8)
print(' Output:', o)
print(' Answer:', a)
def checkio(buildings):
buildings = sorted(starmap(Building, buildings), key=lambda b: b.z)
for a, b in combinations(buildings, 2):
if a.seen:
a.cover(b)
return sum(b.seen for b in buildings)
################################################################################
class Building:
def __init__(self, x1, y1, x2, y2, height):
self.rect = [Rectangle(x1, 0, x2, height)]
self.z = min(y1, y2)
def __str__(self):
return 'Z = {}; {}'.format(self.z, self.rect)
def cover(self, other):
for s in self.rect:
other.rect = list(flatten(o - s for o in other.rect))
@property
def seen(self):
return bool(self.rect)
def flatten(iterable):
if isinstance(iterable, Rectangle):
raise TypeError()
for item in iterable:
try:
yield from flatten(item)
except TypeError:
yield item
################################################################################
class Rectangle:
__slots__ = '__x1', '__y1', '__x2', '__y2'
def __init__(self, x1, y1, x2, y2):
self.__setstate__((min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)))
def __repr__(self):
return '{}({})'.format(type(self).__name__, ', '.join(map(repr, self)))
def __eq__(self, other):
return self.data == other.data
def __ne__(self, other):
return self.data != other.data
def __hash__(self):
return hash(self.data)
def __len__(self):
return 4
def __getitem__(self, key):
return self.data[key]
def __iter__(self):
return iter(self.data)
def __and__(self, other):
x1, y1, x2, y2 = max(self.x1, other.x1), max(self.y1, other.y1), \
min(self.x2, other.x2), min(self.y2, other.y2)
if x1 < x2 and y1 < y2:
return type(self)(x1, y1, x2, y2)
def __sub__(self, other):
intersection = self & other
if intersection is None:
yield self
else:
x, y = {self.x1, self.x2}, {self.y1, self.y2}
if self.x1 < other.x1 < self.x2:
x.add(other.x1)
if self.y1 < other.y1 < self.y2:
y.add(other.y1)
if self.x1 < other.x2 < self.x2:
x.add(other.x2)
if self.y1 < other.y2 < self.y2:
y.add(other.y2)
for (x1, x2), (y1, y2) in product(pairwise(sorted(x)),
pairwise(sorted(y))):
instance = type(self)(x1, y1, x2, y2)
if instance != intersection:
yield instance
def __getstate__(self):
return self.x1, self.y1, self.x2, self.y2
def __setstate__(self, state):
self.__x1, self.__y1, self.__x2, self.__y2 = state
@property
def x1(self):
return self.__x1
@property
def y1(self):
return self.__y1
@property
def x2(self):
return self.__x2
@property
def y2(self):
return self.__y2
@property
def width(self):
return self.x2 - self.x1
@property
def height(self):
return self.y2 - self.y1
intersection = __and__
difference = __sub__
data = property(__getstate__)
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
################################################################################
if __name__ == '__main__':
test()