"""Utilities
"""
# pylint: disable=no-member
import collections.abc
import math
import os
[documentos]def floor(value, size, offset=200):
"""Floor of `value` given `size` and `offset`.
The floor function is best understood with a diagram of the number line::
-200 -100 0 100 200
<--|--x--|-----|--y--|--z--|-->
The number line shown has offset 200 denoted by the left-hand tick mark at
-200 and size 100 denoted by the tick marks at -100, 0, 100, and 200. The
floor of a value is the left-hand tick mark of the range where it lies. So
for the points show above: ``floor(x)`` is -200, ``floor(y)`` is 0, and
``floor(z)`` is 100.
>>> floor(10, 100)
0.0
>>> floor(120, 100)
100.0
>>> floor(-10, 100)
-100.0
>>> floor(-150, 100)
-200.0
>>> floor(50, 167)
-33.0
"""
return float(((value + offset) // size) * size - offset)
[documentos]def path(filename):
"Return full path to `filename` in freegames module."
filepath = os.path.realpath(__file__)
dirpath = os.path.dirname(filepath)
fullpath = os.path.join(dirpath, filename)
return fullpath
[documentos]def line(a, b, x, y):
"Draw line from `(a, b)` to `(x, y)`."
import turtle
turtle.up()
turtle.goto(a, b)
turtle.down()
turtle.goto(x, y)
[documentos]def square(x, y, size, name):
"""Draw square at `(x, y)` with side length `size` and fill color `name`.
The square is oriented so the bottom left corner is at (x, y).
"""
import turtle
turtle.up()
turtle.goto(x, y)
turtle.down()
turtle.color(name)
turtle.begin_fill()
for count in range(4):
turtle.forward(size)
turtle.left(90)
turtle.end_fill()
[documentos]class vector(collections.abc.Sequence):
"""Two-dimensional vector.
Vectors can be modified in-place.
>>> v = vector(0, 1)
>>> v.move(1)
>>> v
vector(1, 2)
>>> v.rotate(90)
>>> v
vector(-2.0, 1.0)
"""
# pylint: disable=invalid-name
PRECISION = 6
__slots__ = ('_x', '_y', '_hash')
[documentos] def __init__(self, x, y):
"""Initialize vector with coordinates: x, y.
>>> v = vector(1, 2)
>>> v.x
1
>>> v.y
2
"""
self._hash = None
self._x = round(x, self.PRECISION)
self._y = round(y, self.PRECISION)
@property
def x(self):
"""X-axis component of vector.
>>> v = vector(1, 2)
>>> v.x
1
>>> v.x = 3
>>> v.x
3
"""
return self._x
@x.setter
def x(self, value):
if self._hash is not None:
raise ValueError('cannot set x after hashing')
self._x = round(value, self.PRECISION)
@property
def y(self):
"""Y-axis component of vector.
>>> v = vector(1, 2)
>>> v.y
2
>>> v.y = 5
>>> v.y
5
"""
return self._y
@y.setter
def y(self, value):
if self._hash is not None:
raise ValueError('cannot set y after hashing')
self._y = round(value, self.PRECISION)
[documentos] def __hash__(self):
"""v.__hash__() -> hash(v)
>>> v = vector(1, 2)
>>> h = hash(v)
>>> v.x = 2
Traceback (most recent call last):
...
ValueError: cannot set x after hashing
"""
if self._hash is None:
pair = (self.x, self.y)
self._hash = hash(pair)
return self._hash
[documentos] def __len__(self):
"""v.__len__() -> len(v)
>>> v = vector(1, 2)
>>> len(v)
2
"""
return 2
[documentos] def __getitem__(self, index):
"""v.__getitem__(v, i) -> v[i]
>>> v = vector(3, 4)
>>> v[0]
3
>>> v[1]
4
>>> v[2]
Traceback (most recent call last):
...
IndexError
"""
if index == 0:
return self.x
if index == 1:
return self.y
raise IndexError
[documentos] def copy(self):
"""Return copy of vector.
>>> v = vector(1, 2)
>>> w = v.copy()
>>> v is w
False
"""
type_self = type(self)
return type_self(self.x, self.y)
[documentos] def __eq__(self, other):
"""v.__eq__(w) -> v == w
>>> v = vector(1, 2)
>>> w = vector(1, 2)
>>> v == w
True
"""
if isinstance(other, vector):
return self.x == other.x and self.y == other.y
return NotImplemented
[documentos] def __ne__(self, other):
"""v.__ne__(w) -> v != w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v != w
True
"""
if isinstance(other, vector):
return self.x != other.x or self.y != other.y
return NotImplemented
[documentos] def __iadd__(self, other):
"""v.__iadd__(w) -> v += w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v += w
>>> v
vector(4, 6)
>>> v += 1
>>> v
vector(5, 7)
"""
if self._hash is not None:
raise ValueError('cannot add vector after hashing')
if isinstance(other, vector):
self.x += other.x
self.y += other.y
else:
self.x += other
self.y += other
return self
[documentos] def __add__(self, other):
"""v.__add__(w) -> v + w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v + w
vector(4, 6)
>>> v + 1
vector(2, 3)
>>> 2.0 + v
vector(3.0, 4.0)
"""
copy = self.copy()
return copy.__iadd__(other)
__radd__ = __add__
[documentos] def move(self, other):
"""Move vector by other (in-place).
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v.move(w)
>>> v
vector(4, 6)
>>> v.move(3)
>>> v
vector(7, 9)
"""
self.__iadd__(other)
[documentos] def __isub__(self, other):
"""v.__isub__(w) -> v -= w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v -= w
>>> v
vector(-2, -2)
>>> v -= 1
>>> v
vector(-3, -3)
"""
if self._hash is not None:
raise ValueError('cannot subtract vector after hashing')
if isinstance(other, vector):
self.x -= other.x
self.y -= other.y
else:
self.x -= other
self.y -= other
return self
[documentos] def __sub__(self, other):
"""v.__sub__(w) -> v - w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v - w
vector(-2, -2)
>>> v - 1
vector(0, 1)
"""
copy = self.copy()
return copy.__isub__(other)
[documentos] def __imul__(self, other):
"""v.__imul__(w) -> v *= w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v *= w
>>> v
vector(3, 8)
>>> v *= 2
>>> v
vector(6, 16)
"""
if self._hash is not None:
raise ValueError('cannot multiply vector after hashing')
if isinstance(other, vector):
self.x *= other.x
self.y *= other.y
else:
self.x *= other
self.y *= other
return self
[documentos] def __mul__(self, other):
"""v.__mul__(w) -> v * w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v * w
vector(3, 8)
>>> v * 2
vector(2, 4)
>>> 3.0 * v
vector(3.0, 6.0)
"""
copy = self.copy()
return copy.__imul__(other)
__rmul__ = __mul__
[documentos] def scale(self, other):
"""Scale vector by other (in-place).
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> v.scale(w)
>>> v
vector(3, 8)
>>> v.scale(0.5)
>>> v
vector(1.5, 4.0)
"""
self.__imul__(other)
[documentos] def __itruediv__(self, other):
"""v.__itruediv__(w) -> v /= w
>>> v = vector(2, 4)
>>> w = vector(4, 8)
>>> v /= w
>>> v
vector(0.5, 0.5)
>>> v /= 2
>>> v
vector(0.25, 0.25)
"""
if self._hash is not None:
raise ValueError('cannot divide vector after hashing')
if isinstance(other, vector):
self.x /= other.x
self.y /= other.y
else:
self.x /= other
self.y /= other
return self
[documentos] def __truediv__(self, other):
"""v.__truediv__(w) -> v / w
>>> v = vector(1, 2)
>>> w = vector(3, 4)
>>> w / v
vector(3.0, 2.0)
>>> v / 2
vector(0.5, 1.0)
"""
copy = self.copy()
return copy.__itruediv__(other)
[documentos] def __neg__(self):
"""v.__neg__() -> -v
>>> v = vector(1, 2)
>>> -v
vector(-1, -2)
"""
# pylint: disable=invalid-unary-operand-type
copy = self.copy()
copy.x = -copy.x
copy.y = -copy.y
return copy
[documentos] def __abs__(self):
"""v.__abs__() -> abs(v)
>>> v = vector(3, 4)
>>> abs(v)
5.0
"""
return (self.x ** 2 + self.y ** 2) ** 0.5
[documentos] def rotate(self, angle):
"""Rotate vector counter-clockwise by angle (in-place).
>>> v = vector(1, 2)
>>> v.rotate(90)
>>> v == vector(-2, 1)
True
"""
if self._hash is not None:
raise ValueError('cannot rotate vector after hashing')
radians = angle * math.pi / 180.0
cosine = math.cos(radians)
sine = math.sin(radians)
x = self.x
y = self.y
self.x = x * cosine - y * sine
self.y = y * cosine + x * sine
[documentos] def __repr__(self):
"""v.__repr__() -> repr(v)
>>> v = vector(1, 2)
>>> repr(v)
'vector(1, 2)'
"""
type_self = type(self)
name = type_self.__name__
return '{}({!r}, {!r})'.format(name, self.x, self.y)