CheckIO: Shooting range

import math


def calc_hitpoint(crd1, direction1, crd2, direction2):
    (a, b), (c, d) = crd1, crd2
    (d1, d2), (d3, d4) = direction1, direction2
    if d1 * d4 == d2 * d3:
        return None, None  # parralel lines
    k = ((b - d) * d3 - (a - c) * d4) / (d1 * d4 - d2 * d3)
    return (a + d1 * k, b + d2 * k), k


def calc_equation(crd1, crd2):
    a = (crd1[1] - crd2[1]) / (crd1[0] - crd2[0])
    b = crd1[1] - a * crd1[0]
    return lambda x: a * x + b


def shot(wall1, wall2, shot_point, later_point):
    avg = lambda x, y: (x + y) / 2
    direction = lambda crd1, crd2: tuple((x[0] - x[1]) for x in zip(crd1, crd2))
    distance = lambda crd1, crd2: math.sqrt(sum((x[0] - x[1]) ** 2 for x in zip(crd1, crd2)))

    ballistic, wall = direction(later_point, shot_point), direction(wall2, wall1)
    hit_point, k = calc_hitpoint(shot_point, ballistic, wall1, wall)

    if k and k > 0 and all(
        sorted(w)[0] <= hit_point[i] <= sorted(w)[1]
        for i, w in enumerate(zip(wall1, wall2))
    ):
        center = tuple(avg(c1, c2) for c1, c2 in zip(wall1, wall2))
        score = calc_equation((0, 100), (distance(wall1, wall2) / 2, 0))
        return round(score(distance(center, hit_point)))
    else:
        return -1


if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert shot((2, 2), (5, 7), (11, 2), (8, 3)) == 100, "1st case"
    assert shot((2, 2), (5, 7), (11, 2), (7, 2)) == 0, "2nd case"
    assert shot((2, 2), (5, 7), (11, 2), (8, 4)) == 29, "3th case"
    assert shot((2, 2), (5, 7), (11, 2), (9, 5)) == -1, "4th case"
    assert shot((2, 2), (5, 7), (11, 2), (10.5, 3)) == -1, "4th case again"

http://www.checkio.org/mission/shoot-range/

Leave a Reply

Your email address will not be published. Required fields are marked *