DrInfy / sharpy-sc2

Python framework for rapid development of Starcraft 2 AI bots
MIT License
69 stars 28 forks source link

Proposition for extending micro #58

Closed thommath closed 4 years ago

thommath commented 4 years ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

With the current architecture it is no good way to extend or change the micro. One either has create a new class for it with no way of just extending what is currently implemented without overwriting.

Describe the solution you'd like I want an architecture where I can for each unit type add or remove micro solvers without modifying the sharpy library.

Describe alternatives you've considered I propose changing the group_combat_manager to have a list of solvers that are executed in order. These solvers contains the functions group_solve_combat and unit_solve_combat that will be executed in order. The functions return either an Action or None depending if they are triggered or are doing blocking actions like burrow or siege. This way the developer could easily reuse code with many simple solvers or have one complex solver.

To optimize, I suggest that the StepMicro is sent as an additional parameter to the unit_solve_combat so this information is only calculated once for each group.

A function to add and one to overwrite the list assigned by default for each unit_type would make it more developer friendly to use.

I considered modifying MicroStep instead of the manager, but it feel more right to keep it in the manager.

DrInfy commented 4 years ago

As a point of view from history, first iteration of the micro had things like MicroBack, MicroForward, FocusFire and RegroupUnits etc. This is pretty close to what you are suggesting.

Now the problem with that was that it started to get extremely hard to keep track what step in the chain does what and if there is a bug, which of these steps is causing the issues.

In the current version the only and best way to implement custom micro is to replace individual unit micro classes.

i.e.

self.knowledge.group_combat_manager.micros[UnitTypeId.ROACH] = MyRoachMicro()

I haven't written the wiki pages for this, because I'm not happy with this solution. What I am thinking now, is to have a class that contains the basic rules for micro. Let's call it MicroRules. Combat manager would have the current settings as default, but the user could use his own set of Rules by giving them in command execute like this combat_manager.execute(pos, MoveType.Assault, my_micro_rules) Or you could write whatever you'd like in the default Rules. I think it is important for users to be able to temporarily set a different kind of micro for example to your flanking units without the need to mess up with your main army logic.

Thanks for the feedback as that really helps to understand the needs better.

DrInfy commented 4 years ago

Drafting something like this now:

class MicroRules:
    regroup_func: Callable[[Point2, List[CombatUnits], MoveType], None]
    focus_fire_func: Callable[[MicroStep, Unit, Action, Optional[Dict[UnitTypeId, int]]], Action]
    melee_focus_fire_func: Callable[[MicroStep, Unit, Action, Optional[Dict[UnitTypeId, int]]], Action]
    ready_to_shoot_func: Callable[[MicroStep, Unit], bool]
    init_group_func: Callable[[MicroStep, CombatUnits, Units, List[CombatUnits], MoveType], None]

    def __init__(self) -> None:
        self.regroup = True
        self.unit_micros: Dict[UnitTypeId, MicroStep] = dict()
        self.regroup_threshold = 0.75
        # How much distance must be between units to consider them to be in different groups, set to 0 for no grouping
        self.own_group_threshold = 7