File size: 3,875 Bytes
a162e39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import numpy as np
from dataclasses import dataclass

import param_
from settings import Settings
from drone import Drone


@dataclass
class Playground:
    """
    This is a cylindrical 3D-env where blue drones defend a central zone from the attack of red drones
    the playground manages also the interactions between foe-drones such as the firing
    """

    perimeter = Settings.perimeter
    perimeter_z = Settings.perimeter_z
    groundzone = Settings.groundzone

    env: object
    blue_drones: [Drone]
    red_drones: [Drone]

    def __post_init__(self):
        # creates the fire matrices
        self.blues_have_fired_reds = np.zeros(shape=(len(self.blue_drones),
                                                     len(self.red_drones)), dtype=int)
        self.reds_have_fired_blues = np.zeros(shape=(len(self.red_drones),
                                                     len(self.blue_drones)), dtype=int)

        # how long the drone needs to have the other in target
        self.blue_shots_to_kill = param_.DRONE_MODELS[param_.DRONE_MODEL[True]]['duration_to_neutralisation']
        self.red_shots_to_kill = param_.DRONE_MODELS[param_.DRONE_MODEL[False]]['duration_to_neutralisation']
        self.blue_shots_to_kill //= param_.STEP
        self.red_shots_to_kill //= param_.STEP

        # how far can a drone shoot
        self.distance_blue_shot = param_.DRONE_MODELS[param_.DRONE_MODEL[True]]['distance_to_neutralisation']
        self.distance_red_shot = param_.DRONE_MODELS[param_.DRONE_MODEL[False]]['distance_to_neutralisation']

    def reset(self):
        self.blues_have_fired_reds[...] = 0
        self.reds_have_fired_blues[...] = 0

    def get_observation(self):
        return self.blues_have_fired_reds / self.blue_shots_to_kill, \
               self.reds_have_fired_blues / self.red_shots_to_kill

    def step(self):
        """
        determines who has fired who, and who is dead in the end
        :return: Tuple with list of Blue and Reds dead. (if a blue or a red is dead, the sequence is over)
        """
        # gets who has fired who in this step
        blues_fire_reds = np.array([[blue.fires_(red) for red in self.red_drones] for blue in self.blue_drones])
        reds_fire_blues = np.array([[red.fires_(blue) for blue in self.blue_drones] for red in self.red_drones])

        # if the foe is no longer seen, the count restarts from 0
        self.blues_have_fired_reds *= blues_fire_reds
        self.reds_have_fired_blues *= reds_fire_blues

        # and the count is incremented for the others
        self.blues_have_fired_reds += blues_fire_reds
        self.reds_have_fired_blues += reds_fire_blues

        # np magic : first find the list of duos shooter/shot, keep the shots (only once)
        red_deads = np.unique(np.argwhere(self.blues_have_fired_reds >= self.blue_shots_to_kill).T[1])
        blue_deads = np.unique(np.argwhere(self.reds_have_fired_blues >= self.red_shots_to_kill).T[1])


        # tell the drones that they are dead
        for drone_id in blue_deads:
            self.blue_drones[drone_id].is_killed(is_blue=True)
        for drone_id in red_deads:
            self.red_drones[drone_id].is_killed(is_blue=False)

        # consider only living drones
        blue_drones = [drone for drone in self.blue_drones if drone.is_alive]
        red_drones = [drone for drone in self.red_drones if drone.is_alive]

        bf_obs, rf_obs = self.get_observation()
        bf_reward = rf_reward = 0
        remaining_blues, remaining_reds = len(blue_drones), len(red_drones),
        blue_shots, red_shots = len(blue_deads), len(red_deads)

        if blue_shots + red_shots > 0:
            print('someone is killed: {0} blues and {1} reds'.format(blue_shots, red_shots))

        return bf_obs, bf_reward, remaining_blues, blue_shots, rf_obs, rf_reward, remaining_reds, red_shots