about summary refs log tree commit diff homepage
path: root/client-examples/hit-and-run.py
blob: 09dc7fa75b7014fb96b8c6c07d43639939660ec1 (plain) (blame)
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
69
70
71
72
#!/usr/bin/env python3
from contextlib import closing, suppress
from math import atan2, degrees, inf
from random import randrange, shuffle
from socket import socket

AROUND = [5, 2, 1, 0, 3, 6, 7, 8]


def get_moves(y, x):
    """Return tuple of encoded moves."""
    return ((y - 1, x - 1), (y - 1, x), (y - 1, x + 1),     # noqa
            (y,     x - 1), (y,     x), (y,     x + 1),     # noqa
            (y + 1, x - 1), (y + 1, x), (y + 1, x + 1))     # noqa


def is_wall(maze, y, x):
    """Return weather the cell (x, y) is wall."""
    return maze[y][x] != '0'


def get_move(maze, move):
    """Return an outstanding move."""
    moves, around = get_moves(len(maze) // 2, len(maze[0]) // 2), AROUND[:]
    if move != 4 and not is_wall(maze, *moves[move]): return move
    if move == 4:
        shuffle(around)
    else:
        idx = AROUND.index(move)
        around.sort(key=lambda i: abs(abs(abs(AROUND.index(i)-idx)-4)-4))
    for move in around:
        idx = AROUND.index(move)
        if all(not is_wall(maze, *moves[i])
               for i in (move, AROUND[idx - 1], AROUND[idx - 7])):
            return move
    return 4


with suppress(KeyboardInterrupt), closing(socket()) as sock:
    sock.connect(('localhost', 42069))
    move = 4
    while True:
        length = sock.recv(7).decode()
        # connection closed or game over
        if length in ('', '0000000'): break
        data = iter(sock.recv(int(length)).decode().split())
        nh, ne, nb, score = (int(next(data)) for i in range(4))
        maze = [list(next(data)) for i in range(nh)]
        hp = (lambda c: 0 if c == 48 else 123 - c)(ord(next(data)))
        hx, hy, ha = (int(next(data)) for i in range(3))
        attackable, heal = (bool(int(next(data))) for i in range(2))

        if nh: move = get_move(maze, move)
        angle, shortest = ha, inf
        for i in range(ne):
            p = 3 - (ord(next(data)) - 97)%3
            x, y, a = (int(next(data)) for j in range(3))
            d = ((x - hx)**2 + (y - hy)**2)**0.5
            if d < shortest:
                shortest = d
                b = degrees(atan2(y - hy, x - hx))
                angle = round(b + 360 if b < 0 else b)

        if hp <= 2 and heal:
            move, attack = 4, 2
        elif not ne:
            attack = randrange(3) * (attackable and hp > 2)
        elif shortest < 160:
            move, angle, attack = AROUND[round(angle/45 - 0.5) - 4], ha, 2
        else:
            attack = 1
        sock.send(f'{move} {angle} {attack}'.encode())