1import math
2import random
3import sys
4import itertools
5from hashlib import md5
6from typing import Iterable, Iterator, Tuple
7
8error_list = []
9
10
11def int_hash(s):
12    h = md5()
13    h.update(s)
14    return int(h.hexdigest(), 16)
15
16
17def seed_rng(seed):
18    """
19    Seeds the default RNG with the specified seed
20    """
21    random.seed(seed)
22    # The following jumpahead call can be uncommented to work around this issue of the Mersenne Twister PRNG
23    # (quoted from wikipedia http://en.wikipedia.org/wiki/Mersenne_twister):
24    # "It can take a long time to start generating output that passes randomness tests, if the initial state is highly
25    # non-random-- particularly if the initial state has many zeros. A consequence of this is that two instances of the
26    # generator, started with initial states that are almost the same, will usually output nearly the same sequence
27    # for many iterations, before eventually diverging."
28    # random.jumpahead(999999)
29
30
31def distance(start, end):
32    """
33    Calculates linear distance between two coordinates.
34    """
35    x1, y1 = start
36    x2, y2 = end
37    return math.hypot(float(x1) - float(x2), float(y1) - float(y2))
38
39
40def report_error(msg):
41    """
42    Handles error messages.
43    """
44    error_list.append(msg)
45    print(msg, file=sys.stderr)
46
47
48def unique_product(first: Iterable[int], second: Iterable[int]) -> Iterator[Tuple[int, int]]:
49    """
50    Create all possible unique pairs for two iterables.
51    Both iterables should consist of comparable objects.
52    Pair is sorted in ascending order.
53    """
54    return filter(lambda x: x[0] < x[1], itertools.product(first, second))
55
56
57class MapGenerationError(RuntimeError):
58    """Map generation runtime error."""
59