mirror of
				https://github.com/MarioSpore/Grinch-AP.git
				synced 2025-10-21 20:21:32 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			194 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from abc import ABC
 | |
| from pathlib import Path
 | |
| from typing import List, Union
 | |
| 
 | |
| from .bot_ai import BotAI
 | |
| from .data import AIBuild, Difficulty, PlayerType, Race
 | |
| 
 | |
| 
 | |
| class AbstractPlayer(ABC):
 | |
| 
 | |
|     def __init__(
 | |
|         self,
 | |
|         p_type: PlayerType,
 | |
|         race: Race = None,
 | |
|         name: str = None,
 | |
|         difficulty=None,
 | |
|         ai_build=None,
 | |
|         fullscreen=False
 | |
|     ):
 | |
|         assert isinstance(p_type, PlayerType), f"p_type is of type {type(p_type)}"
 | |
|         assert name is None or isinstance(name, str), f"name is of type {type(name)}"
 | |
| 
 | |
|         self.name = name
 | |
|         self.type = p_type
 | |
|         self.fullscreen = fullscreen
 | |
|         if race is not None:
 | |
|             self.race = race
 | |
|         if p_type == PlayerType.Computer:
 | |
|             assert isinstance(difficulty, Difficulty), f"difficulty is of type {type(difficulty)}"
 | |
|             # Workaround, proto information does not carry ai_build info
 | |
|             # We cant set that in the Player classmethod
 | |
|             assert ai_build is None or isinstance(ai_build, AIBuild), f"ai_build is of type {type(ai_build)}"
 | |
|             self.difficulty = difficulty
 | |
|             self.ai_build = ai_build
 | |
| 
 | |
|         elif p_type == PlayerType.Observer:
 | |
|             assert race is None
 | |
|             assert difficulty is None
 | |
|             assert ai_build is None
 | |
| 
 | |
|         else:
 | |
|             assert isinstance(race, Race), f"race is of type {type(race)}"
 | |
|             assert difficulty is None
 | |
|             assert ai_build is None
 | |
| 
 | |
|     @property
 | |
|     def needs_sc2(self):
 | |
|         return not isinstance(self, Computer)
 | |
| 
 | |
| 
 | |
| class Human(AbstractPlayer):
 | |
| 
 | |
|     def __init__(self, race, name=None, fullscreen=False):
 | |
|         super().__init__(PlayerType.Participant, race, name=name, fullscreen=fullscreen)
 | |
| 
 | |
|     def __str__(self):
 | |
|         if self.name is not None:
 | |
|             return f"Human({self.race._name_}, name={self.name !r})"
 | |
|         return f"Human({self.race._name_})"
 | |
| 
 | |
| 
 | |
| class Bot(AbstractPlayer):
 | |
| 
 | |
|     def __init__(self, race, ai, name=None, fullscreen=False):
 | |
|         """
 | |
|         AI can be None if this player object is just used to inform the
 | |
|         server about player types.
 | |
|         """
 | |
|         assert isinstance(ai, BotAI) or ai is None, f"ai is of type {type(ai)}, inherit BotAI from bot_ai.py"
 | |
|         super().__init__(PlayerType.Participant, race, name=name, fullscreen=fullscreen)
 | |
|         self.ai = ai
 | |
| 
 | |
|     def __str__(self):
 | |
|         if self.name is not None:
 | |
|             return f"Bot {self.ai.__class__.__name__}({self.race._name_}), name={self.name !r})"
 | |
|         return f"Bot {self.ai.__class__.__name__}({self.race._name_})"
 | |
| 
 | |
| 
 | |
| class Computer(AbstractPlayer):
 | |
| 
 | |
|     def __init__(self, race, difficulty=Difficulty.Easy, ai_build=AIBuild.RandomBuild):
 | |
|         super().__init__(PlayerType.Computer, race, difficulty=difficulty, ai_build=ai_build)
 | |
| 
 | |
|     def __str__(self):
 | |
|         return f"Computer {self.difficulty._name_}({self.race._name_}, {self.ai_build.name})"
 | |
| 
 | |
| 
 | |
| class Observer(AbstractPlayer):
 | |
| 
 | |
|     def __init__(self):
 | |
|         super().__init__(PlayerType.Observer)
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "Observer"
 | |
| 
 | |
| 
 | |
| class Player(AbstractPlayer):
 | |
| 
 | |
|     def __init__(self, player_id, p_type, requested_race, difficulty=None, actual_race=None, name=None, ai_build=None):
 | |
|         super().__init__(p_type, requested_race, difficulty=difficulty, name=name, ai_build=ai_build)
 | |
|         self.id: int = player_id
 | |
|         self.actual_race: Race = actual_race
 | |
| 
 | |
|     @classmethod
 | |
|     def from_proto(cls, proto):
 | |
|         if PlayerType(proto.type) == PlayerType.Observer:
 | |
|             return cls(proto.player_id, PlayerType(proto.type), None, None, None)
 | |
|         return cls(
 | |
|             proto.player_id,
 | |
|             PlayerType(proto.type),
 | |
|             Race(proto.race_requested),
 | |
|             Difficulty(proto.difficulty) if proto.HasField("difficulty") else None,
 | |
|             Race(proto.race_actual) if proto.HasField("race_actual") else None,
 | |
|             proto.player_name if proto.HasField("player_name") else None,
 | |
|         )
 | |
| 
 | |
| 
 | |
| class BotProcess(AbstractPlayer):
 | |
|     """
 | |
|     Class for handling bots launched externally, including non-python bots.
 | |
|     Default parameters comply with sc2ai and aiarena ladders.
 | |
| 
 | |
|     :param path: the executable file's path
 | |
|     :param launch_list: list of strings that launches the bot e.g. ["python", "run.py"] or ["run.exe"]
 | |
|     :param race: bot's race
 | |
|     :param name: bot's name
 | |
|     :param sc2port_arg: the accepted argument name for the port of the sc2 instance to listen to
 | |
|     :param hostaddress_arg: the accepted argument name for the address of the sc2 instance to listen to
 | |
|     :param match_arg: the accepted argument name for the starting port to generate a portconfig from
 | |
|     :param realtime_arg: the accepted argument name for specifying realtime
 | |
|     :param other_args: anything else that is needed
 | |
| 
 | |
|     e.g. to call a bot capable of running on the bot ladders:
 | |
|         BotProcess(os.getcwd(), "python run.py", Race.Terran, "INnoVation")
 | |
|     """
 | |
| 
 | |
|     def __init__(
 | |
|         self,
 | |
|         path: Union[str, Path],
 | |
|         launch_list: List[str],
 | |
|         race: Race,
 | |
|         name=None,
 | |
|         sc2port_arg="--GamePort",
 | |
|         hostaddress_arg="--LadderServer",
 | |
|         match_arg="--StartPort",
 | |
|         realtime_arg="--RealTime",
 | |
|         other_args: str = None,
 | |
|         stdout: str = None,
 | |
|     ):
 | |
|         super().__init__(PlayerType.Participant, race, name=name)
 | |
|         assert Path(path).exists()
 | |
|         self.path = path
 | |
|         self.launch_list = launch_list
 | |
|         self.sc2port_arg = sc2port_arg
 | |
|         self.match_arg = match_arg
 | |
|         self.hostaddress_arg = hostaddress_arg
 | |
|         self.realtime_arg = realtime_arg
 | |
|         self.other_args = other_args
 | |
|         self.stdout = stdout
 | |
| 
 | |
|     def __repr__(self):
 | |
|         if self.name is not None:
 | |
|             return f"Bot {self.name}({self.race.name} from {self.launch_list})"
 | |
|         return f"Bot({self.race.name} from {self.launch_list})"
 | |
| 
 | |
|     def cmd_line(self,
 | |
|                  sc2port: Union[int, str],
 | |
|                  matchport: Union[int, str],
 | |
|                  hostaddress: str,
 | |
|                  realtime: bool = False) -> List[str]:
 | |
|         """
 | |
| 
 | |
|         :param sc2port: the port that the launched sc2 instance listens to
 | |
|         :param matchport: some starting port that both bots use to generate identical portconfigs.
 | |
|                 Note: This will not be sent if playing vs computer
 | |
|         :param hostaddress: the address the sc2 instances used
 | |
|         :param realtime: 1 or 0, indicating whether the match is played in realtime or not
 | |
|         :return: string that will be used to start the bot's process
 | |
|         """
 | |
|         cmd_line = [
 | |
|             *self.launch_list,
 | |
|             self.sc2port_arg,
 | |
|             str(sc2port),
 | |
|             self.hostaddress_arg,
 | |
|             hostaddress,
 | |
|         ]
 | |
|         if matchport is not None:
 | |
|             cmd_line.extend([self.match_arg, str(matchport)])
 | |
|         if self.other_args is not None:
 | |
|             cmd_line.append(self.other_args)
 | |
|         if realtime:
 | |
|             cmd_line.extend([self.realtime_arg])
 | |
|         return cmd_line
 | 
