├── .gitignore ├── .idea └── .gitignore ├── DUC_test.py ├── Functions.py ├── README.md ├── ai_parser.py ├── ai_writer.py ├── archive ├── AlphaScripter.36.py ├── actions.txt ├── alpha.PNG ├── bar.png ├── conditions.txt ├── launch_okay.PNG ├── leave.png ├── main_menu.PNG ├── ok.PNG ├── open_menu.PNG ├── play_again.PNG ├── player_1.PNG ├── quit.PNG ├── readme.txt ├── single_player.PNG ├── skirmish.PNG ├── start_game.PNG ├── tech.PNG ├── try.png ├── won.png └── yes.PNG ├── best.txt ├── edittable.csv ├── elo.py ├── game_launcher.py ├── gui.py ├── main.py ├── params.csv ├── resign.txt └── settings.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/AlphaScripter.iml 3 | .idea/inspectionProfiles/profiles_settings.xml 4 | .idea/inspectionProfiles/Project_Default.xml 5 | .idea/misc.xml 6 | .idea/modules.xml 7 | .idea/vcs.xml 8 | __pycache__/ai_parser.cpython-39.pyc 9 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /DUC_test.py: -------------------------------------------------------------------------------- 1 | from Functions import * 2 | from main import * 3 | 4 | #get_single_ai_data(['huns']*2,'best',list(eloDict.keys()),eloDict,3) 5 | 6 | #benchmarker("best","Shadow 3.1",7,['huns','huns']) 7 | 8 | #ai = read_ai("best") 9 | 10 | ai = generate_ai() 11 | 12 | ab = mutate_ai(ai,.05) 13 | ab = crossover(ai,ab,.05) 14 | write_ai(ab,"test") 15 | save_ai(ab,"test") 16 | 17 | #benchmarker("test","Shadow 3.1",7,['huns','huns']) 18 | 19 | #from elosports.elo import Elo 20 | #eloLeague = Elo(k= 10, g = 1) 21 | # 22 | #working_ais = ['Tlamacazqui','Wartron','Cyan','Eidolon','Odette_AI','Maximus 2_2a','RehoboamUP50%','Goths92602','Viper','Anti_TRiBaL','BTGBestStrats','Crusade 4.42c','Rhapsody0004','Ace','Ahulane UP','AllianceThundaEmpire','Alphav4','Arabian_Knight',"ARFFI-De'gel_ver_1_65",'ARFFI_05_107_50_rules','BambiV030','BambiV030dot2','best','Binary','BooM II','Boss_321','BruteForce3.1','Chameleon','CPS_Alexander','Cyan','Demon','Esty the TutoMaster','Faradol','fire2.3 DC-Final-#01','GamesGod','Grasshopper007','Grasshopper008AOC','II2N Rattlehead','Illuminati','Immortal v0.9c (beta)','IMP_CAES_MIRO III Azt_Teut','IS_UPMachine','John_Mendl','Juggernaut','Junior','Kosmos3.00beta2','Leif Ericson 1.32','Maiar','Miggins','Mini Eel 1.7','Mininati','Mininaut 1.4','No Limits','Pantheon','Pharaon DE','Phyrexx UP','Promi','PUMA Dreadnought','PUMA Lade13','PUMA Promi','PUMA Snake BF','rAge__(fixed)','Ranger 3.04','Rattlehead','Reactionaryv19 UP','Shadow 4.11','Snake #8.5','Subjugator','Ternary','The General','The Horde','best','The Khanate','Thermopylai v1.3','The_Unknown3','Tlamacazqui','Torisan_bc','TRiBaL_Warrior','TRON','UlyssesWK','UnfairSteel','Valkyrie Warriors','VetrixDE Final','Vicky','VNS_Chris_Tournament','Yggdrasil'] 23 | # 24 | #for x in range(len(working_ais)): 25 | # eloLeague.addPlayer(working_ais[x], rating = 1600) 26 | # 27 | #f = open("data.csv","r") 28 | #data = f.read().split("\n") 29 | #f.close() 30 | # 31 | #for i in range(1,len(data)-1): 32 | # line = data[i].split(",") 33 | # AI = line[0] 34 | # opponent = line[6] 35 | # result = line[2] 36 | # 37 | # if result == "win": 38 | # eloLeague.gameOver(winner = AI, loser = opponent,winnerHome = False) 39 | # elif result == "loss": 40 | # eloLeague.gameOver(winner = opponent, loser = AI,winnerHome = False) 41 | # 42 | #for entry in eloLeague.ratingDict: 43 | # print(entry + "," + str(eloLeague.ratingDict[entry])) 44 | 45 | 46 | #ai = generate_ai() 47 | #write_ai(ai,"best") 48 | #save_ai(ai,"best") 49 | # 50 | #while True: 51 | # DUC_search = [] 52 | # DUC_target = [] 53 | # for i in range(5): 54 | # DUC_search.append(generate_DUC_search()) 55 | # DUC_target.append(generate_DUC_target()) 56 | # 57 | # ai = read_ai("best") 58 | # ai[3] = DUC_search 59 | # ai[4] = DUC_target 60 | # write_ai(ai,"test") 61 | # 62 | # wins = basic_benchmarker("test", "BambiV030", 7, ['huns']*2) 63 | # print(wins) 64 | # if wins > 2: 65 | # save_ai(ai,str(wins)) 66 | # write_ai(ai,str(wins)) 67 | # break 68 | # 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlphaScripter 2 | Use the power of genetic algorithms to evolve AI scripts for Age of Empires II. 3 | For now this package runs in AOC Userpatch 1.5 to train. In theory, scripts generated this way should be 4 | compatible with Age of Empires II : Definitive Edition, but in practice a certain amount of porting will need to 5 | take place. This is currently a work in progress. 6 | 7 | ## Dependencies 8 | - `msgpackrpc >= 0.4.1` : A package used to communicate with running AOE processes. (To install, `pip install msgpack-rpc-python`) 9 | - `psutil >= 5.8.0` : A package used to manage running processes. 10 | - `tornado == 4.3.5` : Should be automatically installed with `msgpackrpc`. 11 | 12 | ## How to install and run 13 | 1. Install **32-bit** (!) Python (tested with version 3.9) and the dependencies listed above. 14 | 2. Install Age of Empires II - The Conquerors and install UserPatch 1.5. The UserPatch can be found here: 15 | https://userpatch.aiscripters.net/ 16 | 3. Download the `aoc-auto-game.dll` and paste it in the same folder as the AOC executable. You can download 17 | this DLL file [here.](https://github.com/FLWL/aoc-auto-game/releases/download/v1.15/aoc-auto-game.dll) 18 | 4. Open the Python file `Main.py`, adjust parameters and run the script. (WIP) 19 | 20 | ## What does it do? - Explanation per script 21 | ### The Main Script `Gui.py` 22 | On run, a GUI will open with various options. To begin, I suggest running "FFA new." It will generate a functioning AI and then evolve it based on score against 7 mutations of the parent. 23 | 24 | I suggest checking settings.py and adjusting it as well. Many of the settings are self-explanatory, but please reach out with any questions. 25 | 26 | The best script so far will be saved as "best.per" in the .ai directory. It will be overwritten if you restart the script. 27 | 28 | I will expand this later -- if you would like to help with this project, you can find me on the AI scripters discord 29 | for aoe2 de and dm me. 30 | I specifically need help from those knowledgeable about scripting or engine modification. 31 | 32 | Run types: 33 | You can run vs, run score, or run FFA. 34 | 35 | run vs: 36 | load alpha into p1 and beta into p2. 37 | Game will pick winner as new parent - this is a good adversarial AI but is best for late stage training once the AI is 38 | good enough to possibly defeat another player 39 | 40 | run score: 41 | load training AI (HD, barbarian, extreme) into p1 and alpha into p2 42 | Game will pick all-time-highest scorers as new parent. 43 | 44 | run FFA: 45 | load alpha-h into slots 1-8, make sure no teams are selected 46 | Game will pick two best in each round and crossover their traits. Very good for fast training early on. 47 | 48 | ### Game Launcher `game_launcher.py` 49 | The game launcher is used to, surprise, launch games. To successfully launch a game, the class `Launcher` 50 | needs to be instantiated. This will take 1 optional argument: `path` that specifies the path to the AOC executable. 51 | If you don't pass this argument, the game will look for the executable in the default path: 52 | `C:\Program Files\Microsoft Games\age of empires ii\Age2_x1\age2_x1.exe`. If the executable cannot be found, 53 | the launcher will raise an Exception. 54 | 55 | After correctly instantiating this `Launcher`, you can launch a game using the `Launcher.launch_game` function. 56 | This function takes 2 required and 3 optional arguments: 57 | - (Required) `names : list[str]` - This is a list of strings that represent the names of the AI .per files that the game will 58 | look for when starting the game. These should be in the `..\age of empires ii\ai` folder. 59 | - (Required) `game_settings` - This should be an instance of the `GameSettings` class, which is also declared in 60 | `game_launcher.py`. 61 | - (Optional) `real_time_limit : int` - The number of real time seconds after which the game(s) should be automatically 62 | quit and closed. If not given, there will not be a real-time limit. 63 | - (Optional) `game_time_limit : int` - The number of in-game seconds after the game(s) should be quit and closed. 64 | - (Optional) `instances : int` - The number of games to run simultaneously. This currently a little experimental, so 65 | keep in mind that your mileage with this setting. 66 | 67 | The `GameSettings` class is used to store settings for the game. To instantiate a `GameSettings` object, you will have 68 | to pass 1 required argument and a lot of optional arguments. See below for a description. 69 | - (Required) `civilisations` - A list of strings or int (can also be mixed) that represent the civs that the AI players 70 | will use. It must be the same length as the `names` given to the launcher, otherwise the launcher will raise an 71 | Exception. If an element in the given list is not a valid civ, that element will be replaced by `'huns'`. 72 | - (Optional) `map_id` - A string or int that represents the map. Default `'arabia'` 73 | - (Optional) `map_size` - A string or int that represent the map size. Default `'medium'` 74 | - (Optional) `difficulty` - A string or int that represents the difficulty of the game. Default `'hard'` 75 | - (Optional) `game_type` - Specifies the game type. Default `'random_map'` 76 | - (Optional) `resources` - Specifies the starting resources of each player. Default `'low'` 77 | - (Optional) `reveal_map` - Whether the map should be revealed. Default `'normal'` 78 | - (Optional) `starting_age` - Which age the players should start in. Default `'dark'` 79 | - (Optional) `victory_type` - The victory type. Doesn't work (yet). Default `'conquest'` 80 | -------------------------------------------------------------------------------- /ai_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import pickle 4 | from dataclasses import dataclass, field 5 | 6 | fact_names = {'attack-soldier-count', 'building-available', 'building-count', 'building-count-total', 7 | 'building-type-count', 'building-type-count-total', 'can-afford-building', 'can-afford-complete-wall', 8 | 'can-afford-research', 'can-afford-unit', 'can-build', 'can-build-with-escrow', 'can-build-gate', 9 | 'can-build-gate-with-escrow', 'can-build-wall', 'can-build-wall-with-escrow', 'can-buy-commodity', 10 | 'can-research', 'can-research-with-escrow', 'can-sell-commodity', 'can-spy', 'can-spy-with-escrow', 11 | 'can-train', 'can-train-with-escrow', 'civilian-population', 'civ-selected', 'commodity-buying-price', 12 | 'commodity-selling-price', 'current-age', 'current-age-time', 'current-score', 'death-match-game', 13 | 'defend-soldier-count', 'defend-warboat-count', 'difficulty', 'doctrine', 'dropsite-min-distance', 14 | 'enemy-buildings-in-town', 'enemy-captured-relics', 'escrow-amount', 'event-detected', 'FALSE', 15 | 'food-amount', 'game-time', 'game-type', 'gate-count', 'goal', 'gold-amount', 'hold-koh-ruin', 16 | 'hold-relics', 'housing-headroom', 'idle-farm-count', 'map-size', 'map-type', 'military-population', 17 | 'player-computer', 'player-human', 'player-in-game', 'player-number', 'player-resigned', 'player-valid', 18 | 'players-building-count', 'players-building-type-count', 'players-civ', 'players-civilian-population', 19 | 'players-current-age', 'players-current-age-time', 'players-military-population', 'players-population', 20 | 'players-score', 'players-stance', 'players-unit-count', 'players-unit-type-count', 'population', 21 | 'population-cap', 'population-headroom', 'random-number', 'research-available', 'research-completed', 22 | 'resource-found', 'shared-goal', 'sheep-and-forage-too-far', 'soldier-count', 'stance-toward', 23 | 'starting-age', 'starting-resources', 'stone-amount', 'strategic-number', 'taunt-detected', 24 | 'timer-triggered', 'town-under-attack', 'TRUE', 'unit-available', 'unit-count', 'unit-count-total', 25 | 'unit-type-count', 'unit-type-count-total', 'victory-condition', 'wall-completed-percentage', 26 | 'wall-invisible-percentage', 'warboat-count', 'wood-amount', 'up-add-object-by-id', 'up-allied-goal', 27 | 'up-allied-resource-amount', 'up-allied-sn', 'up-attacker-class', 'up-building-type-in-town', 28 | 'up-can-build', 'up-can-build-line', 'up-can-research', 'up-can-search', 'up-can-train', 29 | 'up-compare-const', 'up-compare-flag', 'up-compare-goal', 'up-compare-sn', 'up-compare-text', 30 | 'up-defender-count', 'up-enemy-buildings-in-town', 'up-enemy-units-in-town', 'up-enemy-villagers-in-town', 31 | 'up-find-remote', 'up-find-resource', 'up-find-status-local', 'up-find-status-remote', 32 | 'up-gaia-type-count', 'up-gaia-type-count-total', 'up-get-fact', 'up-get-fact-max', 'up-get-fact-min', 33 | 'up-get-fact-sum', 'up-get-focus-fact', 'up-get-object-data', 'up-get-object-target-data', 34 | 'up-get-player-fact', 'up-get-point-contains', 'up-get-target-fact', 'up-group-size', 35 | 'up-idle-unit-count', 'up-modify-goal', 'up-object-data', 'up-object-target-data', 'up-object-type-count', 36 | 'up-object-type-count-total', 'up-path-distance', 'up-pending-objects', 'up-pending-placement', 37 | 'up-player-distance', 'up-players-in-game', 'up-point-contains', 'up-point-distance', 38 | 'up-point-elevation', 'up-point-explored', 'up-point-terrain', 'up-point-zone', 'up-projectile-detected', 39 | 'up-projectile-target', 'up-remaining-boar-amount', 'up-research-status', 'up-resource-amount', 40 | 'up-resource-percent', 'up-set-target-by-id', 'up-set-target-object', 'up-timer-status', 41 | 'up-train-site-ready', 'up-unit-type-in-town', 'up-villager-type-in-town', 'up-allied-resource-percent', 42 | 'up-find-local'} 43 | 44 | 45 | # ========== Utility Functions ============ 46 | 47 | def dir_exists(path: str, raise_exception: bool = False) -> bool: 48 | if os.path.isdir(path): 49 | return True 50 | elif raise_exception: 51 | raise Exception(f"Directory {path} does not exist!") 52 | return False 53 | 54 | 55 | def file_exists(path: str, raise_exception: bool = False) -> bool: 56 | if os.path.isfile(path): 57 | return True 58 | elif raise_exception: 59 | raise Exception(f"File {path} does not exist!") 60 | return False 61 | 62 | 63 | def write_to_file(path: str, contents: str, create_if_not_exists=False, append=False): 64 | if not create_if_not_exists and not os.path.isfile(path): 65 | print(f"File writing failed: {path} does not exist!") 66 | mode = "a" if append else "w+" 67 | with open(path, mode) as file: 68 | file.write(contents) 69 | 70 | 71 | def read_file_raw(path: str) -> str: 72 | file_exists(path, raise_exception=True) 73 | with open(path, mode="r") as file: 74 | return file.read() 75 | 76 | 77 | def str_to_int(string: str, allow_negative: bool = True) -> tuple: 78 | if string.isdigit(): 79 | return True, int(string) 80 | if allow_negative and string.startswith("-"): 81 | return True, -int(string[1:]) 82 | # print(f"String '{string}' cannot be converted to int, returning the string instead.") 83 | return False, string 84 | 85 | 86 | def inside_outer_parentheses(string: str): 87 | first_parenthesis_index = string.find("(") + 1 88 | last_parenthesis_index = string.rfind(")") 89 | return string[first_parenthesis_index:last_parenthesis_index] 90 | 91 | 92 | # ========================================= 93 | 94 | operators = {"and": "&", "or": "|", "not": '#', "nand": '$', "nor": '@'} 95 | 96 | 97 | @dataclass 98 | class FactBase: 99 | name: str # For a simple, this the name of the fact. For a Complex, this is an operator. 100 | depth: int = field(init=False, default=0) 101 | 102 | @property 103 | def is_simple(self) -> bool: 104 | return self.name not in operators 105 | 106 | @property 107 | def is_complex(self) -> bool: 108 | return not self.is_simple 109 | 110 | def set_depth(self, value: int): 111 | self.depth = value 112 | 113 | 114 | @dataclass 115 | class Simple(FactBase): 116 | params: list 117 | length: int 118 | relevant_length: int 119 | 120 | def __init__(self, params, *args): 121 | if isinstance(params, str): 122 | self.name = params 123 | if args: 124 | self.params = self.__get_params(args) 125 | if isinstance(params, (list, tuple)): 126 | if len(params) > 5: 127 | raise Exception("A Simple only take a name and a maximum of 4 arguments.") 128 | self.name = params[0] 129 | self.params = self.__get_params(list(params)[1:]) 130 | 131 | if self.params: 132 | self.length = self.relevant_length = len(params) 133 | 134 | self.depth = 0 135 | 136 | @staticmethod 137 | def __get_params(params: list) -> list: 138 | if params is None: 139 | return None 140 | # If the params passed through are a list or tuple, we just use that one. 141 | if isinstance(params, list) or isinstance(params, tuple) or isinstance(params, set): 142 | if len(params) > 4: 143 | raise Exception("Cannot create Simple with more than 4 parameters!") 144 | return [str(x) for x in params] 145 | raise Exception("Arguments passed to the Simple init should be either a single list or multiple int and string") 146 | 147 | def __str__(self): 148 | string = f"({self.name} " 149 | if self.params: 150 | string += ' '.join(self.params[:self.relevant_length]) 151 | 152 | return string + ")" 153 | 154 | def __len__(self): 155 | return self.length 156 | 157 | 158 | @dataclass 159 | class Complex(FactBase): 160 | param1: FactBase 161 | param2: FactBase = None 162 | 163 | def __init__(self, name: str, param1: FactBase, param2: FactBase = None): 164 | self.name = name 165 | self.param1 = param1 166 | self.param2 = param2 167 | self.set_depth(0) # Set the depths recursively 168 | 169 | def __str__(self): 170 | name_tabs = '\t' * (1 if len(self.name) > 2 else 2) 171 | result = f"({self.name}{name_tabs}{self.param1}" 172 | if self.param2: 173 | tabs = '\t' * (self.param2.depth * 2 + 1) # The plus 1 is because every rule has every line indented 1 tab 174 | result += f"\n{tabs}{self.param2}" 175 | return result + ")" 176 | 177 | def set_depth(self, value: int): 178 | self.depth = value 179 | self.param1.set_depth(value + 1) 180 | if self.param2: 181 | self.param2.set_depth(value + 1) 182 | 183 | 184 | @dataclass 185 | class Rule: 186 | facts: list[FactBase] 187 | primary_facts: list[FactBase] 188 | actions: list[Simple] 189 | actions_length: int 190 | comment_unused: bool # If this is True, during printing, this rule will comment out the 'unused' actions and facts 191 | 192 | def __init__(self, facts: list[FactBase], actions: list, facts_length: int = None, actions_length: int = None, 193 | comment_unused: bool = False): 194 | if not facts: 195 | raise Exception("Cannot create a rule without any facts!") 196 | elif not actions: 197 | raise Exception("Cannot create a rule without any actions!") 198 | 199 | self.facts = facts 200 | self.primary_facts = self.get_facts(depth=0) 201 | self.actions = actions 202 | 203 | if facts_length is not None and facts_length < 0: 204 | print(f"Warning! Facts length cannot be smaller than zero. Defaulting to None.") 205 | facts_length = None 206 | 207 | if actions_length is not None and (actions_length < 0 or actions_length > 4): 208 | print(f"Warning! Action length cannot be smaller than zero or greater than 4.") 209 | actions_length = None 210 | 211 | self.facts_length = facts_length if facts_length is not None else len(self.facts) 212 | self.actions_length = actions_length if actions_length is not None else len(self.actions) 213 | self.comment_unused = comment_unused 214 | 215 | def get_facts(self, depth: int = None): 216 | if depth is None: 217 | return self.facts[:] 218 | elif depth == 1: 219 | return self.primary_facts[:] 220 | return [fact for fact in self.facts if fact.depth == depth] 221 | 222 | def __str__(self): 223 | string = "(defrule\n" 224 | 225 | if self.comment_unused: 226 | for index, fact in enumerate(self.facts): 227 | string += f"{index:{';' if index >= self.facts_length else ''}\t}{fact}\n" 228 | string += "=>\n" 229 | for index, action in enumerate(self.actions): 230 | string += f"{index:{';' if index >= self.actions_length else ''}\t}{action}\n" 231 | else: 232 | for fact in self.primary_facts[:self.facts_length]: 233 | string += f"\t{fact}\n" 234 | string += "=>\n" 235 | for action in self.actions[:self.actions_length]: 236 | string += f"\t{action}\n" 237 | 238 | string += ")" 239 | return string 240 | 241 | def __repr__(self): 242 | return str(self) 243 | 244 | 245 | class AIParser: 246 | @staticmethod 247 | def read_single(path: str = "C:\\Program Files\\Microsoft Games\\age of empires ii\\Ai\\Alpha.per", 248 | raise_exception: bool = True): 249 | """ 250 | Read a single .per file and return the AI. 251 | 252 | :param path: The path to the .per file. 253 | :param raise_exception: Whether to raise Exceptions if for example a file cannot be found. 254 | :return: An instance of the AI class if found, else None. 255 | """ 256 | 257 | if os.path.isfile(path): 258 | if path.endswith(".per"): 259 | return AI(path=path) 260 | elif path.endswith(".pickle"): 261 | ai = open(path, "rb") 262 | return pickle.load(ai) 263 | elif raise_exception: 264 | raise Exception(f"Cannot read from {path}. The file is not a .per file.") 265 | elif raise_exception: 266 | raise Exception(f"Cannot read from {path}. No file found at that path.") 267 | return None 268 | 269 | @staticmethod 270 | def read_multiple(path: str, names: set[str] = None, as_dict: bool = True): 271 | """ 272 | Read multiple AI .per files in a directory and return a list containing the found AI's. 273 | 274 | :param path: The path to the directory containing the .per files. 275 | :param names: An optional set of names that will act as a filter when reading AIs. 276 | :param as_dict: Whether to return the result as a dictionary. If False, returns the AI's in a list instead. 277 | :return: A dict (keys are AI names) or list containing all the AIs, depending on the value of 'as_dict'. 278 | """ 279 | 280 | if not os.path.isdir(path): 281 | raise Exception(f"The path {path} does not exist or is not a directory.") 282 | 283 | result = dict() if as_dict else [] 284 | found = set() 285 | for file in os.listdir(path): 286 | 287 | if file.endswith(".per"): 288 | name = file.removesuffix(".per") 289 | elif file.endswith(".pickle"): 290 | name = file.removesuffix(".pickle") 291 | else: 292 | continue 293 | 294 | if names and name not in names: 295 | continue 296 | ai = AIParser.read_single(os.path.join(path, file), raise_exception=False) 297 | if ai: 298 | if as_dict: 299 | result[name] = ai 300 | else: 301 | result.append(ai) 302 | found.add(name) 303 | 304 | if len(found) != len(names): 305 | print(f"We did not find all the AI's you were looking for: \n Query={names} \n Found={found}.") 306 | 307 | return result 308 | 309 | @staticmethod 310 | def write_single(ai, target_directory, pickled: bool = True) -> bool: 311 | if not dir_exists(target_directory, raise_exception=True) or not isinstance(ai, AI): 312 | print(f"Warning! Writing AI {ai} failed.") 313 | return False 314 | if pickled: 315 | pickle.dump(ai, os.path.join(target_directory, ai.name + ".pickle")) 316 | return True 317 | else: 318 | ai.write() 319 | 320 | 321 | class AI: 322 | def __init__(self, path: str): 323 | self.path = self.__repair_path(path) # The path to the AI .per file 324 | # The directory in which this AI's .per file is located & The name of this AI. 325 | self.parent_directory, self.name = os.path.split(path) 326 | self.name = self.name.removesuffix(".per") 327 | # Whether this AI is visible in the selection dropdown in-game 328 | self.visible = os.path.isfile(os.path.join(self.parent_directory, (self.name + ".ai"))) 329 | self.raw_content = read_file_raw(path) 330 | self.simple_indicator = '*' 331 | self.complex_indicator = '%' 332 | self.operators = {"and": "&", "or": "|", "not": '#', "nand": '$', "nor": '@'} 333 | self.operators_inverse = dict() 334 | for operator_name, operator_symbol in self.operators.items(): 335 | self.operators_inverse[operator_symbol] = operator_name 336 | 337 | self.constants, self.rules = self._parse_raw_content(content=self.raw_content) 338 | x = 0 339 | 340 | @staticmethod 341 | def __repair_path(path: str) -> str: 342 | if os.path.isfile(path) and path.endswith(".per"): 343 | return path 344 | raise Exception(f"Cannot instance AI with incorrect path {path}. \n There could be a few reasons for this. \n" 345 | f"1. The path specified does not reference to a valid location / file. \n" 346 | f"2. The path specified does not reference to a .per file but some type of file. \n" 347 | f"3. Between pickling and unpickling this AI, the corresponding .per file has been deleted.") 348 | 349 | def _parse_raw_content(self, content): 350 | constants = dict() 351 | current_rule_lines = None 352 | rules = [] 353 | 354 | for line in content.split("\n"): 355 | line = line.strip() 356 | 357 | comment_index = line.find(";") 358 | if comment_index == 0: # This line is a comment. 359 | continue 360 | elif comment_index > 0: # This line contains a comment. 361 | line = line[:comment_index].strip() 362 | 363 | # Skip empty lines. 364 | if len(line) == 0: 365 | continue 366 | 367 | if line.startswith("(defrule"): 368 | # If we found the beginning of a rule and we are not busy with another rule, 369 | if current_rule_lines is None: 370 | current_rule_lines = [] 371 | else: 372 | rules.append(self._lines_to_rule(current_rule_lines[:])) 373 | current_rule_lines = [] 374 | 375 | elif line.startswith("(load"): # We need to load a different file as well! 376 | extra_file = line.split(' "')[1].removesuffix('")').strip() 377 | loaded_raw_content = read_file_raw(os.path.join(self.parent_directory, (extra_file + ".per"))) 378 | loaded, _ = self._parse_raw_content(loaded_raw_content) 379 | constants.update(loaded) 380 | 381 | if current_rule_lines is not None: 382 | rules.append(self._lines_to_rule(current_rule_lines[:])) 383 | current_rule_lines = None 384 | 385 | elif line.startswith("(defconst"): # This line is a constant 386 | name, value = self._line_to_constant(line) 387 | is_digit, value = str_to_int(value, allow_negative=True) 388 | 389 | # If our constant value is a string, it must be an existing constants' value 390 | if not is_digit and value in constants.keys(): 391 | value = constants[value] 392 | constants[name] = value 393 | 394 | if current_rule_lines is not None: 395 | # TODO : convert list to string here, instead of later in the function 396 | rules.append(self._lines_to_rule(current_rule_lines[:])) 397 | current_rule_lines = None 398 | 399 | # If we are currently working on a rule, add the current line, unless it's the trailing parenthesis 400 | elif current_rule_lines is not None and len(line.strip()) > 0: 401 | current_rule_lines.append(line.strip()) 402 | 403 | return constants, rules 404 | 405 | @staticmethod 406 | def _line_to_constant(line: str) -> tuple: 407 | clean = inside_outer_parentheses(line) 408 | clean = clean.split(" ") 409 | if clean[0] != "defconst": 410 | raise Exception(f"Line '{line}'is not a constant!") 411 | return clean[1], clean[2] 412 | 413 | def _lines_to_rule(self, lines: list[str]): 414 | splitter_index = lines.index("=>") 415 | fact_lines = lines[:splitter_index] 416 | facts = self._lines_to_facts(fact_lines) 417 | # TODO parse actions correctly 418 | action_lines = lines[splitter_index + 1:] 419 | actions = self._lines_to_actions(action_lines) 420 | return Rule(facts, actions) 421 | 422 | @staticmethod 423 | def _lines_to_actions(lines: list[str]): 424 | actions = [] 425 | string = "".join(lines) 426 | string = re.sub(r"[\s\t\n\r]*\([\s\t\n\r]*", "(", string) # Remove whitespace 427 | string = re.sub(r"[\s\t\n\r]*\)[\s\t\n\r]*", ")", string) # Remove whitespace 428 | pattern = r"\([a-zA-Z0-9\- ><=!:\+]+\)" 429 | for action_string in re.findall(pattern, string): 430 | action_string = action_string.removesuffix(")") 431 | action_string = action_string.removeprefix("(") 432 | if len(action_string) > 0: 433 | actions.append(Simple(action_string.split())) 434 | return actions 435 | 436 | def _lines_to_facts(self, lines: list[str]): 437 | string = "".join(lines) 438 | string = re.sub(r"[\s\t\n\r]*\([\s\t\n\r]*", "(", string) # Remove all irrelevant whitespace 439 | string = re.sub(r"[\s\t\n\r]*\)[\s\t\n\r]*", ")", string) # Remove all irrelevant whitespace 440 | 441 | # Replace all operator keywords for symbols 442 | for operator, value in self.operators.items(): 443 | string = re.sub(r"\(\s*" + operator + r"\s*\(", f"({value}(", string) 444 | 445 | simples, string, without_globals = self._extract_simple_facts(string) 446 | complexes = self._extract_complex_facts(without_globals, simples) 447 | simples.extend(complexes) 448 | return simples 449 | 450 | def _extract_simple_facts(self, string: str): 451 | without_globals = string 452 | simples = [] 453 | pattern = r"\([a-zA-Z0-9\- ><=!:]+\)" 454 | match = re.search(pattern, string) 455 | while match: 456 | depth = string.count("(", 0, match.start()) 457 | depth -= string.count(")", 0, match.start()) 458 | simple_as_list = match[0][1:-1].split() # Remove the parenthesis TODO Maybe remove using regex. 459 | simples.append(Simple(simple_as_list)) 460 | string = string.replace(match[0], f"({self.simple_indicator}{len(simples) - 1})") 461 | if depth == 0: 462 | without_globals = without_globals.replace(match[0], "") 463 | else: 464 | without_globals = without_globals.replace(match[0], f"({self.simple_indicator}{len(simples) - 1})") 465 | match = re.search(pattern, string) 466 | return simples, string, without_globals 467 | 468 | def _extract_complex_facts(self, string: str, simples: list) -> list: 469 | if len(string) == 0: 470 | return [] 471 | 472 | pattern = r"\([&|#@$](\((\*|\%)[0-9]+\)){1,2}\)" 473 | match = re.search(pattern, string) 474 | complexes = [] 475 | # If we find a match, we most certainly have a valid complex fact 476 | while match: 477 | s = match[0] 478 | complexes.append(self.string_to_complex(s, simples, complexes)) 479 | string = string.replace(match[0], f"({self.complex_indicator}{len(complexes) - 1})") 480 | match = re.search(pattern, string) 481 | return complexes 482 | 483 | def string_to_complex(self, string: str, simples: list, complexes: list) -> Complex: 484 | string = string.removesuffix(")") 485 | string = string.removeprefix("(") 486 | # The operator symbol, followed by the simple/complex indicator and then the index 487 | end = string.find(")") 488 | index1 = int(string[3:end]) 489 | param1 = simples[index1] if (string[2] == self.simple_indicator) else complexes[index1] 490 | if string[0] == self.operators['not']: 491 | return Complex(name="not", param1=param1) 492 | 493 | elif string[0] in self.operators.values(): 494 | start = string.rfind("(") 495 | end = string.rfind(")") 496 | index2 = int(string[7:end]) 497 | param2 = simples[index2] if (string[start + 1] == self.simple_indicator) else complexes[index2] 498 | return Complex(name=self.operators_inverse[string[0]], param1=param1, param2=param2) 499 | 500 | raise Exception(f"{string[0]} is not a valid operator symbol!") 501 | 502 | def write(self, target_directory: str = None): 503 | destination: str = "" 504 | if target_directory is not None and dir_exists(target_directory, raise_exception=True): 505 | destination = os.path.join(target_directory, self.name + ".per") 506 | elif file_exists(self.path, raise_exception=True): 507 | destination = self.path 508 | else: 509 | raise Exception(f"AI {self.name} couldn't write its content to {destination}, destination doesn't exist!") 510 | 511 | with open(destination, 'w') as file: 512 | # Write constants 513 | for constant_name, constant_value in self.constants.items(): 514 | file.write(f"(defconst {constant_name} {constant_value}) \n") 515 | 516 | # Write rules 517 | for rule in self.rules: 518 | file.write(str(rule) + "\n\n") 519 | 520 | # ai_path = "C:\\Program Files\\Microsoft Games\\age of empires ii\\Ai" 521 | # example_path = "C:\\Program Files\\Microsoft Games\\age of empires ii\\Ai\\Alpha.per" 522 | # ai = AIParser.read_single(example_path) 523 | # ai.write("C:\\Users\\Gabi\\Documents\\GitHub\\AlphaScripter") 524 | # names = {"parent", "a", "b", "c", "d", "e", "f"} 525 | -------------------------------------------------------------------------------- /ai_writer.py: -------------------------------------------------------------------------------- 1 | from Functions import * 2 | import tkinter as tk 3 | from main import backup 4 | 5 | def write_from_csv(file): 6 | #parses csv 7 | f = open(file + ".csv",'r') 8 | data = f.read() 9 | f.close() 10 | 11 | #[type,params,threshold,age_required,requirement,requirement_count,gametime] 12 | 13 | data = data.split("|") 14 | #print(data[7]) 15 | 16 | research = data[1].split("\n") 17 | buildings = data[2].split("\n") 18 | build_forward = data[3].split("\n") 19 | units = data[4].split("\n") 20 | SN = data[5].split("\n") 21 | attack_rules = data[6].split("\n") 22 | goals = data[7].split("\n") 23 | DUC = data[8].split("\n") 24 | goal_actions = data[9].split("\n") 25 | 26 | AI = generate_ai() 27 | AI[0] = [] #clears simples 28 | AI[1] = [] #clears complex 29 | AI[2] = [] #clears attack rules 30 | AI[3] = [] 31 | AI[4] = [] 32 | AI[5] = [] 33 | AI[6] = [] 34 | 35 | simple_dict = {} 36 | 37 | #write research rules 38 | for i in range(2,len(research)-1): 39 | line = research[i].split(",") 40 | 41 | if line[0] != "": 42 | temp = generate_simple() 43 | temp[0] = 'research' 44 | temp[1]['TechId'] = line[0] 45 | temp[3] = [line[1]] 46 | temp[4] = line[2] 47 | temp[5] = int(line[3]) 48 | temp[6] = int(line[4]) 49 | temp[8] = int(line[6]) 50 | temp[9] = line[5] == 'TRUE' 51 | 52 | simple_dict[line[7]] = temp 53 | 54 | 55 | #[type,params,threshold,age_required,requirement] 56 | 57 | for i in range(2,len(buildings)): 58 | line = buildings[i].split(",") 59 | 60 | if line[0] != "": 61 | temp = generate_simple() 62 | temp[0] = 'build' 63 | temp[1]['Buildable'] = line[0] 64 | temp[2] = int(line[2]) 65 | temp[3] = [line[1]] 66 | temp[4] = line[3] 67 | temp[5] = int(line[4]) 68 | temp[6] = int(line[5]) 69 | temp[8] = int(line[7]) 70 | temp[9] = line[6] == 'TRUE' 71 | print(temp[9]) 72 | 73 | simple_dict[line[8]] = temp 74 | 75 | for i in range(2,len(build_forward)): 76 | line = build_forward[i].split(",") 77 | 78 | if line[0] != "": 79 | temp = generate_simple() 80 | temp[0] = 'build-forward' 81 | temp[1]['Buildable'] = line[0] 82 | temp[2] = int(line[2]) 83 | temp[3] = [line[1]] 84 | temp[4] = line[3] 85 | temp[5] = int(line[4]) 86 | temp[6] = int(line[5]) 87 | temp[8] = int(line[7]) 88 | temp[9] = line[6] == 'TRUE' 89 | 90 | simple_dict[line[8]] = temp 91 | 92 | for i in range(2,len(units)): 93 | line = units[i].split(",") 94 | 95 | if line[0] != "": 96 | temp = generate_simple() 97 | temp[0] = 'train' 98 | temp[1]['Trainable'] = line[0] 99 | temp[2] = int(line[2]) 100 | temp[3] = [line[1]] 101 | temp[4] = line[3] 102 | temp[5] = int(line[4]) 103 | temp[6] = int(line[5]) 104 | temp[8] = int(line[7]) 105 | temp[9] = line[6] == 'TRUE' 106 | 107 | simple_dict[line[8]] = temp 108 | 109 | for i in range(2,len(SN)): 110 | line = SN[i].split(",") 111 | 112 | if line[0] != "": 113 | temp = generate_simple() 114 | temp[0] = 'strategic_number' 115 | temp[1]['SnId'] = line[0] 116 | temp[7][temp[1]['SnId']] = line[2] 117 | temp[3] = [line[1]] 118 | temp[4] = line[3] 119 | temp[5] = int(line[4]) 120 | temp[6] = int(line[5]) 121 | temp[8] = int(line[7]) 122 | temp[9] = line[6] == 'TRUE' 123 | 124 | simple_dict[line[8]] = temp 125 | 126 | for item in simple_dict: 127 | AI[0].append(item) 128 | 129 | for item in simple_dict: 130 | AI[0][int(item)] = simple_dict[item] 131 | 132 | 133 | 134 | for i in range(2,len(attack_rules)): 135 | line = attack_rules[i].split(",") 136 | 137 | if line[0] != "": 138 | bool = False 139 | if line[14] == 'TRUE': 140 | bool = True 141 | AI[2] = [[line[0],line[1],line[2],[line[3],line[4],line[5]],[line[6],line[7],line[8]],[line[9],line[10]],line[11],line[12],line[13],line[15],bool]] + AI[2] 142 | 143 | for i in range(2,len(goals)): 144 | line = goals[i].split(",") 145 | 146 | if line[0] != "": 147 | temp = generate_goal() 148 | temp[0] = int(line[0]) 149 | temp[1] = int(line[1]) 150 | temp[2] = line[2] == "TRUE" 151 | temp[3] = int(line[3]) 152 | temp[4] = line[4] == 'TRUE' 153 | temp[6] = int(line[5]) 154 | 155 | for x in range(len(temp[5])): 156 | temp[5][x][0] = line[6 + x*2] 157 | 158 | local_params = line[7 + x*2].split(";") 159 | 160 | if temp[5][x][0] == 'TRUE': 161 | temp[5][x][0] = "true" 162 | if temp[5][x][0] == 'FALSE': 163 | temp[5][x][0] = "false" 164 | 165 | 166 | keys = facts[temp[5][x][0]] 167 | for y in range(len(local_params)): 168 | #print(str(keys[y]) + ", " + str(local_params[y-1])) 169 | #print(temp[5][x][1][keys[y]]) 170 | temp[5][x][1][keys[y+1]] = local_params[y] 171 | #print(keys[y]) 172 | #print(temp[5][x][1][keys[y]]) 173 | 174 | 175 | AI[5] = AI[5] + [temp] 176 | 177 | for i in range(2,len(DUC)): 178 | 179 | #print(DUC[i]) 180 | line = DUC[i].split(",") 181 | 182 | if i % 2 != 1 and line[0] != "": 183 | temp = generate_DUC_search() 184 | #print(write_DUC_search(temp)) 185 | temp[0] = line[0] 186 | temp[1] = int(line[1]) 187 | temp[2] = int(line[2]) 188 | temp[4] = int(line[3]) 189 | temp[5] = line[4] 190 | temp[6] = int(line[5]) 191 | temp[7] = line[6] == 'TRUE' 192 | for x in range(len(temp[3])): 193 | local = line[7 + x].split(";") 194 | temp[3][x] = [int(local[0]),local[1],int(local[2])] 195 | #print(temp[3]) 196 | 197 | AI[3] = AI[3] + [temp] 198 | 199 | elif line[0] != "": 200 | 201 | temp = [] 202 | a = generate_DUC_target() 203 | a[0] = line[0] 204 | a[1] = int(line[1]) 205 | a[2] = int(line[3]) 206 | a[4] = int(line[2]) 207 | 208 | for x in range(len(a[3])): 209 | local = line[4 + x].split(";") 210 | a[3][x] = [int(local[0]),local[1],int(local[2])] 211 | 212 | a[5] = int(line[11]) 213 | a[6] = int(line[12]) 214 | a[7] = int(line[13]) 215 | a[8] = line[14] == 'TRUE' 216 | a[9] = int(line[15]) 217 | a[10] = int(line[16]) 218 | a[11] = int(line[17]) 219 | a[12] = int(line[18]) 220 | a[13] = int(line[19]) 221 | a[14] = line[20] == 'TRUE' 222 | 223 | AI[4] = AI[4] + [a] 224 | 225 | 226 | for i in range(2,len(goal_actions)): 227 | line = goal_actions[i].split(",") 228 | 229 | if line[0] != "": 230 | temp = generate_goal_action() 231 | 232 | #count 233 | temp[3][0] = int(line[0]) 234 | temp[3][1] = int(line[1]) 235 | 236 | for x in range(3): 237 | temp[0][x] = int(line[2+2*x]) 238 | temp[1][x] = int(line[3+2*x]) 239 | 240 | for x in range(3): 241 | action_name = line[8 + 2 * x] 242 | local_params = line[9 + 2 * x].split(";") 243 | 244 | keys = actions[action_name] 245 | for y in range(len(local_params)): 246 | #print(str(keys[y]) + ", " + str(local_params[y-1])) 247 | #print(temp[5][x][1][keys[y]]) 248 | temp[2][x][0] = action_name 249 | temp[2][x][1][keys[y+1]] = local_params[y] 250 | 251 | 252 | AI[6] = AI[6] + [temp] 253 | 254 | backup() 255 | write_ai(AI,"best") 256 | save_ai(AI, "best") 257 | 258 | def read(string): 259 | ai = read_ai(string) 260 | simples = ai[0] 261 | attack_rules = ai[2] 262 | DUC_search = ai[3] 263 | DUC_target = ai[4] 264 | goals = ai[5] 265 | goal_actions = ai[6] 266 | 267 | research = [] 268 | buildings = [] 269 | build_forward = [] 270 | units = [] 271 | SN = [] 272 | 273 | #[type,params,threshold,age_required,requirement,requirement_count,gametime] 274 | for i in range(len(simples)): 275 | 276 | local_simple = simples[i] 277 | 278 | if local_simple[0] == 'research': 279 | #print(local_simple) 280 | research.append([local_simple[1]['TechId'],local_simple[3][0],local_simple[4],local_simple[5],local_simple[6],local_simple[9],local_simple[8],i]) 281 | 282 | if local_simple[0] == 'build': 283 | buildings.append([local_simple[1]['Buildable'],local_simple[3][0],local_simple[2],local_simple[4],local_simple[5],local_simple[6],local_simple[9],local_simple[8],i]) 284 | 285 | if local_simple[0] == 'build-forward': 286 | build_forward.append([local_simple[1]['Buildable'],local_simple[3][0],local_simple[2],local_simple[4],local_simple[5],local_simple[6],local_simple[9],local_simple[8],i]) 287 | 288 | if local_simple[0] == 'train': 289 | units.append([local_simple[1]['Trainable'],local_simple[3][0],local_simple[2],local_simple[4],local_simple[5],local_simple[6],local_simple[9],local_simple[8],i]) 290 | 291 | if local_simple[0] == 'strategic_number': 292 | SN.append([local_simple[1]['SnId'],local_simple[3][0],local_simple[7][local_simple[1]['SnId']],local_simple[4],local_simple[5],local_simple[6],local_simple[9],local_simple[8],i]) 293 | 294 | f = open("edittable.csv","w+") 295 | 296 | f.write("|Research\n") 297 | f.write("TechID,age required,required,requirement count,gametime,goal_checked,goal_id,order\n") 298 | 299 | write_list(research,f) 300 | 301 | f.write("\n|Building\n") 302 | f.write("BuildingID,age required,max count,required,requirement count,gametime,goal_checked,goal_id,order\n") 303 | 304 | write_list(buildings,f) 305 | 306 | f.write("\n|Build forward\n") 307 | f.write("BuildingID,age required,max count,required,requirement count,gametime,goal_checked,goal_id,order\n") 308 | 309 | write_list(build_forward,f) 310 | 311 | f.write("\n|Unit\n") 312 | f.write("UnitID,age required,max count,required,requirement count,gametime,goal_checked,goal_id,order\n") 313 | 314 | write_list(units,f) 315 | 316 | f.write("\n|Strategic Number\n") 317 | f.write("SnID,age required,value,required,requirement count,gametime,goal_checked,goal_id,order\n") 318 | 319 | write_list(SN,f) 320 | 321 | f.write("\n|Attack Rules\n") 322 | f.write("Type,Age required,Enemy Age Required,population1 type,population1 inq,population1 value,population2 type,population2 inq,population2 value,gametime inq,gametime value,attack %,retreat units,retreat location,goal_id,goal_checked\n") 323 | 324 | for i in range(len(attack_rules)): 325 | line = attack_rules[i] 326 | f.write(str(line[0])+ "," + str(line[1]) + "," + str(line[2]) + "," + str(line[3][0]) + "," + str(line[3][1]) + "," + str(line[3][2]) + "," + str(line[4][0]) + "," + str(line[4][1]) + "," + str(line[4][2]) + "," + str(line[5][0]) + "," + str(line[5][1]) + "," + str(line[6]) + "," + str(line[7]) + "," + str(line[8]) + "," + str(line[9]) + "," + str(line[10]) + "\n") 327 | 328 | f.write("\n|Goals\n") 329 | f.write("Goal ID,value,disable after use,checked goal,checked goal value,number of facts used,fact1,params1,fact2,params2,fact3,params3,fact4,params4\n") 330 | 331 | for i in range(len(goals)): 332 | line = goals[i] 333 | f.write(str(line[0])+ "," + str(line[1]) + "," + str(line[2]) + "," + str(line[3])+ "," + str(line[4]) + "," + str(line[6]) + ",") 334 | 335 | for x in range(len(line[5])): 336 | local = line[5][x] 337 | fact_name = local[0] 338 | params = local[1] 339 | f.write(local[0] + "," + str(params[facts[fact_name][1]]) + ";" + str(params[facts[fact_name][2]]) + ";" + str(params[facts[fact_name][3]]) + ";" + str(params[facts[fact_name][4]]) + ",") 340 | f.write("\n") 341 | 342 | f.write("\n|DUC\n") 343 | f.write("lol I will add a descripter later leave this line\n") 344 | for i in range(len(DUC_search)): 345 | f.write(write_DUC_search_local(DUC_search[i])) 346 | f.write(write_DUC_target_local(DUC_target[i])) 347 | 348 | f.write("\n|Goal actions\n") 349 | f.write("goal_count,action_count,goal1,goal_value1,goal2,goal_value2,goal3,goal_value3,action1,action1_parameters,action2,action2_parameters,action3,action3_parameters\n") 350 | 351 | for i in range(len(goal_actions)): 352 | 353 | goals = goal_actions[i][0] 354 | values = goal_actions[i][1] 355 | action = goal_actions[i][2] 356 | count = goal_actions[i][3] 357 | 358 | f.write(str(count[0]) + ",") 359 | f.write(str(count[1]) + ",") 360 | 361 | for x in range(len(goals)): 362 | f.write(str(goals[x]) + ",") 363 | f.write(str(values[x]) + ",") 364 | 365 | for x in range(len(action)): 366 | local = action[x] 367 | action_name = local[0] 368 | params = local[1] 369 | sns = local[2] 370 | f.write(local[0] + "," + str(params[actions[action_name][1]]) + ";" + str(params[actions[action_name][2]]) + ";" + str(params[actions[action_name][3]]) + ";" + str(params[actions[action_name][4]]) + ",") 371 | 372 | f.write("\n") 373 | 374 | 375 | f.close() 376 | 377 | def write_list(list,f): 378 | for i in range(len(list)): 379 | line = list[i] 380 | for x in range(len(line)): 381 | f.write(str(line[x]) + ",") 382 | f.write("\n") 383 | 384 | def swap(name1, name2): 385 | 386 | ai1 = read_ai(name1) 387 | ai2 = read_ai(name2) 388 | 389 | ai1[1] = [] 390 | ai1[1] = ai2[1] 391 | 392 | write_ai(ai1,"best") 393 | save_ai(ai1, "best") 394 | 395 | def write_DUC_search_local(search): 396 | 397 | self_selected = search[0] 398 | self_selected_max = search[1] 399 | used_filters = search[2] 400 | filters = search[3].copy() 401 | group_id = search[4] 402 | selected = search[5] 403 | selected_max = search[6] 404 | distance_check = search[7] 405 | 406 | string = "" 407 | 408 | string += str(self_selected) + "," + str(self_selected_max) + "," + str(used_filters) + "," + str(group_id) + "," + str(selected) + "," + str(selected_max) + "," + str(distance_check) + "," 409 | 410 | for i in range(len(filters)): 411 | 412 | filter_object = filters[i][0] 413 | filter_compare = filters[i][1] 414 | filter_value = filters[i][2] 415 | string += str(filter_object) + ";" + str(filter_compare) + ";" + str(filter_value) + "," 416 | 417 | string += "\n" 418 | 419 | return string 420 | 421 | def write_DUC_target_local(target): 422 | 423 | selected = target[0] 424 | selected_max = target[1] 425 | used_filters = target[2] 426 | filters = target[3] 427 | group_id = target[4] 428 | action = target[5] 429 | position = target[6] 430 | targeted_player = target[7] 431 | target_position = target[8] 432 | formation = target[9] 433 | stance = target[10] 434 | timer_id = target[11] 435 | if timer_id == 0: 436 | timer_id == 1 437 | timer_time = target[12] 438 | if len(target) > 13: 439 | goal = target[13] 440 | use_goal = target[14] 441 | else: 442 | goal = 1 443 | use_goal = False 444 | 445 | string = str(selected) + "," + str(selected_max) + "," + str(group_id) + "," + str(used_filters) + "," 446 | 447 | for i in range(len(filters)): 448 | filter_object = filters[i][0] 449 | filter_compare = filters[i][1] 450 | filter_value = filters[i][2] 451 | string += str(filter_object) + ";" + str(filter_compare) + ";" + str(filter_value) + "," 452 | 453 | string += str(action) + "," + str(position) + "," + str(targeted_player) + "," + str(target_position) + "," + str(formation) + "," + str(stance) + "," + str(timer_id) + "," + str(timer_time) + "," + str(goal) + "," + str(use_goal) + "\n" 454 | 455 | return string 456 | 457 | #a = generate_ai() 458 | #save_ai(a,"test") 459 | #read("test") 460 | #write_from_csv("edittable") 461 | 462 | root = tk.Tk() 463 | 464 | inputtxt = tk.Text(root, height = 1, width = 15) 465 | inputtxt.pack() 466 | 467 | button1 = tk.Button(root, text = "edit", command = lambda: read(inputtxt.get(1.0, "end-1c"))) 468 | button1.pack() 469 | 470 | inputtxt2 = tk.Text(root, height = 1, width = 15) 471 | inputtxt2.pack() 472 | 473 | button2 = tk.Button(root, text = "commit ai", command = lambda: write_from_csv(inputtxt2.get(1.0, "end-1c"))) 474 | button2.pack() 475 | 476 | inputtxt3 = tk.Text(root, height = 1, width = 15) 477 | inputtxt3.pack() 478 | inputtxt4 = tk.Text(root, height = 1, width = 15) 479 | inputtxt4.pack() 480 | 481 | button2 = tk.Button(root, text = "swap complex", command = lambda: swap(inputtxt3.get(1.0, "end-1c"), inputtxt4.get(1.0, "end-1c"))) 482 | button2.pack() 483 | 484 | 485 | root.mainloop() 486 | -------------------------------------------------------------------------------- /archive/AlphaScripter.36.py: -------------------------------------------------------------------------------- 1 | import random 2 | import cv2 3 | import pytesseract 4 | import pyautogui 5 | import time 6 | import pydirectinput 7 | import subprocess 8 | import os 9 | import signal 10 | from collections import OrderedDict 11 | 12 | pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe' 13 | 14 | pyautogui.failsafe = False 15 | 16 | # The path to the installation folder. Please change this to your specific installation folder 17 | installation_folder_path = "E:\\SteamLibrary\\steamapps\\common\\AoE2DE" 18 | 19 | # The installation folder fallback, which is the most standard installation path. 20 | installation_folder_path_fallback = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\AoE2DE" 21 | 22 | # The paths to the actual executable and of the AI folder. 23 | executable_path = "AoE2DE_s.exe" 24 | ai_path = "resources\\_common\\ai" 25 | 26 | #controls for algorithm 27 | fails_before_reset = 20 28 | script_rule_count = 500 29 | mutation_chance = .05 30 | 31 | # alphabet = 'abcdefghijklmnopqrstuvwxyz' 32 | # temp = [] 33 | # alphavalues = [] 34 | # for x in alphabet: 35 | # for y in alphabet: 36 | # temp.append(x + y) 37 | # 38 | # for i in range(300): 39 | # alphavalues.append(temp[i]) 40 | 41 | alphavalues = ['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag', 'ah', 'ai', 'aj', 'ak', 'al', 'am', 'an', 'ao', 'ap', 'aq', 42 | 'ar', 'as', 'at', 'au', 'av', 'aw', 'ax', 'ay', 'az', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'bg', 'bh', 43 | 'bi', 'bj', 'bk', 'bl', 'bm', 'bn', 'bo', 'bp', 'bq', 'br', 'bs', 'bt', 'bu', 'bv', 'bw', 'bx', 'by', 44 | 'bz', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'cg', 'ch', 'ci', 'cj', 'ck', 'cl', 'cm', 'cn', 'co', 'cp', 45 | 'cq', 'cr', 'cs', 'ct', 'cu', 'cv', 'cw', 'cx', 'cy', 'cz', 'da', 'db', 'dc', 'dd', 'de', 'df', 'dg', 46 | 'dh', 'di', 'dj', 'dk', 'dl', 'dm', 'dn', 'do', 'dp', 'dq', 'dr', 'ds', 'dt', 'du', 'dv', 'dw', 'dx', 47 | 'dy', 'dz', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'eg', 'eh', 'ei', 'ej', 'ek', 'el', 'em', 'en', 'eo', 48 | 'ep', 'eq', 'er', 'es', 'et', 'eu', 'ev', 'ew', 'ex', 'ey', 'ez', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff', 49 | 'fg', 'fh', 'fi', 'fj', 'fk', 'fl', 'fm', 'fn', 'fo', 'fp', 'fq', 'fr', 'fs', 'ft', 'fu', 'fv', 'fw', 50 | 'fx', 'fy', 'fz', 'ga', 'gb', 'gc', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gj', 'gk', 'gl', 'gm', 'gn', 51 | 'go', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gv', 'gw', 'gx', 'gy', 'gz', 'ha', 'hb', 'hc', 'hd', 'he', 52 | 'hf', 'hg', 'hh', 'hi', 'hj', 'hk', 'hl', 'hm', 'hn', 'ho', 'hp', 'hq', 'hr', 'hs', 'ht', 'hu', 'hv', 53 | 'hw', 'hx', 'hy', 'hz', 'ia', 'ib', 'ic', 'id', 'ie', 'if', 'ig', 'ih', 'ii', 'ij', 'ik', 'il', 'im', 54 | 'in', 'io', 'ip', 'iq', 'ir', 'is', 'it', 'iu', 'iv', 'iw', 'ix', 'iy', 'iz', 'ja', 'jb', 'jc', 'jd', 55 | 'je', 'jf', 'jg', 'jh', 'ji', 'jj', 'jk', 'jl', 'jm', 'jn', 'jo', 'jp', 'jq', 'jr', 'js', 'jt', 'ju', 56 | 'jv', 'jw', 'jx', 'jy', 'jz', 'ka', 'kb', 'kc', 'kd', 'ke', 'kf', 'kg', 'kh', 'ki', 'kj', 'kk', 'kl', 57 | 'km', 'kn', 'ko', 'kp', 'kq', 'kr', 'ks', 'kt', 'ku', 'kv', 'kw', 'kx', 'ky', 'kz', 'la', 'lb', 'lc', 58 | 'ld', 'le', 'lf', 'lg', 'lh', 'li', 'lj', 'lk', 'll', 'lm', 'ln'] 59 | 60 | ai_names = ['Alpha', 'Beta', 'c', 'd', 'e', 'f', 'g', 'h'] 61 | 62 | # read pre parsed actions 63 | f = open("actions.txt", "r") 64 | actions = f.read().split("\n") 65 | f.close() 66 | 67 | # read pre parsed facts 68 | f = open("conditions.txt", "r") 69 | conditions = f.read().split("\n") 70 | f.close() 71 | 72 | 73 | def check_installation_directory(): 74 | """This function checks whether the given installation directories exist and are valid.""" 75 | global installation_folder_path, installation_folder_path_fallback 76 | 77 | if os.path.isdir(installation_folder_path): 78 | print(f"Installation folder is valid.") 79 | else: 80 | print(f"Warning! Custom installation folder '{installation_folder_path}' is invalid." 81 | f" Trying to fall back on fallback installation path...") 82 | 83 | if os.path.isdir(installation_folder_path_fallback): 84 | installation_folder_path = installation_folder_path_fallback 85 | else: 86 | print(f"Warning! Fall back installation folder '{installation_folder_path_fallback}' is invalid. " 87 | f"Quitting the program...") 88 | quit() 89 | 90 | global executable_path, ai_path 91 | executable_path = installation_folder_path + "\\" + executable_path 92 | ai_path = installation_folder_path + "\\" + ai_path 93 | 94 | if not os.path.isfile(executable_path): 95 | print(f"Warning! Executable not found at location '{executable_path}'." 96 | f"Please correct the installation path in the code. Quitting the program...") 97 | quit() 98 | 99 | if not os.path.isdir(ai_path): 100 | print(f"AI folder not found at location {ai_path}." 101 | f"Please correct the installation path in the code. Quitting the program...") 102 | quit() 103 | 104 | 105 | def end_crash(): 106 | # Ask user for the name of process 107 | os.system("taskkill /f /im AoE2DE_s.exe") 108 | time.sleep(30) 109 | 110 | 111 | def clear_ais(): 112 | global ai_path 113 | 114 | hd_ai_path = ai_path + "\\HD.per" 115 | if os.path.isfile(hd_ai_path): 116 | with open(hd_ai_path, 'r') as hd_ai_file: 117 | hd_ai_contents = hd_ai_file.read() 118 | 119 | else: 120 | raise Exception(f"Clearing AI's failed. This can have 2 reasons. Either the path '{ai_path}' does not exist" 121 | f" or the 'AI (HD version).per' file needs to be duplicated and the duplicate renamed to " 122 | f"'HD.per'.") 123 | 124 | clear_ai("Alpha", hd_ai_contents) 125 | clear_ai("Beta", hd_ai_contents) 126 | clear_ai("c", hd_ai_contents) 127 | clear_ai("d", hd_ai_contents) 128 | clear_ai("e", hd_ai_contents) 129 | clear_ai("f", hd_ai_contents) 130 | clear_ai("g", hd_ai_contents) 131 | clear_ai("h", hd_ai_contents) 132 | 133 | 134 | def clear_ai(name: str, clear_value: str = ""): 135 | """Clear a single AI file.""" 136 | global ai_path 137 | 138 | full_path = ai_path + "\\" + name + ".per" 139 | ai_file_path = ai_path + "\\" + name + ".ai" 140 | 141 | if not os.path.isfile(full_path) or not os.path.isfile(ai_file_path): 142 | print(f"Warning! From AI '{name}' could either the .per or the .ai not be found. These files will be created.") 143 | open(ai_file_path, 'x') 144 | 145 | with open(full_path, 'w+') as file: 146 | file.write(clear_value) 147 | 148 | 149 | def reset_game(image): 150 | fail = True 151 | 152 | print("reset") 153 | subprocess.Popen(executable_path) 154 | find_button("launch_okay.png") 155 | find_button("single_player.png") 156 | find_button("skirmish.png") 157 | 158 | # custom for player find 159 | while True: 160 | try: 161 | x, y = pyautogui.locateCenterOnScreen("player_1.png", confidence=0.9) 162 | time.sleep(1) 163 | pydirectinput.click(x + 100, y) 164 | break 165 | except (pyautogui.ImageNotFoundException, TypeError): 166 | pass 167 | 168 | find_button(image) 169 | find_button("start_game.png") 170 | time.sleep(7) 171 | pyautogui.press("2") 172 | pyautogui.press("2") 173 | pyautogui.press("2") 174 | pyautogui.press("2") 175 | time.sleep(1) 176 | pyautogui.press("2") 177 | pyautogui.press("2") 178 | pyautogui.press("2") 179 | pyautogui.press("2") 180 | time.sleep(1) 181 | pyautogui.press("2") 182 | pyautogui.press("2") 183 | pyautogui.press("2") 184 | pyautogui.press("2") 185 | 186 | for i in range(50): 187 | try: 188 | x, y = pyautogui.locateCenterOnScreen("open_menu.png", confidence=0.9) 189 | time.sleep(1) 190 | pydirectinput.click(x, y) 191 | 192 | x, y = pyautogui.locateCenterOnScreen("quit.png", confidence=0.9) 193 | time.sleep(1) 194 | pydirectinput.click(x, y) 195 | 196 | find_button("yes.png") 197 | 198 | fail = False 199 | break 200 | except (pyautogui.ImageNotFoundException, TypeError): 201 | pass 202 | 203 | if not fail: 204 | return False 205 | else: 206 | return "crash" 207 | 208 | 209 | def crossover(rule1, rule2, alpha1, alpha2): 210 | global mutation_chance 211 | 212 | rules_out = [] 213 | for i in range(len(rule1)): 214 | 215 | if random.random() < .5: 216 | if random.random() < mutation_chance: 217 | rules_out.append(random.choice(rule1)) 218 | else: 219 | rules_out.append(rule1[i]) 220 | else: 221 | if random.random() < mutation_chance: 222 | rules_out.append(random.choice(rule2)) 223 | else: 224 | rules_out.append(rule2[i]) 225 | 226 | alphas_out = [] 227 | for i in range(len(alpha1)): 228 | 229 | if random.random() < .5: 230 | if random.random() < mutation_chance: 231 | alphas_out.append(random.choice(alpha1)) 232 | else: 233 | alphas_out.append(alpha1[i]) 234 | else: 235 | if random.random() < mutation_chance: 236 | alphas_out.append(random.choice(alpha2)) 237 | else: 238 | alphas_out.append(alpha2[i]) 239 | 240 | return rules_out, alphas_out 241 | 242 | 243 | def read_best(): 244 | f = open("best.txt", "r") 245 | 246 | file = f.read().split("\n") 247 | 248 | f.close() 249 | 250 | file_rules = file[0].split("|") 251 | file_constants = file[1].split(",") 252 | 253 | constants_out = [] 254 | for i in range(len(file_constants)): 255 | if file_constants[i] != "": 256 | constants_out.append(int(file_constants[i])) 257 | 258 | rules_out = [] 259 | for i in range(len(file_rules)): 260 | if file_rules[i] != "": 261 | local_rule = file_rules[i].split(",") 262 | local_conditions = [local_rule[0], local_rule[1], local_rule[2], local_rule[3], local_rule[4]] 263 | local_actions = [local_rule[5], local_rule[6], local_rule[7], local_rule[8], local_rule[9]] 264 | local_alphas = [local_rule[10], local_rule[11], local_rule[12], local_rule[13], local_rule[14], 265 | local_rule[15], local_rule[16], local_rule[17], local_rule[18], local_rule[19]] 266 | if_and = int(local_rule[20]) 267 | action_length = int(local_rule[21]) 268 | condition_length = int(local_rule[22]) 269 | age_required = int(local_rule[23]) 270 | # print(local_rule) 271 | 272 | rules_out.append([local_conditions, local_actions, local_alphas, if_and, action_length, condition_length]) 273 | 274 | return rules_out, constants_out 275 | 276 | 277 | def static_code(): 278 | f = open("Goals.txt", "r") 279 | goals = f.read() 280 | f.close() 281 | 282 | f = open("constants.txt", "r") 283 | statics = f.read() 284 | f.close() 285 | 286 | return goals + "\n\n\n" + statics 287 | 288 | 289 | def read_run_length(): 290 | f = open("run_length.txt") 291 | length = int(f.read()) 292 | f.close() 293 | 294 | return length 295 | 296 | 297 | def start_game(image): 298 | find_button("main_menu.png") 299 | time.sleep(1) 300 | find_button("single_player.png") 301 | find_button("skirmish.png") 302 | 303 | while True: 304 | try: 305 | x, y = pyautogui.locateCenterOnScreen("player_1.png", confidence=0.9) 306 | time.sleep(1) 307 | pydirectinput.click(x + 100, y) 308 | break 309 | except (pyautogui.ImageNotFoundException, TypeError): 310 | pass 311 | 312 | find_button(image) 313 | find_button("start_game.png") 314 | time.sleep(5) 315 | pyautogui.press("2") 316 | pyautogui.press("2") 317 | pyautogui.press("2") 318 | pyautogui.press("2") 319 | time.sleep(1) 320 | pyautogui.press("2") 321 | pyautogui.press("2") 322 | pyautogui.press("2") 323 | pyautogui.press("2") 324 | time.sleep(1) 325 | pyautogui.press("2") 326 | pyautogui.press("2") 327 | pyautogui.press("2") 328 | pyautogui.press("2") 329 | 330 | # find_button("play_again.PNG") 331 | # find_button("ok.PNG") 332 | 333 | 334 | def clean(string): 335 | numeric_filter = filter(str.isdigit, string) 336 | string = "".join(numeric_filter) 337 | 338 | try: 339 | value = int(string) 340 | except ValueError: 341 | value = 0 342 | 343 | return value 344 | 345 | 346 | def find_button(image): 347 | while True: 348 | try: 349 | x, y = pyautogui.locateCenterOnScreen(image, confidence=0.8) 350 | time.sleep(1) 351 | pydirectinput.click(x, y) 352 | break 353 | except (pyautogui.ImageNotFoundException, TypeError): 354 | pass 355 | 356 | 357 | def end_game(): 358 | game_ended = False 359 | start = time.time() 360 | current = start 361 | 362 | run_length = read_run_length() 363 | 364 | while not game_ended: 365 | current = time.time() 366 | 367 | if current - start > run_length: 368 | 369 | try: 370 | x, y = pyautogui.locateCenterOnScreen("open_menu.png", confidence=0.9) 371 | time.sleep(1) 372 | pydirectinput.click(x, y) 373 | except (pyautogui.ImageNotFoundException, TypeError): 374 | return "crash" 375 | 376 | time.sleep(1) 377 | 378 | try: 379 | x, y = pyautogui.locateCenterOnScreen("quit.png", confidence=0.9) 380 | time.sleep(1) 381 | pydirectinput.click(x, y) 382 | except (pyautogui.ImageNotFoundException, TypeError): 383 | return "crash" 384 | 385 | find_button("yes.PNG") 386 | print("timed out") 387 | return True 388 | 389 | try: 390 | x, y = pyautogui.locateCenterOnScreen('leave.png', confidence=0.9) 391 | pyautogui.click(x, y) 392 | game_ended = True 393 | return False 394 | except (pyautogui.ImageNotFoundException, TypeError): 395 | pass 396 | 397 | 398 | def generate_constants(): 399 | temp = [] 400 | for i in range(300): 401 | temp.append(random.randint(0, 200)) 402 | return temp 403 | 404 | 405 | def generate_rules(count): 406 | rules = [] 407 | 408 | for i in range(count): 409 | conditions1 = [random.choice(conditions), random.choice(conditions), random.choice(conditions), 410 | random.choice(conditions), random.choice(conditions)] 411 | actions1 = [random.choice(actions), random.choice(actions), random.choice(actions), random.choice(actions), 412 | random.choice(actions)] 413 | alphavalues1 = [random.choice(alphavalues), random.choice(alphavalues), random.choice(alphavalues), 414 | random.choice(alphavalues), random.choice(alphavalues), random.choice(alphavalues), 415 | random.choice(alphavalues), random.choice(alphavalues), random.choice(alphavalues), 416 | random.choice(alphavalues)] 417 | if_and = random.randint(1, 3) 418 | action_length = random.randint(1, 5) 419 | condition_length = random.randint(1, 5) 420 | age_required = random.randint(0, 3) 421 | 422 | rules.append([conditions1, actions1, alphavalues1, if_and, action_length, condition_length, age_required]) 423 | 424 | # print(conditions1) 425 | return rules 426 | 427 | 428 | def mutate_rules(list): 429 | global mutation_chance 430 | 431 | rules1 = [] 432 | 433 | for x in range(len(list)): 434 | conditions1 = list[x][0].copy() 435 | actions1 = list[x][1].copy() 436 | alphavalues1 = list[x][2].copy() 437 | if_and = list[x][3] 438 | action_length = list[x][4] 439 | condition_length = list[x][5] 440 | age_required = list[x][6] 441 | 442 | for i in range(len(conditions1)): 443 | conditions1[i] = mutate_single(conditions, conditions1[i]) 444 | actions1[i] = mutate_single(actions, actions1[i]) 445 | 446 | for i in range(len(alphavalues1)): 447 | alphavalues1[i] = mutate_single(alphavalues, alphavalues1[i]) 448 | 449 | if random.random() < mutation_chance: 450 | if_and = random.randint(1, 3) 451 | 452 | if random.random() < mutation_chance: 453 | action_length = random.randint(1, 5) 454 | 455 | if random.random() < mutation_chance: 456 | condition_length = random.randint(1, 5) 457 | 458 | if random.random() < mutation_chance: 459 | age_required = random.randint(0, 3) 460 | 461 | rules1.append([conditions1, actions1, alphavalues1, if_and, action_length, condition_length, age_required]) 462 | 463 | return rules1 464 | 465 | 466 | def mutate_single(list, item): 467 | global mutation_chance 468 | 469 | if random.random() < mutation_chance: 470 | return random.choice(list) 471 | else: 472 | return item 473 | 474 | 475 | def age_grab(y): 476 | find_button("tech.png") 477 | 478 | feudal_age = pyautogui.screenshot(region=(820, y, 100, 50)) 479 | age = pytesseract.image_to_string(feudal_age) 480 | 481 | if ":" in age: 482 | age_score = 1000 483 | else: 484 | age_score = 0 485 | 486 | return age_score 487 | 488 | 489 | def score_grab(y, bonus): 490 | military = pyautogui.screenshot(region=(832, y, 100, 50)) 491 | eco = pyautogui.screenshot(region=(940, y, 100, 50)) 492 | society = pyautogui.screenshot("try.png", region=(1132, y, 100, 50)) 493 | 494 | eco_score = clean(pytesseract.image_to_string(eco)) #** .7 495 | military_score = clean(pytesseract.image_to_string(military)) #** .7 496 | society_score = clean(pytesseract.image_to_string(society)) #** .7 497 | 498 | tscore = military_score + eco_score + society_score + bonus 499 | 500 | return tscore 501 | 502 | 503 | def save_ai(rules, alphavalues, name): 504 | f = open(name + ".txt", "w+") 505 | for i in range(len(rules)): 506 | conditions1 = rules[i][0].copy() 507 | actions1 = rules[i][1].copy() 508 | constants1 = rules[i][2].copy() 509 | if_and = rules[i][3] 510 | action_length = rules[i][4] 511 | condition_length = rules[i][5] 512 | age_required = rules[i][6] 513 | 514 | f.write("|") 515 | for x in range(len(conditions1)): 516 | f.write(conditions1[x] + ",") 517 | for x in range(len(actions1)): 518 | f.write(actions1[x] + ",") 519 | for x in range(len(constants1)): 520 | f.write(constants1[x] + ",") 521 | f.write(str(if_and) + ",") 522 | f.write(str(action_length) + ",") 523 | f.write(str(condition_length) + ",") 524 | f.write(str(age_required)) 525 | 526 | f.write("\n") 527 | for i in range(len(alphavalues)): 528 | f.write(str(alphavalues[i]) + ",") 529 | f.close() 530 | 531 | 532 | def write_rules(list): 533 | string = "" 534 | 535 | # print(list) 536 | 537 | for i in range(len(list)): 538 | 539 | conditions1 = list[i][0].copy() 540 | actions1 = list[i][1].copy() 541 | alphavalues1 = list[i][2].copy() 542 | if_and = list[i][3] 543 | action_length = list[i][4] 544 | condition_length = list[i][5] 545 | age_required = list[i][6] 546 | 547 | 548 | load_if_value = ["",""] 549 | 550 | if age_required == 1: 551 | load_if_value = ["#load-if-not-defined DARK-AGE-END\n","#end-if\n"] 552 | 553 | elif age_required == 2: 554 | load_if_value = ["#load-if-not-defined FEUDAL-AGE-END\n","#end-if\n"] 555 | 556 | elif age_required == 3: 557 | load_if_value = ["#load-if-not-defined CASTLE-AGE-END\n","#end-if\n"] 558 | 559 | string += load_if_value[0] + "(defrule\n" 560 | if_and_value = ["", ""] 561 | 562 | if condition_length == 2: 563 | if if_and == 1: 564 | string += "(or" 565 | if_and_value[0] = "\t" 566 | if_and_value[1] = ")" 567 | 568 | elif if_and == 2: 569 | string += "(and" 570 | if_and_value[0] = "\t" 571 | if_and_value[1] = ")" 572 | 573 | for i in range(condition_length): 574 | string += if_and_value[0] + conditions1[i].replace("AlphaMorphValue", alphavalues1[i]) + "\n" 575 | 576 | string += if_and_value[1] + "\n=>\n" 577 | 578 | for i in range(action_length): 579 | string += actions1[i].replace("AlphaMorphValue", alphavalues1[5 + i]) + "\n" 580 | 581 | string += ")\n" + load_if_value[1] + "\n" 582 | 583 | return string 584 | 585 | 586 | def mutate_constants(list): 587 | global mutation_chance 588 | 589 | list2 = list.copy() 590 | for i in range(len(list2)): 591 | 592 | if random.random() < .01: 593 | list2[i] = random.randint(0, 200) 594 | 595 | elif random.random() < mutation_chance: 596 | list2[i] += random.randint(-10, 10) 597 | list2[i] = max(0, list2[i]) 598 | 599 | return list2 600 | 601 | 602 | def write_ai(list, rules, name: str, to_ai_folder: bool = True): 603 | constant_write = "" 604 | for i in range(len(alphavalues)): 605 | constant_write += "\n(defconst " + alphavalues[i] + " " + str(list[i]) + ")" 606 | 607 | # If we want to write to the AI folder, do that, else just use the project folder. 608 | full_path = ai_path + "\\" + name + ".per" if to_ai_folder else name + ".per" 609 | 610 | if not os.path.isfile(full_path): 611 | print(f"AI file {full_path} cannot be found for writing. This file will therefore be created.") 612 | 613 | with open(full_path, "w+") as ai_file: 614 | ai_file.write(static_code() + "\n\n\n") 615 | ai_file.write(constant_write) 616 | ai_file.write("\n\n\n") 617 | ai_file.write(write_rules(rules)) 618 | ai_file.write("\n\n\n") 619 | 620 | def generate_script(length): 621 | 622 | print("generating new script") 623 | genesParent = generate_constants() 624 | rulesParent = generate_rules(length) 625 | 626 | return rulesParent, genesParent 627 | 628 | def run_vs(genesParent, rulesParent): 629 | global mutation_chance 630 | 631 | clear_ais() 632 | reset_game("alpha.png") 633 | 634 | time.sleep(1) 635 | best = 0 636 | fails = 0 637 | while True: 638 | try: 639 | 640 | # alphaDNA = genesParent.copy() 641 | alphaDNA = mutate_constants(genesParent) 642 | alphaRules = mutate_rules(rulesParent) 643 | 644 | betaDNA = genesParent.copy() 645 | betaRules = rulesParent.copy() 646 | 647 | write_ai(alphaDNA, alphaRules, "Alpha") 648 | write_ai(betaDNA, betaRules, "Beta") 649 | 650 | start_game("alpha.png") 651 | timed_out = end_game() 652 | 653 | while timed_out == "crash": 654 | end_crash() 655 | timed_out = reset_game("alpha.png") 656 | if timed_out != "crash": 657 | start_game("alpha.png") 658 | timed_out = end_game() 659 | 660 | alphaDNA = mutate_constants(genesParent) 661 | alphaRules = mutate_rules(rulesParent) 662 | write_ai(alphaDNA, alphaRules, "Alpha") 663 | 664 | time.sleep(5) 665 | 666 | # finds winner based on crown location in end screen and gives bonus 667 | try: 668 | x, y = pyautogui.locateCenterOnScreen('won.PNG', confidence=0.8) 669 | except (pyautogui.ImageNotFoundException, TypeError): 670 | y = 0 671 | print("could not find crown") 672 | 673 | alpha_bonus = 0 674 | beta_bonus = 0 675 | 676 | if y < 337 and not timed_out: 677 | winner = alphaDNA 678 | alpha_bonus = 10000 679 | print("alpha won") 680 | 681 | elif not timed_out: 682 | winner = betaDNA 683 | print("beta won") 684 | beta_bonus = 10000 685 | 686 | # reads score from screen 687 | alpha_score = score_grab(290, alpha_bonus) 688 | beta_score = score_grab(337, beta_bonus) 689 | 690 | if beta_score > alpha_score: 691 | fails += 1 692 | mutation_chance += fails / 1000 693 | winner = betaDNA.copy() 694 | winnerRules = rulesParent.copy() 695 | print("beta won by score: " + str(beta_score)) 696 | 697 | else: 698 | fails = 0 699 | mutation_chance = .05 700 | winner = alphaDNA.copy() 701 | winnerRules = alphaRules.copy() 702 | print("alpha won by score: " + str(alpha_score)) 703 | 704 | genesParent = winner.copy() 705 | rulesParent = winnerRules.copy() 706 | write_ai(winner, winnerRules, "best") 707 | save_ai(winnerRules, winner, "best") 708 | 709 | # if score > best: 710 | # best = score 711 | # print("new best: " + str(score)) 712 | # alpha = write_ai(alphaDNA, "Best") 713 | # genesParent = alphaDNA.copy() 714 | 715 | except KeyboardInterrupt: 716 | input("enter anything to continue...") 717 | 718 | 719 | def run_score(genesParent, rulesParent): 720 | time.sleep(1) 721 | best = 0 722 | fails = 0 723 | 724 | clear_ais() 725 | reset_game("bar.png") 726 | 727 | second_placeRules = rulesParent.copy() 728 | second_place = genesParent.copy() 729 | 730 | while True: 731 | 732 | try: 733 | 734 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 735 | 736 | alphaDNA = mutate_constants(crossed_genes) 737 | alphaRules = mutate_rules(crossed_rules) 738 | write_ai(alphaDNA, alphaRules, "Alpha") 739 | 740 | start_game("bar.png") 741 | timed_out = end_game() 742 | 743 | while timed_out == "crash": 744 | end_crash() 745 | timed_out = reset_game("bar.png") 746 | 747 | if timed_out != "crash": 748 | start_game("bar.png") 749 | timed_out = end_game() 750 | 751 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 752 | alphaDNA = mutate_constants(crossed_genes) 753 | alphaRules = mutate_rules(crossed_rules) 754 | write_ai(alphaDNA, alphaRules, "Alpha") 755 | 756 | time.sleep(5) 757 | 758 | # reads score from screen 759 | score = score_grab(337, 0) 760 | 761 | if score > best: 762 | fails = 0 763 | mutation_chance = .05 764 | best = score 765 | print("new best: " + str(score)) 766 | write_ai(alphaDNA, alphaRules, "Best") 767 | save_ai(alphaRules, alphaDNA, "Best") 768 | second_place = genesParent.copy() 769 | second_placeRules = rulesParent.copy() 770 | genesParent = alphaDNA.copy() 771 | rulesParent = alphaRules.copy() 772 | else: 773 | fails += 1 774 | mutation_chance += fails / 1000 775 | 776 | except KeyboardInterrupt: 777 | input("enter anything to continue...") 778 | 779 | 780 | def run_ffa(genesParent, rulesParent, load): 781 | global mutation_chance 782 | check = "crash" 783 | 784 | clear_ais() 785 | reset_game("alpha.png") 786 | 787 | time.sleep(1) 788 | 789 | second_placeRules = rulesParent.copy() 790 | second_place = genesParent.copy() 791 | 792 | best = 0 793 | if load == "load": 794 | generation = 1 795 | else: 796 | generation = 0 797 | fails = 0 798 | 799 | while True: 800 | 801 | generation += 1 802 | 803 | try: 804 | 805 | # refector later 806 | # alphaDNA = genesParent.copy() 807 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 808 | alphaDNA = mutate_constants(crossed_genes) 809 | alphaRules = mutate_rules(crossed_rules) 810 | 811 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 812 | cDNA = mutate_constants(crossed_genes) 813 | cRules = mutate_rules(crossed_rules) 814 | 815 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 816 | dDNA = mutate_constants(crossed_genes) 817 | dRules = mutate_rules(crossed_rules) 818 | 819 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 820 | eDNA = mutate_constants(crossed_genes) 821 | eRules = mutate_rules(crossed_rules) 822 | 823 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 824 | fDNA = mutate_constants(crossed_genes) 825 | fRules = mutate_rules(crossed_rules) 826 | 827 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 828 | gDNA = mutate_constants(crossed_genes) 829 | gRules = mutate_rules(crossed_rules) 830 | 831 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 832 | hDNA = mutate_constants(crossed_genes) 833 | hRules = mutate_rules(crossed_rules) 834 | 835 | betaDNA = genesParent.copy() 836 | betaRules = rulesParent.copy() 837 | 838 | write_ai(alphaDNA, alphaRules, "Alpha") 839 | write_ai(betaDNA, betaRules, "Beta") 840 | write_ai(cDNA, cRules, "c") 841 | write_ai(dDNA, dRules, "d") 842 | write_ai(eDNA, eRules, "e") 843 | write_ai(fDNA, fRules, "f") 844 | write_ai(gDNA, gRules, "g") 845 | write_ai(hDNA, hRules, "h") 846 | 847 | start_game("alpha.png") 848 | timed_out = end_game() 849 | 850 | while timed_out == "crash": 851 | 852 | end_crash() 853 | timed_out = reset_game("alpha.png") 854 | 855 | if timed_out != "crash": 856 | start_game("alpha.png") 857 | timed_out = end_game() 858 | 859 | else: 860 | if generation == 1: 861 | rulesParent, genesParent = generate_script(script_rule_count) 862 | 863 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 864 | alphaDNA = mutate_constants(crossed_genes) 865 | alphaRules = mutate_rules(crossed_rules) 866 | 867 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 868 | cDNA = mutate_constants(crossed_genes) 869 | cRules = mutate_rules(crossed_rules) 870 | 871 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 872 | dDNA = mutate_constants(crossed_genes) 873 | dRules = mutate_rules(crossed_rules) 874 | 875 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 876 | eDNA = mutate_constants(crossed_genes) 877 | eRules = mutate_rules(crossed_rules) 878 | 879 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 880 | fDNA = mutate_constants(crossed_genes) 881 | fRules = mutate_rules(crossed_rules) 882 | 883 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 884 | gDNA = mutate_constants(crossed_genes) 885 | gRules = mutate_rules(crossed_rules) 886 | 887 | crossed_rules, crossed_genes = crossover(rulesParent, second_placeRules, genesParent, second_place) 888 | hDNA = mutate_constants(crossed_genes) 889 | hRules = mutate_rules(crossed_rules) 890 | 891 | betaDNA = genesParent.copy() 892 | betaRules = rulesParent.copy() 893 | 894 | write_ai(alphaDNA, alphaRules, "Alpha") 895 | write_ai(betaDNA, betaRules, "Beta") 896 | write_ai(cDNA, cRules, "c") 897 | write_ai(dDNA, dRules, "d") 898 | write_ai(eDNA, eRules, "e") 899 | write_ai(fDNA, fRules, "f") 900 | write_ai(gDNA, gRules, "g") 901 | write_ai(hDNA, hRules, "h") 902 | 903 | time.sleep(3) 904 | pyautogui.click(10, 10) 905 | 906 | # reads score from screen 907 | alpha_score = score_grab(287, 0) 908 | beta_score = score_grab(337, 0) 909 | c_score = score_grab(387, 0) 910 | d_score = score_grab(437, 0) 911 | e_score = score_grab(487, 0) 912 | f_score = score_grab(537, 0) 913 | g_score = score_grab(587, 0) 914 | h_score = score_grab(637, 0) 915 | 916 | alpha_score += age_grab(287) 917 | beta_score += age_grab(337) 918 | c_score += age_grab(387) 919 | d_score += age_grab(437) 920 | e_score += age_grab(487) 921 | f_score += age_grab(537) 922 | g_score += age_grab(587) 923 | h_score += age_grab(637) 924 | 925 | score_list = [alpha_score, beta_score, c_score, d_score, e_score, f_score, g_score, h_score] 926 | 927 | score_list = sorted(score_list, reverse=True) 928 | 929 | # checks number of rounds with no improvement and sets annealing 930 | if beta_score == max(score_list): 931 | fails += 1 932 | mutation_chance += fails / 1000 933 | else: 934 | fails = 0 935 | mutation_chance = .05 936 | 937 | failed = False 938 | 939 | # checks for unlikely score, need to fix later 940 | # if score_list[0] > score_list[1] * 10: 941 | # winner = genesParent.copy() 942 | # winnerRules = rulesParent.copy() 943 | # print("impossible score, skipping round") 944 | 945 | if alpha_score == max(score_list): 946 | winner = alphaDNA.copy() 947 | winnerRules = alphaRules.copy() 948 | print("alpha won by score: " + str(alpha_score)) 949 | 950 | elif beta_score == max(score_list): 951 | failed = True 952 | winner = genesParent.copy() 953 | winnerRules = rulesParent.copy() 954 | second_place = genesParent.copy() 955 | winnerRules = rulesParent.copy() 956 | print("beta won by score: " + str(beta_score)) 957 | 958 | elif c_score == max(score_list): 959 | winner = cDNA.copy() 960 | winnerRules = cRules.copy() 961 | print("c won by score: " + str(c_score)) 962 | 963 | elif d_score == max(score_list): 964 | winner = dDNA.copy() 965 | winnerRules = dRules.copy() 966 | print("d won by score: " + str(d_score)) 967 | 968 | elif e_score == max(score_list): 969 | winner = eDNA.copy() 970 | winnerRules = eRules.copy() 971 | print("e won by score: " + str(e_score)) 972 | 973 | elif f_score == max(score_list): 974 | winner = fDNA.copy() 975 | winnerRules = fRules.copy() 976 | print("f won by score: " + str(f_score)) 977 | 978 | elif g_score == max(score_list): 979 | winner = gDNA.copy() 980 | winnerRules = gRules.copy() 981 | print("g won by score: " + str(g_score)) 982 | 983 | else: # h_score == max(score_list): 984 | winner = hDNA.copy() 985 | winnerRules = hRules.copy() 986 | print("h won by score: " + str(h_score)) 987 | 988 | # checks if second best for crossover, also gross and needs to be replaced later 989 | if not failed: 990 | if score_list[0] > score_list[1] * 5: 991 | second_place = genesParent.copy() 992 | second_placeRules = rulesParent.copy() 993 | 994 | elif alpha_score == score_list[1]: 995 | second_place = alphaDNA.copy() 996 | second_placeRules = alphaRules.copy() 997 | 998 | elif beta_score == score_list[1]: 999 | second_place = genesParent.copy() 1000 | second_placeRules = rulesParent.copy() 1001 | 1002 | elif c_score == score_list[1]: 1003 | second_place = cDNA.copy() 1004 | second_placeRules = cRules.copy() 1005 | 1006 | elif d_score == score_list[1]: 1007 | second_place = dDNA.copy() 1008 | second_placeRules = dRules.copy() 1009 | 1010 | elif e_score == score_list[1]: 1011 | second_place = eDNA.copy() 1012 | second_placeRules = eRules.copy() 1013 | 1014 | elif f_score == score_list[1]: 1015 | second_place = fDNA.copy() 1016 | second_placeRules = fRules.copy() 1017 | 1018 | elif g_score == score_list[1]: 1019 | second_place = gDNA.copy() 1020 | second_placeRules = gRules.copy() 1021 | 1022 | else: # h_score == score_list[1]: 1023 | second_place = hDNA.copy() 1024 | second_placeRules = hRules.copy() 1025 | 1026 | genesParent = winner.copy() 1027 | rulesParent = winnerRules.copy() 1028 | write_ai(winner, winnerRules, "best", to_ai_folder=False) 1029 | save_ai(winnerRules, winner, "best") 1030 | 1031 | #restarts after 10 fails 1032 | if fails > fails_before_reset: 1033 | print("fail threshold exceeded, reseting...") 1034 | write_ai(winner, winnerRules, str(max(score_list)), to_ai_folder=False) 1035 | save_ai(winnerRules, winner, str(max(score_list))) 1036 | fails = 0 1037 | generation = 0 1038 | rulesParent, genesParent = generate_script(script_rule_count) 1039 | 1040 | except KeyboardInterrupt: 1041 | input("enter anything to continue...") 1042 | 1043 | 1044 | check_installation_directory() 1045 | 1046 | rulesParent, genesParent = generate_script(script_rule_count) 1047 | #rulesParent, genesParent = read_best() 1048 | 1049 | # run_vs(genesParent, rulesParent) 1050 | # run_score(genesParent, rulesParent) 1051 | run_ffa(genesParent, rulesParent, "no") 1052 | -------------------------------------------------------------------------------- /archive/actions.txt: -------------------------------------------------------------------------------- 1 | (attack-now) 2 | (build archery-range) 3 | (build barracks) 4 | (build blacksmith) 5 | (build bombard-tower) 6 | (build castle) 7 | (build dock) 8 | (build farm) 9 | (build house) 10 | (build lumber-camp) 11 | (build market) 12 | (build mill) 13 | (build mining-camp) 14 | (build monastery) 15 | (build siege-workshop) 16 | (build stable) 17 | (build town-center) 18 | (build university) 19 | (build watch-tower) 20 | (build-forward archery-range) 21 | (build-forward barracks) 22 | (build-forward bombard-tower) 23 | (build-forward castle) 24 | (build-forward siege-workshop) 25 | (build-forward stable) 26 | (build-forward watch-tower) 27 | (buy-commodity food) 28 | (buy-commodity stone) 29 | (buy-commodity wood) 30 | (delete-building blacksmith) 31 | (delete-building bombard-tower) 32 | (delete-building castle) 33 | (delete-building farm) 34 | (delete-building feitoria) 35 | (delete-building fortified-wall) 36 | (delete-building guard-tower) 37 | (delete-building house) 38 | (delete-building keep) 39 | (delete-building lumber-camp) 40 | (delete-building market) 41 | (delete-building mining-camp) 42 | (delete-building monastery) 43 | (delete-building palisade-wall) 44 | (delete-building stone-wall) 45 | (delete-building stone-wall-line) 46 | (delete-building town-center) 47 | (delete-building university) 48 | (delete-building watch-tower) 49 | (delete-unit caravel-line) 50 | (delete-unit demolition-ship-line) 51 | (delete-unit fire-ship-line) 52 | (delete-unit fishing-ship) 53 | (delete-unit galley-line) 54 | (delete-unit longboat-line) 55 | (delete-unit trade-cart) 56 | (delete-unit trade-cog) 57 | (delete-unit turtle-ship-line) 58 | (delete-unit villager) 59 | (disable-self) 60 | (disable-timer civ-explore-timer) 61 | (disable-timer defend-timer) 62 | (disable-timer FDrop) 63 | (disable-timer human-coop-timer) 64 | (disable-timer increase-town-size-timer) 65 | (disable-timer increase-town-size-timer) 66 | (disable-timer one-minute-timer) 67 | (disable-timer reset-timer) 68 | (disable-timer reset-town-size-timer) 69 | (disable-timer retreat-timer) 70 | (disable-timer spread-military-timer) 71 | (disable-timer stance) 72 | (disable-timer tensec) 73 | (disable-timer thirty) 74 | (disable-timer tribute-timer2) 75 | (disable-timer two-minute-timer) 76 | (disable-timer under-attack-timer) 77 | (enable-timer spread-military-timer spread-interval) 78 | (release-escrow food) 79 | (release-escrow food)(release-escrow gold) 80 | (release-escrow food)(release-escrow gold) 81 | (release-escrow food)(release-escrow wood) 82 | (release-escrow food)(release-escrow wood)(release-escrow gold) 83 | (release-escrow gold) 84 | (release-escrow gold)(release-escrow stone) 85 | (release-escrow wood) 86 | (release-escrow wood)(release-escrow food) 87 | (release-escrow wood)(release-escrow food)(release-escrow gold) 88 | (release-escrow wood)(release-escrow gold) 89 | (release-escrow wood)(release-escrow stone) 90 | (research castle-age) 91 | (research feudal-age) 92 | (research imperial-age) 93 | (research my-unique-research) 94 | (research my-unique-unit-upgrade) 95 | (research ri-arbalest) 96 | (research ri-architecture) 97 | (research ri-arquebus) 98 | (research ri-arrowslits) 99 | (research ri-arson) 100 | (research ri-atlatl) 101 | (research ri-atonement) 102 | (research ri-ballistics) 103 | (research ri-banking) 104 | (research ri-blast-furnace) 105 | (research ri-block-printing) 106 | (research ri-bloodlines) 107 | (research ri-bodkin-arrow) 108 | (research ri-boiling-oil) 109 | (research ri-bombard-tower) 110 | (research ri-bow-saw) 111 | (research ri-bracer) 112 | (research ri-camel-corps) 113 | (research ri-cannon-galleon) 114 | (research ri-capped-ram) 115 | (research ri-caravan) 116 | (research ri-careening) 117 | (research ri-carrack) 118 | (research ri-cartography) 119 | (research ri-cavalier) 120 | (research ri-chain-barding) 121 | (research ri-chain-mail) 122 | (research ri-champion) 123 | (research ri-chemistry) 124 | (research ri-chieftains) 125 | (research ri-chivalry) 126 | (research ri-coinage) 127 | (research ri-conscription) 128 | (research ri-crop-rotation) 129 | (research ri-crossbow) 130 | (research ri-deck-guns) 131 | (research ri-double-bit-axe) 132 | (research ri-double-crossbow) 133 | (research ri-dry-dock) 134 | (research ri-elite-skirmisher) 135 | (research ri-expeditions) 136 | (research ri-faith) 137 | (research ri-fast-fire-ship) 138 | (research ri-fervor) 139 | (research ri-fletching) 140 | (research ri-forced-levy) 141 | (research ri-forging) 142 | (research ri-galleon) 143 | (research ri-gillnets) 144 | (research ri-gold-mining) 145 | (research ri-gold-shaft-mining) 146 | (research ri-great-wall) 147 | (research ri-greek-fire) 148 | (research ri-guard-tower) 149 | (research ri-guilds) 150 | (research ri-halberdier) 151 | (research ri-hand-cart) 152 | (research ri-heated-shot) 153 | (research ri-heavy-camel) 154 | (research ri-heavy-cavalry-archer) 155 | (research ri-heavy-demolition-ship) 156 | (research ri-heavy-plow) 157 | (research ri-heavy-scorpion) 158 | (research ri-herbal-medicine) 159 | (research ri-heresy) 160 | (research ri-hoardings) 161 | (research ri-horse-collar) 162 | (research ri-howdah) 163 | (research ri-husbandry) 164 | (research ri-hussar) 165 | (research ri-illumination) 166 | (research ri-imperial-camel) 167 | (research ri-imperial-skirmisher) 168 | (research ri-inquisition) 169 | (research ri-iron-casting) 170 | (research ri-ironclad) 171 | (research ri-keep) 172 | (research ri-leather-archer-armor) 173 | (research ri-light-cavalry) 174 | (research ri-long-swordsman) 175 | (research ri-loom) 176 | (research ri-madrasah) 177 | (research ri-man-at-arms) 178 | (research ri-manipur-cavalry) 179 | (research ri-marauders) 180 | (research ri-masonry) 181 | (research ri-mercenaries) 182 | (research ri-murder-holes) 183 | (research ri-nomads) 184 | (research ri-obsidian-arrows) 185 | (research ri-onager) 186 | (research ri-orthodoxy) 187 | (research ri-padded-archer-armor) 188 | (research ri-paladin) 189 | (research ri-panokseon) 190 | (research ri-paper-money) 191 | (research ri-parthian-tactics) 192 | (research ri-pavise) 193 | (research ri-perfusion) 194 | (research ri-pikeman) 195 | (research ri-plate-barding) 196 | (research ri-plate-mail) 197 | (research ri-recurve-bow) 198 | (research ri-redemption) 199 | (research ri-ring-archer-armor) 200 | (research ri-royal-heirs) 201 | (research ri-sanctity) 202 | (research ri-scale-barding) 203 | (research ri-scale-mail) 204 | (research ri-shipwright) 205 | (research ri-siege-engineers) 206 | (research ri-siege-onager) 207 | (research ri-siege-ram) 208 | (research ri-silk-road) 209 | (research ri-squires) 210 | (research ri-stonecutting) 211 | (research ri-stone-mining) 212 | (research ri-stone-shaft-mining) 213 | (research ri-stronghold) 214 | (research ri-sultans) 215 | (research ri-thalassocracy) 216 | (research ri-theocracy) 217 | (research ri-thumb-ring) 218 | (research ri-tigui) 219 | (research ri-torsion) 220 | (research ri-town-watch) 221 | (research ri-tracking) 222 | (research ri-treadmill-crane) 223 | (research ri-tusk-swords) 224 | (research ri-two-handed-swordsman) 225 | (research ri-two-man-saw) 226 | (research ri-war-galley) 227 | (research ri-war-wolf) 228 | (research ri-wheel-barrow) 229 | (research ri-yasama) 230 | (sell-commodity food) 231 | (sell-commodity stone) 232 | (sell-commodity wood) 233 | (set-difficulty-parameter ability-to-dodge-missiles ability-to-dodge) 234 | (set-difficulty-parameter ability-to-maintain-distance ability-to-kite) 235 | (set-goal assistance no) 236 | (set-goal assistance yes) 237 | (set-goal attack-status-goal groups) 238 | (set-goal attack-status-goal retreat) 239 | (set-goal attack-status-goal tsa) 240 | (set-goal control-goal aggressive-rush) 241 | (set-goal control-goal battle-elephant) 242 | (set-goal control-goal belated-flush-defense) 243 | (set-goal control-goal cavalry-archer) 244 | (set-goal control-goal eagle-warrior) 245 | (set-goal control-goal militiaman-line) 246 | (set-goal control-goal my-unique-unit-line) 247 | (set-goal control-goal wonder) 248 | (set-goal enemy-fortifications-goal -1) 249 | (set-goal enemy-goal eagle-warrior) 250 | (set-goal enemy-goal flush) 251 | (set-goal enemy-goal rush) 252 | (set-goal enemy-goal skirmisher) 253 | (set-goal escrow-purpose-goal blacksmith) 254 | (set-goal escrow-purpose-goal my-unique-unit-line) 255 | (set-goal escrow-purpose-goal navy) 256 | (set-goal escrow-purpose-goal ri-halberdier) 257 | (set-goal escrow-purpose-goal town-center) 258 | (set-goal FDTaunt no) 259 | (set-goal FDTaunt yes) 260 | (set-goal FDTaunt2 no) 261 | (set-goal FDTaunt2 yes) 262 | (set-goal forward-goal -1) 263 | (set-goal forward-goal siege-workshop) 264 | (set-goal forward-threat-goal -1) 265 | (set-goal increase-town-size-goal archery-range) 266 | (set-goal increase-town-size-goal barracks) 267 | (set-goal increase-town-size-goal blacksmith) 268 | (set-goal increase-town-size-goal castle) 269 | (set-goal increase-town-size-goal feitoria) 270 | (set-goal increase-town-size-goal house) 271 | (set-goal increase-town-size-goal market) 272 | (set-goal increase-town-size-goal monastery) 273 | (set-goal increase-town-size-goal siege-workshop) 274 | (set-goal increase-town-size-goal stable) 275 | (set-goal increase-town-size-goal town-center) 276 | (set-goal increase-town-size-goal university) 277 | (set-goal landnomad no) 278 | (set-goal landnomad yes) 279 | (set-goal max-navy minNavy) 280 | (set-goal nomad no) 281 | (set-goal nomad yes) 282 | (set-goal position-goal flank) 283 | (set-goal position-goal pocket) 284 | (set-goal ranged-unit-type-goal archer) 285 | (set-goal ranged-unit-type-goal cavalry-archer) 286 | (set-goal ranged-unit-type-goal default-ranged) 287 | (set-goal ranged-unit-type-goal gunpowder-class) 288 | (set-goal ranged-unit-type-goal monk) 289 | (set-goal ranged-unit-type-goal scorpion) 290 | (set-goal ranged-unit-type-goal slinger) 291 | (set-goal ranged-unit-type-goal uu) 292 | (set-goal retargetenemy yes) 293 | (set-goal save-wood-goal siege-workshop) 294 | (set-goal strategy-goal boom) 295 | (set-goal strategy-goal default-strategy) 296 | (set-goal strategy-goal fast-imp) 297 | (set-goal strategy-goal flush) 298 | (set-goal strategy-goal pocket-strategy) 299 | (set-goal strategy-goal rush) 300 | (set-goal train-civ-goal -1) 301 | (set-goal train-civ-goal navy) 302 | (set-goal train-civ-goal ri-bodkin-arrow) 303 | (set-goal train-civ-goal ri-chain-barding) 304 | (set-goal train-civ-goal ri-chain-mail) 305 | (set-goal train-civ-goal ri-crossbow) 306 | (set-goal train-civ-goal ri-elite-eagle-warrior) 307 | (set-goal train-civ-goal ri-fletching) 308 | (set-goal train-civ-goal ri-pikeman) 309 | (set-goal train-civ-goal wonder) 310 | (set-goal unit-goal archer) 311 | (set-goal unit-goal cavalry-archer) 312 | (set-goal unit-goal default-flush-unit) 313 | (set-goal unit-goal default-ranged) 314 | (set-goal unit-goal default-unit) 315 | (set-goal unit-goal gunpowder-class) 316 | (set-goal unit-goal knight) 317 | (set-goal unit-goal militiaman-line) 318 | (set-goal unit-goal mix) 319 | (set-goal unit-goal monk) 320 | (set-goal unit-goal my-unique-unit-line) 321 | (set-goal unit-goal no-gold-flush-unit) 322 | (set-goal unit-goal pocket-unit) 323 | (set-goal unit-goal scorpion) 324 | (set-goal unit-goal scout-cavalry) 325 | (set-goal unit-goal skirmisher) 326 | (set-goal unit-goal wonder) 327 | (set-strategic-number sn-blot-size blot-size) 328 | (set-strategic-number sn-camp-max-distance camp-distance) 329 | (set-strategic-number sn-camp-max-distance castle-age-camp-distance) 330 | (set-strategic-number sn-current-age castle) 331 | (set-strategic-number sn-current-age ci-transit) 332 | (set-strategic-number sn-current-age dark) 333 | (set-strategic-number sn-current-age df-transit) 334 | (set-strategic-number sn-current-age fc-transit) 335 | (set-strategic-number sn-current-age feudal) 336 | (set-strategic-number sn-current-age imperial) 337 | (set-strategic-number sn-enable-patrol-attack enable-patrol) 338 | (set-strategic-number sn-food-gatherer-percentage ci-uu-switch-food) 339 | (set-strategic-number sn-food-gatherer-percentage pidm-food) 340 | (set-strategic-number sn-food-gatherer-percentage pidm-food2) 341 | (set-strategic-number sn-food-gatherer-percentage uu-food2) 342 | (set-strategic-number sn-gold-gatherer-percentage ci-uu-switch-gold) 343 | (set-strategic-number sn-gold-gatherer-percentage pidm-gold) 344 | (set-strategic-number sn-gold-gatherer-percentage pidm-gold2) 345 | (set-strategic-number sn-gold-gatherer-percentage uu-gold2) 346 | (set-strategic-number sn-home-exploration-time home-exploration-time) 347 | (set-strategic-number sn-local-targeting-mode local-targeting) 348 | (set-strategic-number sn-maximum-fish-boat-drop-distance -1) 349 | (set-strategic-number sn-maximum-food-drop-distance food-distance) 350 | (set-strategic-number sn-maximum-hunt-drop-distance hunt-distance) 351 | (set-strategic-number sn-maximum-town-size pop120-town-size) 352 | (set-strategic-number sn-maximum-town-size pop60-town-size) 353 | (set-strategic-number sn-maximum-town-size pop90-town-size) 354 | (set-strategic-number sn-maximum-wood-drop-distance wood-distance) 355 | (set-strategic-number sn-maximum-wood-drop-distance wood-distance2) 356 | (set-strategic-number sn-military-level -1) 357 | (set-strategic-number sn-mill-max-distance mill-distance) 358 | (set-strategic-number sn-minimum-water-body-size-for-dock water-islands) 359 | (set-strategic-number sn-minimum-water-body-size-for-dock water-mixed) 360 | (set-strategic-number sn-preferred-trade-distance trade-distance) 361 | (set-strategic-number sn-resource-control battering-ram) 362 | (set-strategic-number sn-resource-control mangonel) 363 | (set-strategic-number sn-resource-control monk) 364 | (set-strategic-number sn-resource-control navy) 365 | (set-strategic-number sn-resource-control ri-arbalest) 366 | (set-strategic-number sn-resource-control ri-bodkin-arrow) 367 | (set-strategic-number sn-resource-control ri-chain-barding) 368 | (set-strategic-number sn-resource-control ri-chain-mail) 369 | (set-strategic-number sn-resource-control ri-chemistry) 370 | (set-strategic-number sn-resource-control ri-crossbow) 371 | (set-strategic-number sn-resource-control ri-eagle-warrior) 372 | (set-strategic-number sn-resource-control ri-elite-eagle-warrior) 373 | (set-strategic-number sn-resource-control ri-elite-skirmisher) 374 | (set-strategic-number sn-resource-control ri-inquisition) 375 | (set-strategic-number sn-resource-control ri-orthodoxy) 376 | (set-strategic-number sn-resource-control ri-pikeman) 377 | (set-strategic-number sn-safe-town-size safe-town) 378 | (set-strategic-number sn-special-attack-type2 -1) 379 | (set-strategic-number sn-special-attack-type2 special-attack-type2) 380 | (set-strategic-number sn-stone-gatherer-percentage ci-uu-switch-stone) 381 | (set-strategic-number sn-stone-gatherer-percentage pidm-stone) 382 | (set-strategic-number sn-stone-gatherer-percentage pidm-stone2) 383 | (set-strategic-number sn-stone-gatherer-percentage uu-stone2) 384 | (set-strategic-number sn-target-evaluation-distance evaluation-distance) 385 | (set-strategic-number sn-target-evaluation-hitpoints evaluation-hitpoints) 386 | (set-strategic-number sn-target-evaluation-range -100) 387 | (set-strategic-number sn-town-center-placement lumber-camp) 388 | (set-strategic-number sn-town-center-placement mining-camp) 389 | (set-strategic-number sn-wood-gatherer-percentage ci-uu-switch-wood) 390 | (set-strategic-number sn-wood-gatherer-percentage pidm-wood) 391 | (set-strategic-number sn-wood-gatherer-percentage pidm-wood2) 392 | (set-strategic-number sn-wood-gatherer-percentage uu-wood2) 393 | (train archer-line) 394 | (train battering-ram-line) 395 | (train bombard-cannon) 396 | (train camel-line) 397 | (train cannon-galleon-line) 398 | (train caravel-line) 399 | (train cavalry-archer-line) 400 | (train demolition-ship-line) 401 | (train eagle-warrior-line) 402 | (train elephant-archer-line) 403 | (train extra-anti-cav-unit) 404 | (train fire-ship-line) 405 | (train fishing-ship) 406 | (train hand-cannoneer) 407 | (train knight-line) 408 | (train longboat-line) 409 | (train mangonel-line) 410 | (train militiaman-line) 411 | (train missionary) 412 | (train monk) 413 | (train my-unique-unit-line) 414 | (train petard) 415 | (train scorpion-line) 416 | (train scout-cavalry-line) 417 | (train skirmisher-line) 418 | (train slinger) 419 | (train spearman-line) 420 | (train trebuchet) 421 | (train villager) 422 | (up-assign-builders c: blacksmith c: 3)(disable-self) 423 | (up-assign-builders c: market c: 2)(disable-self) 424 | (up-change-name -1) 425 | (up-find-player enemy find-closest closest-enemy-goal) 426 | (up-get-fact building-type-count farm math-goal2) 427 | (up-get-fact building-type-count-total feitoria math-goal) 428 | (up-get-fact resource-amount amount-relics relic-count) 429 | (up-get-fact unit-type-count fishing-ship math-goal) 430 | (up-get-fact unit-type-count turtle-ship-line math-goal) 431 | (up-get-fact unit-type-count villager math-goal) 432 | (up-get-fact unit-type-count-total trade-cart trade-units) 433 | (up-get-fact unit-type-count-total trade-cog math-goal) 434 | (up-get-fact-max any-enemy unit-type-count cannon-galleon-line math-goal) 435 | (up-get-fact-max any-enemy unit-type-count fire-ship-line math-goal) 436 | (up-get-fact-max any-enemy unit-type-count galley-line math-goal) 437 | (up-get-fact-max any-enemy unit-type-count longboat-line math-goal) 438 | (up-get-fact-max any-enemy unit-type-count turtle-ship-line math-goal) 439 | (up-get-focus-fact unit-type-count cannon-galleon-line math-goal) 440 | (up-get-focus-fact unit-type-count fire-ship-line math-goal) 441 | (up-get-focus-fact unit-type-count galley-line math-goal) 442 | (up-get-focus-fact unit-type-count longboat-line math-goal) 443 | (up-get-focus-fact unit-type-count turtle-ship-line math-goal) 444 | (up-get-threat-data threat-time threat-player threat-source threat-target) 445 | (up-get-treaty-data treaty-time) 446 | (up-get-victory-data winning-player victory-type victory-time) 447 | (up-jump-rule 1) 448 | (up-jump-rule -2) 449 | (up-jump-rule -4) 450 | (up-modify-goal attacking-enemy-goal g:= threat-player) 451 | (up-modify-goal biggest-enemy-navy g:+ math-goal) 452 | (up-modify-goal biggest-enemy-navy g:= math-goal) 453 | (up-modify-goal control-goal g:= unit-goal) 454 | (up-modify-goal custom-civ-pop g:+ math-goal) 455 | (up-modify-goal custom-civ-pop g:+ relic-count) 456 | (up-modify-goal enemies g:+ math-goal) 457 | (up-modify-goal enemy-focus-navy g:+ math-goal) 458 | (up-modify-goal enemy-focus-navy g:= math-goal) 459 | (up-modify-goal math-goal c:- max-civ) 460 | (up-modify-goal math-goal c:= max-pop) 461 | (up-modify-goal math-goal g:- math-goal2) 462 | (up-modify-goal math-goal g:= trade-units) 463 | (up-modify-goal math-goal s:%* sn-food-gatherer-percentage) 464 | (up-modify-goal math-goal s:= sn-focus-player-number) 465 | (up-modify-goal math-goal s:min sn-food-gatherer-percentage) 466 | (up-modify-goal math-goal s:min sn-gold-gatherer-percentage) 467 | (up-modify-goal math-goal2 g:= math-goal) 468 | (up-modify-goal math-goal3 c:= minNavy) 469 | (up-modify-goal math-goal3 g:- math-goal4) 470 | (up-modify-goal math-goal3 g:* enemies) 471 | (up-modify-goal math-goal4 g:- math-goal2) 472 | (up-modify-goal math-goal4 g:- math-goal3) 473 | (up-modify-goal math-goal4 g:+ navy-count) 474 | (up-modify-goal max-navy c:+ minNavy) 475 | (up-modify-goal max-navy c:max minNavy) 476 | (up-modify-goal max-navy g:= biggest-enemy-navy) 477 | (up-modify-goal max-navy g:= enemy-focus-navy) 478 | (up-modify-goal max-navy g:max math-goal) 479 | (up-modify-goal navy-count g:+ math-goal) 480 | (up-modify-goal trade-units g:+ math-goal) 481 | (up-modify-goal trade-units g:+ relic-count) 482 | (up-modify-sn sn-civilian-number g:= math-goal3) 483 | (up-modify-sn sn-focus-player-number g:= closest-enemy-goal) 484 | (up-modify-sn sn-focus-player-number g:= math-goal) 485 | (up-modify-sn sn-focus-player-number g:= threat-player) 486 | (up-modify-sn sn-focus-player-number g:= winning-player) 487 | (up-modify-sn sn-focus-player-number s:== sn-target-player-number) 488 | (up-modify-sn sn-food-gatherer-percentage s:+ sn-food-modifier-percentage) 489 | (up-modify-sn sn-food-modifier-percentage g:- math-goal) 490 | (up-modify-sn sn-food-modifier-percentage g:+ math-goal) 491 | (up-modify-sn sn-food-modifier-percentage g:+ math-goal2) 492 | (up-modify-sn sn-gold-gatherer-percentage s:+ sn-gold-modifier-percentage) 493 | (up-modify-sn sn-gold-modifier-percentage g:- math-goal) 494 | (up-modify-sn sn-home-exploration-time c:= home-exploration-time) 495 | (up-modify-sn sn-maximum-food-drop-distance c:max food-distance) 496 | (up-modify-sn sn-maximum-town-size c:+ tsa-increment) 497 | (up-modify-sn sn-maximum-wood-drop-distance c:max wood-distance) 498 | (up-modify-sn sn-maximum-wood-drop-distance c:max wood-distance2) 499 | (up-modify-sn sn-stone-gatherer-percentage s:+ sn-stone-modifier-percentage) 500 | (up-modify-sn sn-target-player-number g:= closest-enemy-goal) 501 | (up-modify-sn sn-target-player-number g:= winning-player) 502 | (up-modify-sn sn-total-number g:= math-goal3) 503 | (up-modify-sn sn-wood-gatherer-percentage s:+ sn-wood-modifier-percentage) 504 | (up-modify-sn sn-wood-modifier-percentage g:+ math-goal) 505 | (up-modify-sn sn-wood-modifier-percentage g:+ math-goal2) 506 | (up-modify-sn teambalance g:- enemies) 507 | (up-modify-sn teambalance g:+ allies) 508 | (up-reset-unit c: -1) 509 | (up-reset-unit c: -1) 510 | (up-retreat-now) 511 | (up-send-scout group-type-land-explore scout-enemy) 512 | (up-send-scout group-type-land-explore scouting-type) 513 | (up-send-scout group-type-water-explore scouting-type) 514 | 515 | (build-gate AlphaMorphValue ) 516 | (build-wall AlphaMorphValue stone-wall-line) 517 | (delete-building AlphaMorphValue ) 518 | (enable-timer AlphaMorphValue AlphaMorphValue ) 519 | (enable-timer attack-timer AlphaMorphValue ) 520 | (enable-timer boar-timer AlphaMorphValue ) 521 | (enable-timer civ-explore-timer AlphaMorphValue ) 522 | (enable-timer defend-timer AlphaMorphValue ) 523 | (enable-timer FDrop AlphaMorphValue ) 524 | (enable-timer garrison-timer AlphaMorphValue ) 525 | (enable-timer help-ally-timer AlphaMorphValue ) 526 | (enable-timer human-coop-timer AlphaMorphValue ) 527 | (enable-timer increase-town-size-goal AlphaMorphValue ) 528 | (enable-timer increase-town-size-timer AlphaMorphValue ) 529 | (enable-timer lumber-timer AlphaMorphValue ) 530 | (enable-timer micro-timer AlphaMorphValue ) 531 | (enable-timer navy-attack-timer AlphaMorphValue ) 532 | (enable-timer one-minute-timer AlphaMorphValue ) 533 | (enable-timer reset-timer AlphaMorphValue ) 534 | (enable-timer reset-town-size-timer AlphaMorphValue ) 535 | (enable-timer resign-timer AlphaMorphValue ) 536 | (enable-timer retreat-timer AlphaMorphValue ) 537 | (enable-timer scouting-timer AlphaMorphValue ) 538 | (enable-timer spread-military-timer AlphaMorphValue ) 539 | (enable-timer stance AlphaMorphValue ) 540 | (enable-timer tensec AlphaMorphValue ) 541 | (enable-timer thirty AlphaMorphValue ) 542 | (enable-timer tribute-timer AlphaMorphValue ) 543 | (enable-timer tribute-timer2 AlphaMorphValue ) 544 | (enable-timer two-minute-timer AlphaMorphValue ) 545 | (enable-timer under-attack-timer AlphaMorphValue ) 546 | (enable-wall-placement AlphaMorphValue ) 547 | (generate-random-number AlphaMorphValue ) 548 | (release-escrow food)(set-escrow-percentage food AlphaMorphValue ) 549 | (release-escrow wood)(set-escrow-percentage wood AlphaMorphValue ) 550 | (research AlphaMorphValue ) 551 | (set-difficulty-parameter ability-to-dodge-missiles AlphaMorphValue ) 552 | (set-difficulty-parameter ability-to-maintain-distance AlphaMorphValue ) 553 | (set-escrow-percentage food 0)(set-escrow-percentage gold AlphaMorphValue ) 554 | (set-escrow-percentage food 0)(set-escrow-percentage wood 0)(set-escrow-percentage gold AlphaMorphValue ) 555 | (set-escrow-percentage food 10)(set-escrow-percentage gold AlphaMorphValue ) 556 | (set-escrow-percentage food 20)(set-escrow-percentage gold AlphaMorphValue ) 557 | (set-escrow-percentage food 30)(set-escrow-percentage gold AlphaMorphValue ) 558 | (set-escrow-percentage food AlphaMorphValue ) 559 | (set-escrow-percentage gold AlphaMorphValue ) 560 | (set-escrow-percentage stone AlphaMorphValue ) 561 | (set-escrow-percentage wood 0)(set-escrow-percentage food 0)(set-escrow-percentage gold AlphaMorphValue ) 562 | (set-escrow-percentage wood 0)(set-escrow-percentage food AlphaMorphValue ) 563 | (set-escrow-percentage wood 0)(set-escrow-percentage gold AlphaMorphValue ) 564 | (set-escrow-percentage wood 0)(set-escrow-percentage stone AlphaMorphValue ) 565 | (set-escrow-percentage wood 20)(set-escrow-percentage food AlphaMorphValue ) 566 | (set-escrow-percentage wood 20)(set-escrow-percentage gold AlphaMorphValue ) 567 | (set-escrow-percentage wood 30)(set-escrow-percentage food AlphaMorphValue ) 568 | (set-escrow-percentage wood 30)(set-escrow-percentage gold AlphaMorphValue ) 569 | (set-escrow-percentage wood AlphaMorphValue ) 570 | (set-goal anti-cavalry-threat-goal AlphaMorphValue ) 571 | (set-goal anti-monk-threat-goal AlphaMorphValue ) 572 | (set-goal attack-goal AlphaMorphValue ) 573 | (set-goal attack-status-goal AlphaMorphValue ) 574 | (set-goal biggest-enemy-navy AlphaMorphValue ) 575 | (set-goal control-goal AlphaMorphValue ) 576 | (set-goal enemy-boats-goal AlphaMorphValue ) 577 | (set-goal enemy-focus-navy AlphaMorphValue ) 578 | (set-goal enemy-fortifications-goal AlphaMorphValue ) 579 | (set-goal enemy-goal AlphaMorphValue ) 580 | (set-goal enemy-sighted-goal AlphaMorphValue ) 581 | (set-goal escrow-purpose-goal AlphaMorphValue ) 582 | (set-goal farm-goal AlphaMorphValue ) 583 | (set-goal ffa-game-goal AlphaMorphValue ) 584 | (set-goal forgotten AlphaMorphValue ) 585 | (set-goal forward-goal AlphaMorphValue ) 586 | (set-goal forward-threat-goal AlphaMorphValue ) 587 | (set-goal gather-inside-goal AlphaMorphValue ) 588 | (set-goal hostilities-goal AlphaMorphValue ) 589 | (set-goal housing-goal AlphaMorphValue ) 590 | (set-goal increase-town-size-goal AlphaMorphValue ) 591 | (set-goal math-goal AlphaMorphValue ) 592 | (set-goal math-goal2 AlphaMorphValue ) 593 | (set-goal meso-enemy-goal AlphaMorphValue ) 594 | (set-goal monk-threat-goal AlphaMorphValue ) 595 | (set-goal navy-count AlphaMorphValue ) 596 | (set-goal need-loom-goal AlphaMorphValue ) 597 | (set-goal nr-map-goal AlphaMorphValue ) 598 | (set-goal position-goal AlphaMorphValue ) 599 | (set-goal raid-goal AlphaMorphValue ) 600 | (set-goal ranged-unit-type-goal AlphaMorphValue ) 601 | (set-goal request AlphaMorphValue ) 602 | (set-goal request2 AlphaMorphValue ) 603 | (set-goal reset AlphaMorphValue ) 604 | (set-goal restart-attack-goal AlphaMorphValue ) 605 | (set-goal retargetenemy AlphaMorphValue ) 606 | (set-goal retreat-now-goal AlphaMorphValue ) 607 | (set-goal save-wood-goal AlphaMorphValue ) 608 | (set-goal spread-military-goal AlphaMorphValue ) 609 | (set-goal stanceg AlphaMorphValue ) 610 | (set-goal team-coordination-goal AlphaMorphValue ) 611 | (set-goal threat-source AlphaMorphValue ) 612 | (set-goal threat-target AlphaMorphValue ) 613 | (set-goal train-civ-goal AlphaMorphValue ) 614 | (set-goal tribute-goal AlphaMorphValue ) 615 | (set-goal under-attack-goal AlphaMorphValue ) 616 | (set-goal uu-up-goal AlphaMorphValue ) 617 | (set-strategic-number sn-allow-adjacent-dropsites AlphaMorphValue ) 618 | (set-strategic-number sn-allow-civilian-defense AlphaMorphValue ) 619 | (set-strategic-number sn-allow-civilian-offense AlphaMorphValue ) 620 | (set-strategic-number sn-archer-threat AlphaMorphValue ) 621 | (set-strategic-number sn-attack-group-gather-spacing AlphaMorphValue ) 622 | (set-strategic-number sn-attack-group-size-randomness AlphaMorphValue ) 623 | (set-strategic-number sn-attack-intelligence AlphaMorphValue ) 624 | (set-strategic-number sn-attack-winning-player AlphaMorphValue ) 625 | (set-strategic-number sn-attack-winning-player-factor AlphaMorphValue ) 626 | (set-strategic-number sn-blot-exploration-map AlphaMorphValue ) 627 | (set-strategic-number sn-build-frequency AlphaMorphValue ) 628 | (set-strategic-number sn-camp-max-distance AlphaMorphValue ) 629 | (set-strategic-number sn-cap-civilian-builders AlphaMorphValue ) 630 | (set-strategic-number sn-cap-civilian-explorers AlphaMorphValue ) 631 | (set-strategic-number sn-cap-civilian-gatherers AlphaMorphValue ) 632 | (set-strategic-number sn-cavalry-threat AlphaMorphValue ) 633 | (set-strategic-number sn-civilian-number AlphaMorphValue ) 634 | (set-strategic-number sn-consecutive-idle-unit-limit AlphaMorphValue ) 635 | (set-strategic-number sn-coop-share-attacking AlphaMorphValue ) 636 | (set-strategic-number sn-coop-share-attacking-interval AlphaMorphValue ) 637 | (set-strategic-number sn-coop-share-information AlphaMorphValue ) 638 | (set-strategic-number sn-defense-distance AlphaMorphValue ) 639 | (set-strategic-number sn-defer-dropsite-update AlphaMorphValue ) 640 | (set-strategic-number sn-disable-builder-assistance AlphaMorphValue ) 641 | (set-strategic-number sn-disable-trade-evasion AlphaMorphValue ) 642 | (set-strategic-number sn-dock-training-filter AlphaMorphValue ) 643 | (set-strategic-number sn-do-not-scale-for-difficulty-level AlphaMorphValue ) 644 | (set-strategic-number sn-dropsite-separation-distance AlphaMorphValue ) 645 | (set-strategic-number sn-enable-boar-hunting AlphaMorphValue ) 646 | (set-strategic-number sn-enable-new-building-system AlphaMorphValue ) 647 | (set-strategic-number sn-enable-patrol-attack AlphaMorphValue ) 648 | (set-strategic-number sn-enable-training-queue AlphaMorphValue ) 649 | (set-strategic-number sn-enemy-sighted-response-distance AlphaMorphValue ) 650 | (set-strategic-number sn-focus-player-number AlphaMorphValue ) 651 | (set-strategic-number sn-food-gatherer-percentage AlphaMorphValue ) 652 | (set-strategic-number sn-food-gatherer-percentage AlphaMorphValue ) 653 | (set-strategic-number sn-food-gatherer-percentage AlphaMorphValue ) 654 | (set-strategic-number sn-food-modifier-percentage AlphaMorphValue ) 655 | (set-strategic-number sn-forage-defend-priority AlphaMorphValue ) 656 | (set-strategic-number sn-garrison-rams AlphaMorphValue ) 657 | (set-strategic-number sn-gather-defense-units AlphaMorphValue ) 658 | (set-strategic-number sn-gather-idle-soldiers-at-center AlphaMorphValue ) 659 | (set-strategic-number sn-gold-defend-priority AlphaMorphValue ) 660 | (set-strategic-number sn-gold-dropsite-distance AlphaMorphValue ) 661 | (set-strategic-number sn-gold-gatherer-percentage AlphaMorphValue ) 662 | (set-strategic-number sn-gold-gatherer-percentage AlphaMorphValue ) 663 | (set-strategic-number sn-gold-gatherer-percentage AlphaMorphValue ) 664 | (set-strategic-number sn-gold-gatherer-percentage AlphaMorphValue ) 665 | (set-strategic-number sn-gold-modifier-percentage AlphaMorphValue ) 666 | (set-strategic-number sn-group-commander-selection-method AlphaMorphValue ) 667 | (set-strategic-number sn-group-form-distance AlphaMorphValue ) 668 | (set-strategic-number sn-group-leader-defense-distance AlphaMorphValue ) 669 | (set-strategic-number sn-ignore-attack-group-under-attack AlphaMorphValue ) 670 | (set-strategic-number sn-ignore-tower-elevation AlphaMorphValue ) 671 | (set-strategic-number sn-infantry-threat AlphaMorphValue ) 672 | (set-strategic-number sn-initial-exploration-required AlphaMorphValue ) 673 | (set-strategic-number sn-intelligent-gathering AlphaMorphValue ) 674 | (set-strategic-number sn-livestock-defend-priority AlphaMorphValue ) 675 | (set-strategic-number sn-livestock-to-town-center AlphaMorphValue ) 676 | (set-strategic-number sn-maximum-attack-group-size AlphaMorphValue ) 677 | (set-strategic-number sn-maximum-boat-attack-group-size AlphaMorphValue ) 678 | (set-strategic-number sn-maximum-food-drop-distance AlphaMorphValue ) 679 | (set-strategic-number sn-maximum-food-drop-distance AlphaMorphValue ) 680 | (set-strategic-number sn-maximum-gaia-attack-response AlphaMorphValue ) 681 | (set-strategic-number sn-maximum-gold-drop-distance AlphaMorphValue ) 682 | (set-strategic-number sn-maximum-gold-drop-distance AlphaMorphValue ) 683 | (set-strategic-number sn-maximum-hunt-drop-distance AlphaMorphValue ) 684 | (set-strategic-number sn-maximum-hunt-drop-distance AlphaMorphValue ) 685 | (set-strategic-number sn-maximum-stone-drop-distance AlphaMorphValue ) 686 | (set-strategic-number sn-maximum-town-size AlphaMorphValue ) 687 | (set-strategic-number sn-maximum-wood-drop-distance AlphaMorphValue ) 688 | (set-strategic-number sn-maximum-wood-drop-distance AlphaMorphValue ) 689 | (set-strategic-number sn-max-skips-per-attempt AlphaMorphValue ) 690 | (set-strategic-number sn-military-level AlphaMorphValue ) 691 | (set-strategic-number sn-mill-max-distance AlphaMorphValue ) 692 | (set-strategic-number sn-minimum-attack-group-size AlphaMorphValue ) 693 | (set-strategic-number sn-minimum-boar-hunt-group-size AlphaMorphValue ) 694 | (set-strategic-number sn-minimum-boat-attack-group-size AlphaMorphValue ) 695 | (set-strategic-number sn-minimum-civilian-explorers AlphaMorphValue ) 696 | (set-strategic-number sn-minimum-dropsite-buffer AlphaMorphValue ) 697 | (set-strategic-number sn-minimum-explore-group-size AlphaMorphValue ) 698 | (set-strategic-number sn-minimum-number-hunters AlphaMorphValue ) 699 | (set-strategic-number sn-minimum-water-body-size-for-dock AlphaMorphValue ) 700 | (set-strategic-number sn-number-attack-groups AlphaMorphValue ) 701 | (set-strategic-number sn-number-boat-attack-groups AlphaMorphValue ) 702 | (set-strategic-number sn-number-boat-explore-groups AlphaMorphValue ) 703 | (set-strategic-number sn-number-build-attempts-before-skip AlphaMorphValue ) 704 | (set-strategic-number sn-number-civilian-militia AlphaMorphValue ) 705 | (set-strategic-number sn-number-enemy-objects-required AlphaMorphValue ) 706 | (set-strategic-number sn-number-explore-groups AlphaMorphValue ) 707 | (set-strategic-number sn-percent-attack-soldiers AlphaMorphValue ) 708 | (set-strategic-number sn-percent-building-cancellation AlphaMorphValue ) 709 | (set-strategic-number sn-percent-civilian-builders AlphaMorphValue ) 710 | (set-strategic-number sn-percent-civilian-explorers AlphaMorphValue ) 711 | (set-strategic-number sn-percent-civilian-gatherers AlphaMorphValue ) 712 | (set-strategic-number sn-percent-enemy-sighted-response AlphaMorphValue ) 713 | (set-strategic-number sn-placement-zone-size AlphaMorphValue ) 714 | (set-strategic-number sn-preferred-mill-placement AlphaMorphValue ) 715 | (set-strategic-number sn-preferred-trade-distance AlphaMorphValue ) 716 | (set-strategic-number sn-resource-control AlphaMorphValue ) 717 | (set-strategic-number sn-retask-gather-amount AlphaMorphValue ) 718 | (set-strategic-number sn-scale-maximum-attack-group-size AlphaMorphValue ) 719 | (set-strategic-number sn-scale-minimum-attack-group-size AlphaMorphValue ) 720 | (set-strategic-number sn-sentry-distance AlphaMorphValue ) 721 | (set-strategic-number sn-sentry-distance-variation AlphaMorphValue ) 722 | (set-strategic-number sn-special-attack-influence1 AlphaMorphValue ) 723 | (set-strategic-number sn-special-attack-type1 AlphaMorphValue ) 724 | (set-strategic-number sn-special-attack-type3 AlphaMorphValue ) 725 | (set-strategic-number sn-stone-defend-priority AlphaMorphValue ) 726 | (set-strategic-number sn-stone-dropsite-distance AlphaMorphValue ) 727 | (set-strategic-number sn-stone-gatherer-percentage AlphaMorphValue ) 728 | (set-strategic-number sn-stone-gatherer-percentage AlphaMorphValue ) 729 | (set-strategic-number sn-stone-modifier-percentage AlphaMorphValue ) 730 | (set-strategic-number sn-target-evaluation-ally-proximity AlphaMorphValue ) 731 | (set-strategic-number sn-target-evaluation-attack-attempts AlphaMorphValue ) 732 | (set-strategic-number sn-target-evaluation-boat AlphaMorphValue ) 733 | (set-strategic-number sn-target-evaluation-continent AlphaMorphValue ) 734 | (set-strategic-number sn-target-evaluation-damage-capability AlphaMorphValue ) 735 | (set-strategic-number sn-target-evaluation-in-progress AlphaMorphValue ) 736 | (set-strategic-number sn-target-evaluation-kills AlphaMorphValue ) 737 | (set-strategic-number sn-target-evaluation-randomness AlphaMorphValue ) 738 | (set-strategic-number sn-target-evaluation-rof AlphaMorphValue ) 739 | (set-strategic-number sn-target-evaluation-siege-weapon AlphaMorphValue ) 740 | (set-strategic-number sn-target-evaluation-time-kill-ratio AlphaMorphValue ) 741 | (set-strategic-number sn-target-player-number AlphaMorphValue ) 742 | (set-strategic-number sn-task-ungrouped-soldiers AlphaMorphValue ) 743 | (set-strategic-number sn-total-number AlphaMorphValue ) 744 | (set-strategic-number sn-total-number-explorers AlphaMorphValue ) 745 | (set-strategic-number sn-town-center-placement AlphaMorphValue ) 746 | (set-strategic-number sn-ttkfactor-scalar AlphaMorphValue ) 747 | (set-strategic-number sn-wall-targeting-mode AlphaMorphValue ) 748 | (set-strategic-number sn-wood-dropsite-distance AlphaMorphValue ) 749 | (set-strategic-number sn-wood-gatherer-percentage AlphaMorphValue ) 750 | (set-strategic-number sn-wood-gatherer-percentage AlphaMorphValue ) 751 | (set-strategic-number sn-wood-gatherer-percentage AlphaMorphValue ) 752 | (set-strategic-number sn-wood-gatherer-percentage AlphaMorphValue ) 753 | (set-strategic-number sn-wood-modifier-percentage AlphaMorphValue ) 754 | (set-strategic-number sn-zero-priority-distance AlphaMorphValue ) 755 | (up-assign-builders c: archery-range c: AlphaMorphValue ) 756 | (up-assign-builders c: bombard-tower c: AlphaMorphValue ) 757 | (up-assign-builders c: castle c: AlphaMorphValue ) 758 | (up-assign-builders c: house c: AlphaMorphValue ) 759 | (up-assign-builders c: market c: AlphaMorphValue ) 760 | (up-assign-builders c: stable c: AlphaMorphValue ) 761 | (up-assign-builders c: town-center c: AlphaMorphValue ) 762 | (up-assign-builders c: town-center-foundation c: AlphaMorphValue ) 763 | (up-assign-builders c: watch-tower c: AlphaMorphValue ) 764 | (up-build place-control AlphaMorphValue c: watch-tower) 765 | (up-drop-resources AlphaMorphValue c: AlphaMorphValue ) 766 | (up-drop-resources food c: AlphaMorphValue ) 767 | (up-drop-resources gold c: AlphaMorphValue ) 768 | (up-drop-resources sea-fish c: AlphaMorphValue ) 769 | (up-drop-resources shore-fish c: AlphaMorphValue ) 770 | (up-get-fact civilian-population AlphaMorphValue custom-civ-pop) 771 | (up-get-fact civilian-population AlphaMorphValue math-goal3) 772 | (up-get-fact food-amount AlphaMorphValue math-goal) 773 | (up-get-fact gold-amount AlphaMorphValue math-goal) 774 | (up-get-fact military-population AlphaMorphValue math-goal) 775 | (up-get-fact population AlphaMorphValue math-goal) 776 | (up-get-fact population AlphaMorphValue math-goal3) 777 | (up-get-fact population-cap AlphaMorphValue feitoria-cap) 778 | (up-get-fact stone-amount AlphaMorphValue math-goal) 779 | (up-get-fact treaty-time AlphaMorphValue AlphaMorphValue ) 780 | (up-get-fact warboat-count AlphaMorphValue navy-count) 781 | (up-get-fact wood-amount AlphaMorphValue math-goal) 782 | (up-get-fact-sum any-ally player-in-game AlphaMorphValue allies) 783 | (up-get-fact-sum any-ally warboat-count AlphaMorphValue math-goal4) 784 | (up-get-fact-sum any-enemy player-in-game AlphaMorphValue math-goal) 785 | (up-get-fact-sum any-enemy warboat-count AlphaMorphValue math-goal2) 786 | (up-get-fact-sum any-neutral player-in-game AlphaMorphValue enemies) 787 | (up-get-target-fact civilian-population AlphaMorphValue math-goal4) 788 | (up-get-target-fact military-population AlphaMorphValue math-goal2) 789 | (up-get-target-fact population AlphaMorphValue math-goal4) 790 | (up-jump-rule AlphaMorphValue ) 791 | (up-modify-goal feitoria-cap c:z/ AlphaMorphValue ) 792 | (up-modify-goal jumpgoal c:+ AlphaMorphValue ) 793 | (up-modify-goal math-goal c:- AlphaMorphValue ) 794 | (up-modify-goal math-goal c:* AlphaMorphValue ) 795 | (up-modify-goal math-goal c:/ AlphaMorphValue ) 796 | (up-modify-goal math-goal c:max AlphaMorphValue ) 797 | (up-modify-goal math-goal c:min AlphaMorphValue ) 798 | (up-modify-goal math-goal c:z/ AlphaMorphValue ) 799 | (up-modify-goal math-goal2 c:max AlphaMorphValue ) 800 | (up-modify-goal math-goal2 c:mod AlphaMorphValue ) 801 | (up-modify-goal math-goal4 c:max AlphaMorphValue ) 802 | (up-modify-goal max-navy c:max AlphaMorphValue ) 803 | (up-modify-sn sn-allow-civilian-defense c:max AlphaMorphValue ) 804 | (up-modify-sn sn-allow-civilian-defense c:min AlphaMorphValue ) 805 | (up-modify-sn sn-allow-civilian-offense c:max AlphaMorphValue ) 806 | (up-modify-sn sn-camp-max-distance c:+ AlphaMorphValue ) 807 | (up-modify-sn sn-focus-player-number c:+ AlphaMorphValue ) 808 | (up-modify-sn sn-food-modifier-percentage c:- AlphaMorphValue ) 809 | (up-modify-sn sn-gold-modifier-percentage c:- AlphaMorphValue ) 810 | (up-modify-sn sn-home-exploration-time c:+ AlphaMorphValue ) 811 | (up-modify-sn sn-maximum-food-drop-distance c:max AlphaMorphValue ) 812 | (up-modify-sn sn-maximum-town-size c:+ AlphaMorphValue ) 813 | (up-modify-sn sn-maximum-wood-drop-distance c:+ AlphaMorphValue ) 814 | (up-modify-sn sn-maximum-wood-drop-distance c:max AlphaMorphValue ) 815 | (up-modify-sn sn-number-boat-explore-groups c:- AlphaMorphValue ) 816 | (up-modify-sn sn-number-boat-explore-groups c:+ AlphaMorphValue ) 817 | (up-modify-sn sn-number-boat-explore-groups c:max AlphaMorphValue ) 818 | (up-modify-sn sn-number-civilian-militia c:max AlphaMorphValue ) 819 | (up-modify-sn sn-stone-modifier-percentage c:+ AlphaMorphValue ) 820 | (up-modify-sn sn-wood-modifier-percentage c:- AlphaMorphValue ) 821 | (up-modify-sn sn-wood-modifier-percentage c:+ AlphaMorphValue ) 822 | (up-modify-sn teambalance c:= AlphaMorphValue ) 823 | (up-request-hunters c: AlphaMorphValue ) 824 | (up-set-placement-data my-player-number -1 c: AlphaMorphValue ) 825 | -------------------------------------------------------------------------------- /archive/alpha.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/alpha.PNG -------------------------------------------------------------------------------- /archive/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/bar.png -------------------------------------------------------------------------------- /archive/launch_okay.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/launch_okay.PNG -------------------------------------------------------------------------------- /archive/leave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/leave.png -------------------------------------------------------------------------------- /archive/main_menu.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/main_menu.PNG -------------------------------------------------------------------------------- /archive/ok.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/ok.PNG -------------------------------------------------------------------------------- /archive/open_menu.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/open_menu.PNG -------------------------------------------------------------------------------- /archive/play_again.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/play_again.PNG -------------------------------------------------------------------------------- /archive/player_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/player_1.PNG -------------------------------------------------------------------------------- /archive/quit.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/quit.PNG -------------------------------------------------------------------------------- /archive/readme.txt: -------------------------------------------------------------------------------- 1 | This is where the old algorithm lives. We do not speak of him. 2 | -------------------------------------------------------------------------------- /archive/single_player.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/single_player.PNG -------------------------------------------------------------------------------- /archive/skirmish.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/skirmish.PNG -------------------------------------------------------------------------------- /archive/start_game.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/start_game.PNG -------------------------------------------------------------------------------- /archive/tech.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/tech.PNG -------------------------------------------------------------------------------- /archive/try.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/try.png -------------------------------------------------------------------------------- /archive/won.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/won.png -------------------------------------------------------------------------------- /archive/yes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mboop127/AlphaScripter/0b39ba61c6afed53eb61b9f95ff52a3ca5e891c2/archive/yes.PNG -------------------------------------------------------------------------------- /edittable.csv: -------------------------------------------------------------------------------- 1 | |Research,,,,,,,,,, 2 | TechID,age required,required,requirement count,gametime,,,,,, 3 | 246,current-age < 1,none,3,0,,,,,, 4 | 100,,castle,0,0,,,,,, 5 | 100,,castle,0,0,,,,,, 6 | 265,current-age > 1,outpost,0,6605,,,,,, 7 | 102,current-age != 3,archery-range,0,1276,,,,,, 8 | 102,current-age != 3,archery-range,0,1276,,,,,, 9 | 75,current-age > 1,dock,-1,3881,,,,,, 10 | 75,current-age > 1,dock,-1,3881,,,,,, 11 | ,,,,,,,,,, 12 | |Building,,,,,,,,,, 13 | BuildingID,age required,max count,required,requirement count,gametime,,,,, 14 | palisade-wall,current-age > 1,103,none,8,3702,,,,, 15 | blacksmith,current-age <= 1,147,archery-range,0,6899,,,,, 16 | house,current-age > 1,24,none,3,65,,,,, 17 | farm,current-age < 1,10,gate,0,-40,,,,, 18 | mill,,1,none,-1,-89,,,,, 19 | farm,current-age == 2,40,stable,0,-26,,,,, 20 | farm,current-age == 2,40,stable,0,-26,,,,, 21 | blacksmith,current-age <= 3,1,farm,5,19,,,,, 22 | stable,,-1,gate,0,0,,,,, 23 | monastery,,1,none,1,0,,,,, 24 | ,,,,,,,,,, 25 | |Build forward,,,,,,,,,, 26 | BuildingID,age required,max count,required,requirement count,gametime,,,,, 27 | castle,current-age >= 0,5,none,-1,0,,,,, 28 | town-center,current-age >= 1,20,gate,-1,0,,,,, 29 | ,,,,,,,,,, 30 | |Unit,,,,,,,,,, 31 | UnitID,age required,max count,required,requirement count,gametime,,,,, 32 | knight-line,current-age > 1,94,palisade-wall,-1,-21,,,,, 33 | petard,current-age > 1,50,none,1,2000,,,,, 34 | 83,,33,none,4,-156,,,,, 35 | 83,current-age > 1,107,none,0,0,,,,, 36 | 83,current-age > 1,107,none,0,0,,,,, 37 | scout-cavalry-line,current-age > 1,100,none,0,2500,,,,, 38 | monk,,1,none,0,0,,,,, 39 | ,,,,,,,,,, 40 | ,,,,,,,,,, 41 | |Strategic Number,,,,,,,,,, 42 | SnID,age required,value,required,requirement count,gametime,,,,, 43 | 252,current-age < 0,61,none,-1,-79,,,,, 44 | 259,current-age > 1,60,farm,-1,65,,,,, 45 | 234,current-age > 1,38,none,-1,-21,,,,, 46 | 234,current-age > 1,38,none,-1,-21,,,,, 47 | 237,current-age == 0,40,none,0,-70,,,,, 48 | 234,current-age > 1,40,none,0,45,,,,, 49 | 32,current-age < 1,20,market,8,0,,,,, 50 | 247,,1,none,1,0,,,,, 51 | 229,,1,none,0,0,,,,, 52 | 4,current-age == 2,100,stable,1,0,,,,, 53 | 4,current-age == 2,100,stable,1,0,,,,, 54 | ,,,,,,,,,, 55 | |Distributions,,,,,,,,,, 56 | food,wood,gold,stone,attack threshold,,,,,, 57 | 0.257765868,0.171163456,0,0,0.188310373,,,,,, 58 | 0.25275149,0.297518006,0.199677928,0,0.174997736,,,,,, 59 | 0.25,0.1092283,0.079021779,0.082951927,0.119756894,,,,,, 60 | 0.156781853,0.094644496,0.238434988,0.278034057,0.1,,,,,, 61 | ,,,,,,,,,, 62 | |Attack Rules,,,,,,,,,, 63 | Age required,Enemy Age Required,population1 type,population1 inq,population1 value,population2 type,population2 inq,population2 value,gametime inq,gametime value,attack % 64 | current-age >= 0,,,,65,defend-soldier-count,>,0,>,500,100 65 | -------------------------------------------------------------------------------- /elo.py: -------------------------------------------------------------------------------- 1 | from main import * 2 | 3 | get_single_ai_data(['huns']*2,'best',list(eloDict.keys()),eloDict,3) 4 | 5 | #benchmarker("best","Shadow 3.1",300,['huns']*2) 6 | -------------------------------------------------------------------------------- /game_launcher.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | import enum 4 | import os 5 | import subprocess 6 | import time 7 | from ctypes import windll 8 | from dataclasses import dataclass 9 | 10 | import msgpackrpc 11 | 12 | all_civilisations = { 13 | "britons": 1, 14 | "franks": 2, 15 | "goths": 3, 16 | "teutons": 4, 17 | "japanese": 5, 18 | "chinese": 6, 19 | "byzantine": 7, 20 | "persians": 8, 21 | "saracens": 9, 22 | "turks": 10, 23 | "vikings": 11, 24 | "mongols": 12, 25 | "celts": 13, 26 | "spanish": 14, 27 | "aztec": 15, 28 | "mayan": 16, 29 | "huns": 17, 30 | "koreans": 18, 31 | "random": 19 32 | } 33 | 34 | maps = { 35 | "arabia": 21, #was 9, replaced with 21, migration is now dry arabia 36 | "archipelago": 10, 37 | "baltic": 11, 38 | "black_forest": 12, 39 | "coastal": 13, 40 | "continental": 14, 41 | "crater_cake": 15, 42 | "fortress": 16, 43 | "gold_rush": 17, 44 | "highland": 18, 45 | "islands": 19, 46 | "mediterranean": 20, 47 | "migration": 21, 48 | "rivers": 22, 49 | "team_islands": 23, 50 | "random_map": 24, 51 | "random": 24, 52 | "scandinavia": 25, 53 | "mongolia": 26, 54 | "yucatan": 27, 55 | "salt_marsh": 28, 56 | "arena": 29, 57 | "oasis": 31, 58 | "ghost_lake": 32, 59 | "nomad": 33, 60 | "iberia": 34, 61 | "britain": 35, 62 | "mideast": 36, 63 | "texas": 37, 64 | "italy": 38, 65 | "central_america": 39, 66 | "france": 40, 67 | "norse_lands": 41, 68 | "sea_of_japan": 42, 69 | "byzantium": 43, 70 | "random_land_map": 45, 71 | "random_real_world_map": 47, 72 | "blind_random": 48, 73 | "conventional_random_map": 49 74 | } 75 | 76 | map_sizes = {'tiny': 0, 'small': 1, 'medium': 2, 'normal': 3, 'large': 4, 'giant': 5} 77 | difficulties = {'hardest': 0, 'hard': 1, 'moderate': 2, 'standard': 3, 'easiest': 4} 78 | game_types = {'random_map': 0, 'regicide': 1, 'death_match': 2, 'scenario': 3, 'king_of_the_hill': 4, 'wonder_race': 6, 79 | 'turbo_random_map': 8} 80 | starting_resources = {'standard': 0, 'low': 1, 'medium': 2, 'high': 3} 81 | reveal_map_types = {'normal': 1, 'explored': 2, 'all_visible': 3} 82 | starting_ages = {'standard': 0, 'dark': 2, 'feudal': 3, 'castle': 4, 'imperial': 5, 'post-imperial': 6} 83 | victory_types = {'standard': 0, 'conquest': 1, 'relics': 4, 'time_limit': 7, 'score': 8} 84 | 85 | 86 | def get_key_by_value(d: dict, v): 87 | for key, value in d.items(): 88 | if v == value: 89 | return key 90 | return None 91 | 92 | 93 | class GameStatus(enum.Enum): 94 | NONE = "No status yet." # If we read this there is probably something wrong. 95 | INIT = "Initialized" # This means no process has been launched, no RPC client has been launched or anything. 96 | LAUNCHED = "Game process launched." # The process exists, but not RPC client has been launched and connected. 97 | CONNECTED = "RPC Client connected." # The process and RPC client exist, but the game is still in the main menu. 98 | SETUP = "Game settings have been applied." # The game has applied settings. 99 | RUNNING = "Game is running" # The game is just running. 100 | ENDED = "Game is no longer running." # The game has ended and we are in the main menu. 101 | QUIT = "Game has been quit and process killed." 102 | EXCEPTED = "This game has encountered an error and is therefore terminated." 103 | 104 | 105 | class GameSettings: 106 | def __init__(self, names: list, civilisations: list = None, map_id='arabia', map_size='tiny', difficulty='hard', 107 | game_type='random_map', resources='low', reveal_map='normal', starting_age='dark', 108 | victory_type='conquest', game_time_limit=0, speed = True): 109 | 110 | self.names = names 111 | self.civilisations = self.__correct_civilizations(civilisations, default='huns') 112 | self.map_id = self.__correct_setting(map_id, maps, 'arabia', 'map name/type') 113 | self.map_size = self.__correct_setting(map_size, map_sizes, 'medium', 'map size') 114 | self.difficulty = self.__correct_setting(difficulty, difficulties, 'hard', 'difficulty') 115 | self.game_type = self.__correct_setting(game_type, game_types, 'random_map', 'game type') 116 | self.resources = self.__correct_setting(resources, starting_resources, 'standard', 'starting resources') 117 | self.reveal_map = self.__correct_setting(reveal_map, reveal_map_types, 'normal', 'reveal map') 118 | self.starting_age = self.__correct_setting(starting_age, starting_ages, 'dark', 'starting age') 119 | self.victory_type = self.__correct_setting(victory_type, victory_types, 'conquest', 'victory type (WIP)') 120 | self.victory_value = 0 # TODO: Make this work. 121 | self.game_time_limit = max(0, game_time_limit) 122 | self.speed = speed 123 | 124 | @property 125 | def map(self): 126 | return self.map_id 127 | 128 | @property 129 | def civs(self): 130 | return self.civilisations 131 | 132 | @staticmethod 133 | def __correct_setting(value, possible_values: dict, default, setting_name): 134 | if value in possible_values.values(): 135 | return value 136 | elif value.lower() in possible_values.keys(): 137 | return possible_values[value.lower()] 138 | print(f"Warning! Value {value} not valid for setting {setting_name}. Defaulting to {default}.") 139 | return possible_values[default] 140 | 141 | def __correct_civilizations(self, civilizations: list, default='huns'): 142 | if civilizations is None: 143 | civilizations = [] 144 | result = [] 145 | 146 | if len(civilizations) < len(self.names): 147 | print(f"The number of civilisations provided is less than the number of names. For every player that " 148 | f"does not have a civilisation provided for it, it will default to {default}.") 149 | 150 | for index, name in enumerate(self.names): 151 | if index < len(civilizations): # Just copy the civilisations list for as far as we can 152 | civ = civilizations[index] 153 | if civ in all_civilisations.values(): 154 | result.append(civ) 155 | elif civ in all_civilisations.keys(): 156 | result.append(all_civilisations[civ]) 157 | else: 158 | print(f"Civ {civ} is not valid. Defaulting to {default}.") 159 | result.append(all_civilisations[default]) 160 | else: # If we have a list of civs that is to short, fill up the rest with the default. 161 | result.append(all_civilisations[default]) 162 | return result 163 | 164 | def clone(self): 165 | return GameSettings(self.names, self.civilisations, self.map_id, self.map_size, self.difficulty, self.game_type, 166 | self.resources, self.reveal_map, self.starting_age, self.victory_type, self.game_time_limit, self.speed) 167 | 168 | 169 | class PlayerStats: 170 | index: int 171 | name: str 172 | alive: bool 173 | score: int 174 | 175 | def __init__(self, index: int, name: str): 176 | self.name = name 177 | self.index = index 178 | self.alive = True 179 | self.score = 0 180 | 181 | def update(self, score, alive): 182 | self.score = score 183 | self.alive = alive 184 | 185 | 186 | class GameStats: 187 | elapsed_game_time: int 188 | player_stats: dict 189 | _settings: GameSettings 190 | 191 | def __init__(self, settings: GameSettings): 192 | self.elapsed_game_time = 0 193 | self._settings = settings 194 | self.player_stats = dict() 195 | for index, name in enumerate(settings.names): 196 | self.player_stats[index] = PlayerStats(index=index, name=name) 197 | 198 | def update_player(self, index: int, score: int, alive: bool): 199 | self.player_stats[index].update(score=score, alive=alive) 200 | 201 | @property 202 | def scores(self): 203 | return [self.player_stats[i].score for i in range(len(self._settings.names))] 204 | 205 | @property 206 | def alives(self): 207 | return [self.player_stats[i].alive for i in range(len(self._settings.names))] 208 | 209 | def __str__(self): 210 | string = f"Played @ {get_key_by_value(maps, self._settings.map_id)}" \ 211 | f"[{get_key_by_value(map_sizes, self._settings.map_size)}] \n" \ 212 | f"Elapsed Game Time: {self.elapsed_game_time} \n\n" 213 | for i in range(len(self.player_stats)): 214 | ps: PlayerStats = self.player_stats[i] 215 | string += f"Player {i} '{ps.name}' ({get_key_by_value(all_civilisations,self._settings.civs[i])}) \n" \ 216 | f"\t\t Score: {ps.score} \n" \ 217 | f"\t\t Alive: {ps.alive} \n" 218 | return string 219 | 220 | 221 | class Game: 222 | name: str = "GameWithoutName" 223 | _settings: GameSettings = None 224 | status: GameStatus = GameStatus.NONE 225 | 226 | _process: subprocess.Popen = None 227 | _rpc: msgpackrpc.Client = None 228 | _port: int = 0 229 | stats: GameStats = None 230 | 231 | def __init__(self, name: str, debug: bool = False): 232 | self.name = name 233 | self.status = GameStatus.INIT 234 | self.debug = debug 235 | 236 | async def launch_process(self, executable_path: str, dll_path: str, multiple: bool, port: int) -> subprocess.Popen: 237 | """ 238 | Launch an instance of the game (i.e. open a new process) 239 | 240 | :param executable_path: The path to the executable of the game. 241 | :param dll_path: The path to the DLL needed to communicate with the process. 242 | :param multiple: Whether multiple processes are going to be launched. Used as a required launch parameter. 243 | :param port: The port on which to start this process communication channels (using the DLL) 244 | :return: The process that was started of type ``subprocess.Popen`` 245 | """ 246 | 247 | if self.status != GameStatus.INIT: 248 | print(f"Warning! This game does not have the status {GameStatus.INIT} so it's probably not the right time" 249 | f" to call this launch_process method!") 250 | 251 | launch_options = f"{executable_path} {'-multipleinstances ' if multiple else ''}-autogameport {port}" 252 | aoc_proc = subprocess.Popen(launch_options) 253 | 254 | # write dll path into aoc memory 255 | aoc_handle = windll.kernel32.OpenProcess(0x1FFFFF, False, aoc_proc.pid) # PROCESS_ALL_ACCESS 256 | remote_memory = windll.kernel32.VirtualAllocEx(aoc_handle, 0, 260, 0x3000, 0x40) 257 | windll.kernel32.WriteProcessMemory(aoc_handle, remote_memory, dll_path, len(dll_path), 0) 258 | 259 | # load the dll from the remote process 260 | # noinspection PyProtectedMember 261 | load_library = windll.kernel32.GetProcAddress(windll.kernel32._handle, b'LoadLibraryA') 262 | remote_thread = windll.kernel32.CreateRemoteThread(aoc_handle, 0, 0, load_library, remote_memory, 0, 0) 263 | windll.kernel32.WaitForSingleObject(remote_thread, 0xFFFFFFFF) 264 | windll.kernel32.CloseHandle(remote_thread) 265 | 266 | # clean up 267 | windll.kernel32.VirtualFreeEx(aoc_handle, remote_memory, 0, 0x00008000) 268 | windll.kernel32.CloseHandle(aoc_handle) 269 | 270 | self._port = port 271 | self._process = aoc_proc 272 | self.status = GameStatus.LAUNCHED 273 | return aoc_proc 274 | 275 | def setup_rpc_client(self, custom_port: int = 0) -> msgpackrpc.Client: 276 | """ 277 | Create a RPC client to manage this game remotely. 278 | 279 | :param custom_port: A custom port to connect to. If not specified (or set to 0 or lower), this will use the 280 | port assigned automatically when creating the game process. 281 | 282 | :return: A ``msgpackrpc.Client`` instance that is connected to the game process. 283 | """ 284 | 285 | if self.debug and self.status != GameStatus.LAUNCHED: 286 | print(f"Warning! Game {self.name} does have the status {GameStatus.LAUNCHED}. Setting up the RPC client" 287 | f" is probably not a good idea!") 288 | 289 | setup_port = custom_port if custom_port != 0 else self._port 290 | self._rpc = msgpackrpc.Client(msgpackrpc.Address("127.0.0.1", setup_port)) 291 | self.status = GameStatus.CONNECTED 292 | return self._rpc 293 | 294 | async def apply_settings(self, settings: GameSettings): 295 | """ 296 | Apply game settings to this game. 297 | 298 | :param settings: The GameSettings settings to apply. 299 | """ 300 | 301 | if self.debug and self.status != GameStatus.CONNECTED: 302 | print(f"Warning! Status of game {self.name} is not {GameStatus.CONNECTED}. It might not be a good time" 303 | f" to setup the game...") 304 | 305 | self._settings = settings 306 | try: 307 | self._rpc.call_async('ResetGameSettings') 308 | self._rpc.call_async('SetGameMapType', settings.map) 309 | self._rpc.call_async('SetGameDifficulty', settings.difficulty) # Set to hard 310 | self._rpc.call_async('SetGameRevealMap', settings.reveal_map) # Set to standard exploration 311 | self._rpc.call_async('SetGameMapSize', settings.map_size) # Set to medium map size 312 | self._rpc.call_async('SetGameVictoryType', settings.victory_type, settings.victory_value) 313 | self._rpc.call_async('SetRunUnfocused', True) 314 | self._rpc.call_async('SetRunFullSpeed', settings.speed) 315 | # self.call_safe('SetUseInGameResolution', False, game_index=game_index) 316 | for index, name in enumerate(settings.names): 317 | self._rpc.call_async('SetPlayerComputer', index + 1, name) 318 | self._rpc.call_async('SetPlayerCivilization', index + 1, settings.civilisations[index]) 319 | self._rpc.call_async('SetPlayerTeam', index + 1, 0) 320 | except BaseException as e: 321 | message = f"Warning! Game Settings could not be applied to game {self.name} because of exception {e}" \ 322 | f" The rpc client will be closed and the game process will be terminated." 323 | self.handle_except(e, message) 324 | self.stats = GameStats(settings=settings) 325 | self.status = GameStatus.SETUP 326 | 327 | async def start_game(self): 328 | """ 329 | Start the game. 330 | """ 331 | 332 | if self.debug and self.status != GameStatus.SETUP: 333 | print(f"Warning! Game {self.name} has not the status {GameStatus.SETUP}. It might not be a good idea to" 334 | f" try and start this game...") 335 | try: 336 | self._rpc.call('StartGame') # self._rpc.call_async('StartGame') did not work. 337 | if self.debug: 338 | print(f"Game {self.name} launched.") 339 | except BaseException as e: 340 | message = f"Could not start game {self.name} because it has excepted with exception {e}. " \ 341 | f"The game will be ended and the process killed." 342 | self.handle_except(e, message) 343 | self.status = GameStatus.RUNNING 344 | 345 | async def update(self) -> bool: 346 | """ 347 | Check whether the game is still running and extract the stats if it isn't. 348 | """ 349 | try: 350 | is_running = self._rpc.call('GetGameInProgress') 351 | game_time = 0 352 | self.stats.winner = 0 353 | try: 354 | game_time = self._rpc.call('GetGameTime') 355 | self.stats.elapsed_game_time = game_time 356 | except BaseException as e: 357 | message = f"Couldn't get game time for game {self.name} because of {e}. " \ 358 | f"Closing the RPC client and killing process." 359 | self.handle_except(e, message) 360 | 361 | over_time = 0 < self._settings.game_time_limit < game_time 362 | 363 | if not is_running or over_time: 364 | temp = self._rpc.call('GetWinningPlayers') 365 | if len(temp) > 1: 366 | self.stats.winner = 0 367 | else: 368 | self.stats.winner = temp[0] 369 | #print(self._rpc.call('GetWinningPlayer')) 370 | for index, name in enumerate(self._settings.names): 371 | try: 372 | if game_time >= 1.5 * self._settings.game_time_limit: 373 | score = 0 374 | else: 375 | score = self._rpc.call("GetPlayerScore", index + 1) 376 | alive = self._rpc.call("GetPlayerAlive", index + 1) 377 | #print(self.stats.winner) 378 | 379 | if score is not None and alive is not None: 380 | self.stats.update_player(index=index, score=score, alive=alive) 381 | else: 382 | self.stats.update_player(index=index, score=0, alive=False) 383 | if self.debug: 384 | print(f"Couldn't get score or alive status for player {name}. Setting this score to 0") 385 | except BaseException as e: 386 | message = f"Score and/or alive status for player {name} in game {self.name} couldn't be " \ 387 | f"retrieved because of {e}. Setting this players' score to 0." 388 | self.stats.update_player(index=index, score=0, alive=False) 389 | self.handle_except(e, message) 390 | self.status = GameStatus.ENDED 391 | self.kill() 392 | 393 | except BaseException as e: 394 | message = f"Warning! Game {self.name} could not be updated because of exception {e}." 395 | self.handle_except(e, message) 396 | 397 | def handle_except(self, exception, extra_message: str = None): 398 | """ 399 | Handle exceptions that occur during a running game by killing the process and disconnecting the RPC client. 400 | Also, print debug statements if relevant. 401 | 402 | :param exception: The exception that occurred. 403 | :param extra_message: An optional extra message to print as well. 404 | """ 405 | 406 | if self.debug: 407 | if extra_message: 408 | print(extra_message) 409 | print(f"Exception {exception} occurred on {self.name}. Killing the process and closing the rpc client.") 410 | self.kill() # Important to do before setting the Excepted state! 411 | self.status = GameStatus.EXCEPTED 412 | 413 | def kill(self): 414 | """ 415 | Kill the process and disconnect the RPC client. 416 | """ 417 | 418 | if self._rpc is not None: 419 | self._rpc.close() 420 | self._rpc = None 421 | if self._process is not None: 422 | self._process.kill() 423 | self._process = None 424 | self.status = GameStatus.QUIT 425 | 426 | def print_stats(self): 427 | string = f"Game {self.name} Stats \n ------------------------------------------- \n" \ 428 | f"Status: {self.status} \n" 429 | string += str(self.stats) 430 | print(string) 431 | print("\n\n") 432 | 433 | @property 434 | def statistics(self): 435 | return self.stats 436 | 437 | @property 438 | def scores(self): 439 | return self.stats.scores 440 | 441 | @property 442 | def overtime(self): 443 | return 0 < self._settings.game_time_limit < self.stats.elapsed_game_time 444 | 445 | @property 446 | def winner(self): 447 | return self.stats.winner 448 | 449 | def __str__(self): 450 | return self.name 451 | 452 | def __repr__(self): 453 | return self.name 454 | 455 | 456 | class Launcher: 457 | def __init__(self, 458 | settings: GameSettings, 459 | executable_path: str = "C:\\Program Files\\Microsoft Games\\age of empires ii\\Age2_x1\\age2_x1.exe", 460 | debug: bool = False): 461 | self.executable_path = executable_path 462 | self.directory, self.aoc_name = os.path.split(executable_path) 463 | self.dll_path = (os.path.join(self.directory, 'aoc-auto-game.dll')).encode('UTF-8') 464 | self.games: list[Game] = None 465 | self.base_port = 64720 466 | self.settings = settings 467 | self.debug = debug 468 | 469 | @property 470 | def names(self): 471 | return self.settings.names 472 | 473 | @property 474 | def number_of_games(self): 475 | if not self.games: 476 | return 0 477 | return len(self.games) 478 | 479 | @property 480 | def running_games(self): 481 | return [game for game in self.games if game.status == GameStatus.RUNNING] 482 | 483 | def launch_games(self, instances: int = 1, round_robin: bool = False): 484 | all_settings = [self.settings] * instances if not round_robin else self._apply_round_robin(self.settings) 485 | if not round_robin: 486 | self.games = [Game(f"Game#{i + 1}", self.debug) for i in range(instances)] 487 | else: 488 | self.games = [Game(f"Game#{i + 1}", self.debug) for i in range(len(all_settings))] 489 | 490 | asyncio.run(self._launch_games(), debug=self.debug) 491 | time.sleep(5.0) # Make sure all games are launched. 492 | self._setup_rpc_clients() 493 | asyncio.run(self._apply_games_settings(settings=all_settings), debug=self.debug) # Apply settings to the games 494 | time.sleep(2) 495 | asyncio.run(self._start_games()) 496 | 497 | any_game_running = True 498 | while any_game_running: 499 | asyncio.run(self.update_games()) 500 | if self.debug: 501 | print(f"({datetime.datetime.now()}) : {self.running_games}") 502 | time.sleep(1) 503 | any_game_running = len(self.running_games) > 0 504 | 505 | return self.games 506 | 507 | async def _launch_games(self) -> list[subprocess.Popen]: 508 | tasks = [] 509 | multiple = self.number_of_games > 1 510 | for index, game in enumerate(self.games): 511 | t = asyncio.create_task( 512 | coro=game.launch_process( 513 | executable_path=self.executable_path, 514 | dll_path=self.dll_path, 515 | multiple=multiple, 516 | port=self.base_port + index 517 | ), 518 | name=f"GameLaunch{index}") 519 | tasks.append(t) 520 | return await asyncio.gather(*tasks) 521 | 522 | def _setup_rpc_clients(self): 523 | for game in self.games: 524 | game.setup_rpc_client() 525 | 526 | async def _apply_games_settings(self, settings: list[GameSettings]): 527 | tasks = [] 528 | for index, game in enumerate(self.games): 529 | t = asyncio.create_task(coro=game.apply_settings(settings[index]), name=f"ApplyGameSettings-{game.name}") 530 | tasks.append(t) 531 | return await asyncio.gather(*tasks) 532 | 533 | @staticmethod 534 | def _apply_round_robin(original_settings: GameSettings) -> list[GameSettings]: 535 | settings = [] 536 | # Suppose the number of names = 5, then we want to go from 0 to (incl.) 3 537 | # And for index2, we want to go from 1 to (incl.) 4 538 | for index1 in range(len(original_settings.names) - 1): 539 | for index2 in range(index1 + 1, len(original_settings.names)): 540 | gs = original_settings.clone() 541 | gs.names = [original_settings.names[index1], original_settings.names[index2]] 542 | gs.civilisations = [original_settings.civilisations[index1], original_settings.civilisations[index2]] 543 | settings.append(gs) 544 | return settings 545 | 546 | async def _start_games(self): 547 | tasks = [] 548 | for index, game in enumerate(self.games): 549 | t = asyncio.create_task(coro=game.start_game(), name=f"StartingGame-{game.name}") 550 | tasks.append(t) 551 | return await asyncio.gather(*tasks) 552 | 553 | async def update_games(self): 554 | tasks = [] 555 | for game in self.running_games: 556 | t = asyncio.create_task(coro=game.update(), name=f"UpdateGame-{game.name}") 557 | tasks.append(t) 558 | await asyncio.gather(*tasks) 559 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from main import * 3 | 4 | def read_and_write(ai_name): 5 | 6 | a = read_ai(ai_name) 7 | write_ai(a,"best") 8 | 9 | root = tk.Tk() 10 | 11 | inputtxt = tk.Text(root, height = 1, width = 15) 12 | inputtxt.pack() 13 | 14 | inputtxt2 = tk.Text(root, height = 1, width = 15) 15 | inputtxt2.pack() 16 | 17 | button1 = tk.Button(root, text = "Self train infinite", command = lambda: run_vs_self(0,True,int(inputtxt2.get(1.0, "end-1c")),True)) 18 | button1.pack() 19 | 20 | button1 = tk.Button(root, text = "Self train one round", command = lambda: run_vs_self(0,True,int(inputtxt2.get(1.0, "end-1c")),False)) 21 | button1.pack() 22 | 23 | button1 = tk.Button(root, text = "Other train infinite", command = lambda: run_vs_other(0,True,inputtxt.get(1.0, "end-1c"),[civ,civ],int(inputtxt2.get(1.0, "end-1c")),True)) 24 | button1.pack() 25 | 26 | button1 = tk.Button(root, text = "Other train one round", command = lambda: run_vs_other(0,True,inputtxt.get(1.0, "end-1c"),[civ,'huns'],int(inputtxt2.get(1.0, "end-1c")),False)) 27 | button1.pack() 28 | 29 | button1 = tk.Button(root, text = "FFA new start", command = lambda: run_ffa(5000,False)) 30 | button1.pack() 31 | 32 | button1 = tk.Button(root, text = "FFA load", command = lambda: run_ffa(0,True)) 33 | button1.pack() 34 | 35 | button1 = tk.Button(root, text = "Save AI", command = backup) 36 | button1.pack() 37 | 38 | button1 = tk.Button(root, text = "Benchmark", command = lambda: benchmarker("best",inputtxt.get(1.0, "end-1c"),100,[civ,'huns'])) 39 | button1.pack() 40 | 41 | button1 = tk.Button(root, text = "Ladder", command = lambda: group_train(ai_ladder,True,int(inputtxt2.get(1.0, "end-1c")))) 42 | button1.pack() 43 | 44 | inputtxt3 = tk.Text(root, height = 1, width = 15) 45 | inputtxt3.pack() 46 | 47 | button1 = tk.Button(root, text = "Write AI", command = lambda: read_and_write(inputtxt3.get(1.0, "end-1c"))) 48 | button1.pack() 49 | 50 | button1 = tk.Button(root, text = "Speed score", command = lambda: speed_train(inputtxt.get(1.0, "end-1c"))) 51 | button1.pack() 52 | 53 | button1 = tk.Button(root, text = "ELO Train", command = lambda: elo_train()) 54 | button1.pack() 55 | 56 | button1 = tk.Button(root, text = "Other train slow", command = lambda: run_vs_other_slow(0,True,inputtxt.get(1.0, "end-1c"),['huns','huns'],40,True)) 57 | button1.pack() 58 | 59 | button1 = tk.Button(root, text = "Self train slow", command = lambda: run_vs_self_slow2(0,True,int(inputtxt2.get(1.0, "end-1c")),True)) 60 | button1.pack() 61 | 62 | button1 = tk.Button(root, text = "Run Robin", command = lambda: run_robin(int(inputtxt2.get(1.0, "end-1c")))) 63 | button1.pack() 64 | 65 | button1 = tk.Button(root, text = "Run vs selfs", command = lambda: run_vs_selfs(0,True,int(inputtxt2.get(1.0, "end-1c")),True)) 66 | button1.pack() 67 | 68 | root.mainloop() 69 | -------------------------------------------------------------------------------- /params.csv: -------------------------------------------------------------------------------- 1 | ActionId,-1;600;601;602;603;604;605;606;607;608;609;610;611;612;613;614;615;616;617;618;619;620;621;631 2 | Age,0;1;2;3 3 | AllyPlayer,1;2;3;4;5;6;7;8;my-player-number;target-player;focus-player;-101;-103;-108;-201 4 | AnyPlayer,0;1;2;3;4;5;6;7;8;my-player-number;target-player;focus-player;-101;-102;-103;-104;-105;-106;-107;-108;-109;-110;-111;-201;-202;-203;-204;-205 5 | BuildingId,10;12;14;18;19;20;30;31;32;45;47;49;51;68;70;71;82;84;86;87;101;103;104;109;116;129;130;131;132;133;137;141;142;153;209;210;276;463;464;465;481;482;483;498;562;563;564;565;584;585;586;587;598;611;612;613;614;615;616;618;619;620;621;624;625;626;712;713;714;715;716;717;718;719;734;1189;1251 6 | Civ,0|18 7 | ClassId,-1;900;901;902;903;904;905;906;907;908;909;910;911;912;913;914;915;918;919;920;921;922;923;927;930;932;933;935;936;939;942;943;944;947;949;951;952;954;955;958;959 8 | CmdId,-1|10 9 | ColorId,1|8 10 | Commodity,0;1;2 11 | compareOp,>;>=;<;<=;==;!=;c:>;c:>=;c:<;c:<=;c:==;c:!=;g:>;g:>=;g:<;g:<=;g:==;g:!=;s:>;s:>=;s:<;s:<=;s:==;s:!= 12 | ComputerAllyPlayer,1;2;3;4;5;6;7;8;my-player-number;target-player;focus-player;-103 13 | CustomResource,0;1;2;3;907;908;909;910;915;932;933;958 14 | difficulty,0;1;2;3;4 15 | DiffParameterId,0;1 16 | ElapsedTime,1|120000 17 | EscrowState,0|512 18 | ESPlayerStance,0;1;3 19 | EventID,0|255 20 | EventType,0 21 | ExploredState,0;15;128 22 | FactId,0|54 23 | FindPlayerMethod,0;1;2;3 24 | GameType,0;1;2;3;5;6;7;8 25 | GarrisonableUnitId,35;422;548;545;141;481;482;612;483;82;79;234;236;235 26 | GoalId,1|512 27 | GroupId,0|9 28 | GroupType,100|109 29 | Id,0|32000 30 | IdleType,0;1;2;3 31 | MapSize,0|5 32 | MapType,-1;9;10;11;12;13;14;15;16;17;18;19;20;21;22;23;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40;41;42;43;44 33 | mathOp,0|35 34 | MaxDistance,-1|32767 35 | MaxGarrison,-1|32767 36 | MinDistance,-1|32767 37 | MinGarrison,-1|32767 38 | ObjectData,-1|82 39 | ObjectList,0;1 40 | ObjectStatus,0;2;3;4;5 41 | OnMainland,-1;0;1 42 | OrderId,-1;700;701;702;703;704;705;706;707;708;709;710;711;712;713;714;715;716;717;718;719;720;721;731 43 | Perimeter,1;2 44 | PlacementType,0;1;2;3 45 | PlayerId,0|8 46 | PlayerStance,0;1;2;3 47 | Point,41|510 48 | PositionType,0|13 49 | ProjectileType,0|7 50 | ResearchState,0;1;2;3 51 | Resource,0;1;2;3 52 | ResourceAmount,0|224 53 | SearchOrder,0;1;2 54 | SearchSource,1;2 55 | SharedGoalId,1|256 56 | SignalId,0|255 57 | SnId,0;1;2;3;5;16;18;19;20;22;23;25;26;28;29;32;34;35;36;38;41;42;43;44;50;51;52;54;56;57;72;73;74;75;77;78;79;80;81;82;83;86;87;88;89;90;92;93;94;98;99;100;101;103;104;105;106;107;108;109;110;114;115;117;118;119;120;122;123;131;134;138;139;140;141;142;143;144;145;146;148;149;156;157;158;159;160;163;164;165;166;167;168;169;179;184;185;194;198;201;202;203;204;216;218;219;225;226;227;228;229;230;231;232;233;234;235;237;238;239;242;243;244;245;246;247;248;249;250;251;252;253;254;255;256;257;258;259;260;261;262;263;264;265;266;267;268;269;270;271;272;273;274;275;276;277;278;279;280;281;282;283;284;285;286;287;288;291;292;293;294;295 58 | StartingResources,1;2;3 59 | State,-1;0;1 60 | Strict,0;1 61 | TauntId,1|255 62 | TechId,2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;19;21;22;23;24;27;34;35;37;39;45;47;48;49;50;51;52;54;55;59;60;61;63;64;67;68;74;75;76;77;80;81;82;83;90;93;96;98;100;101;102;103;140;182;194;197;199;200;201;202;203;207;209;211;212;213;215;217;218;219;221;222;230;231;233;236;237;239;244;246;249;252;254;255;257;264;265;278;279;280;315;316;319;320;321;322;360;361;362;363;364;365;366;367;368;369;370;371;372;373;374;375;376;377;379;380;398;408;428;429;432;434;435;436;437;438;439;440;441;445;448;450;457 63 | Terrain,0|41 64 | TimerId,1|50 65 | TimerState,0;1;2 66 | typeOp,c:;g:;s: 67 | UnitId,-299;-298;-297;-296;-295;-294;-293;-292;-291;-290;-289;-288;-287;-286;-285;-284;-283;-282;-281;-280;-279;-278;-277;-276;-275;-274;-273;-272;-271;-270;-269;-268;-267;-266;-265;-264;-1;4;5;6;7;8;11;13;17;21;24;25;35;36;38;39;40;41;42;46;56;57;73;74;75;77;83;93;106;114;118;120;122;123;124;125;128;156;183;184;185;203;204;207;208;212;214;216;218;220;222;223;230;232;239;250;259;260;279;280;281;282;283;286;291;293;329;330;331;354;358;359;418;420;422;440;441;442;448;453;459;467;473;474;492;494;527;528;529;530;531;532;533;534;539;542;545;546;548;553;554;555;556;557;558;559;560;561;567;569;579;581;588;590;592;653;691;692;694;701;725;726;732;751;752;753;755;757;759;760;761;762;763;765;766;771;773;774;775;782;784;811;823;827;829;830;831;832;836;861;866;868;869;871;873;875;876;878;879;881;882;886;887;891;900;901;902;903;904;905;906;907;908;909;910;911;912;913;914;915;918;919;920;921;922;923;927;930;932;933;935;936;939;942;943;944;947;949;951;952;954;955;958;959 68 | UnitIdStrict,4;5;6;7;8;11;13;17;21;24;25;35;36;38;39;40;41;42;46;56;57;73;74;75;77;83;93;106;114;118;120;122;123;124;125;128;156;183;184;185;203;204;207;208;212;214;216;218;220;222;223;230;232;239;250;259;260;279;280;281;282;283;286;291;293;329;330;331;354;358;359;418;420;422;440;441;442;448;453;459;467;473;474;492;494;527;528;529;530;531;532;533;534;539;542;545;546;548;553;554;555;556;557;558;559;560;561;567;569;579;581;588;590;592;653;691;692;694;701;725;726;732;751;752;753;755;757;759;760;761;762;763;765;766;771;773;774;775;782;784;811;823;827;829;830;831;832;836;861;866;868;869;871;873;875;876;878;879;881;882;886;887;891 69 | value256,0|255 70 | value32,-32768|32767 71 | value32Positive,1|32767 72 | Victory,0;1;2;3;4 73 | WallId,72;117;155;-399 74 | RuleId,0|32767 75 | PriorityType,0;1 76 | ResetMode,0;1 77 | OnOff,0;1 78 | ObjectId,UnitId;BuildingId 79 | ScoutMethod,0;1;2;3;4;5;6 80 | AttackStance,0;1;2;3 81 | TargetAction,0|18 82 | Formation,-1;2;4;7;8 83 | -------------------------------------------------------------------------------- /resign.txt: -------------------------------------------------------------------------------- 1 | ;(defrule 2 | ;(game-time > 1500) 3 | ;(game-time < 1600) 4 | ;(players-building-type-count any-enemy town-center < 1) 5 | ;=> 6 | ;(chat-to-all "failed to scout, GG") 7 | ;(disable-self) 8 | ;(resign) 9 | ;) 10 | 11 | (defrule 12 | (game-time > 1200) 13 | (current-age == dark-age) 14 | (up-research-status c: feudal-age >= 2) 15 | (not(players-current-age any-enemy == dark-age)) 16 | => 17 | (chat-to-all "Stuck in dark age, GG") 18 | (resign) 19 | ) 20 | 21 | (defrule 22 | (game-time > 900) 23 | (population < 20) 24 | (players-population any-enemy > 40);50 25 | (not(players-current-age any-enemy == dark-age)) 26 | => 27 | (chat-to-all "Crippled early game GG") 28 | (resign) 29 | ) 30 | 31 | (defrule 32 | (game-time > 1200) 33 | (or(population < 40) 34 | (and(unit-type-count-total villager < 40) 35 | (military-population < 5))) 36 | (or(players-military-population any-enemy >= 50) 37 | (players-population any-enemy >= 100)) 38 | (or(players-civilian-population any-enemy >= 75) 39 | (players-population any-enemy >= 100)) 40 | => 41 | (chat-to-all "Their eco and military are too strong. GG") 42 | (resign) 43 | ) 44 | 45 | (defrule 46 | (game-time > 1200) 47 | (or(population < 60) 48 | (and(unit-type-count-total villager < 60) 49 | (military-population < 10))) 50 | (or(players-military-population any-enemy >= 80) 51 | (players-population any-enemy >= 150));160 52 | (or(players-civilian-population any-enemy >= 125) 53 | (players-population any-enemy >= 150));160 54 | => 55 | (chat-to-all "We may still have a lot, but they are too strong. GG") 56 | (resign) 57 | ) 58 | 59 | (defrule 60 | (building-type-count-total town-center < 1) 61 | (unit-type-count-total villager < 30) 62 | (players-population any-enemy >= 60) 63 | (military-population < 5) 64 | => 65 | (chat-to-all "Lost TCs, GG") 66 | (resign) 67 | ) 68 | 69 | (defrule 70 | (game-time > 600) 71 | (unit-type-count-total villager < 10) 72 | => 73 | (chat-to-all "We have too few villagers to fight back, GG") 74 | (resign) 75 | ) 76 | 77 | (defrule 78 | (game-time > 1500) 79 | (unit-type-count-total villager < 20) 80 | => 81 | (chat-to-all "Lost too many vills at this stage, GG") 82 | (resign) 83 | ) 84 | 85 | (defrule 86 | ;(cc-players-unit-type-count 0 66 == 0) ;Gold is gone 87 | ;(cc-players-unit-type-count 0 915 == 0) ;Wood is gone 88 | (military-population < 20) 89 | (current-age-time >= 900) 90 | (current-age >= castle-age) 91 | ;(not(can-build farm)) 92 | (gold-amount < 100) 93 | (players-current-age any-enemy == imperial-age) 94 | (players-military-population any-enemy >= 30) 95 | => 96 | (chat-to-all "Can't produce any military. Enemy is in Imp. GG") 97 | (resign) 98 | ) -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | 2 | fails_before_reset = 1000 3 | #game_time = 3000 4 | game_time = 7800 5 | 6 | default_mutation_chance = .01 7 | anneal_amount = 5 #bigger = less annealing 8 | max_fact_length = 4 9 | max_action_length = 3 10 | ai_length = 0 #normal rules 11 | simple_count = 100 12 | attack_rule_count = 0 13 | DUC_count = 10 14 | goal_rule_count = 25 15 | goal_action_count = 5 16 | 17 | #network_drive = "C:/Users/mboop/Desktop/Shared/" 18 | network_drive = "C:/Program Files/Microsoft Games/Age of Empires II/AI/" 19 | local_drive = "C:/Program Files/Microsoft Games/Age of Empires II/AI/" 20 | 21 | working_ais = ['Tlamacazqui','Wartron','Cyan','Eidolon','Odette_AI','Maximus 2_2a','RehoboamUP50%','Goths92602','Viper','Anti_TRiBaL','BTGBestStrats','Crusade 4.42c','Rhapsody0004','Ace','Ahulane UP','AllianceThundaEmpire','Alphav4','Arabian_Knight',"ARFFI-De'gel_ver_1_65",'ARFFI_05_107_50_rules','BambiV030','BambiV030dot2','best','Binary','BooM II','Boss_321','BruteForce3.1','Chameleon','CPS_Alexander','Cyan','Demon','Esty the TutoMaster','Faradol','fire2.3 DC-Final-01','GamesGod','Grasshopper007','Grasshopper008AOC','II2N Rattlehead','Illuminati','Immortal v0.9c (beta)','IMP_CAES_MIRO III Azt_Teut','IS_UPMachine','John_Mendl','Juggernaut','Junior','Kosmos3.00beta2','Leif Ericson 1.32','Maiar','Miggins','Mini Eel 1.7','Mininati','Mininaut 1.4','No Limits','Pantheon','Pharaon DE','Phyrexx UP','Promi','PUMA Dreadnought','PUMA Lade13','PUMA Promi','PUMA Snake BF','rAge__(fixed)','Ranger 3.04','Rattlehead','Reactionaryv19 UP','Shadow 4.11','Snake 8.5','Subjugator','Ternary','The General','The Horde','best','The Khanate','Thermopylai v1.3','The_Unknown3','Tlamacazqui','Torisan_bc','TRiBaL_Warrior','TRON','UlyssesWK','UnfairSteel','Valkyrie Warriors','VetrixDE Final','Vicky','VNS_Chris_Tournament','Yggdrasil'] 22 | 23 | eloDict = {'Hun Krush': 1745.128208, 'BambiV030': 1843.355396, 'UCS': 1669.937395, 'The Horde': 2008.261136, 'GamesGod': 1751.458229, 'Shadow 1': 1130.270815, 'Alphav4': 1490.374699, 'Subjugator': 1847.823758, 'VNS_Chris_Tournament': 1687.102006, 'Vicky': 1707.662764, 'Ace': 1672.926251, 'Alphav3': 1211.091988, 'Shadow 0': 1081.197184, 'Barbarian': 2063.981729, 'kt_trainer': 803.970408, 'Flying_Monk': 1500.779466, 'Crusade 4.42c': 2253.207843, 'Reactionaryv9': 1672.520563, 'MDE': 861.9773302, 'VNS_Halen_': 1834.394246, 'The_Unknown3': 2062.802085, 'The Khanate': 1777.137909, 'Shadow 2': 1302.087291, 'king': 914.8197984, 'Arabian_Knight': 1848.035116, 'Shadow 4.11': 2167.584068, 'Cal': 1301.195953, 'alpha_drush': 862.7897774, 'Vermin Supreme': 1654.355526, 'Ranger 3.04': 1929.164509, 'TRiBaL_Warrior': 2312.385075, 'Shadow 3.1': 1492.129188, 'CD': 1166.636131, 'AT_Scout_Rush': 1314.765075, 'UnfairSteel': 2293.514252, 'Rhapsody0004': 1429.094763, 'UlyssesWK': 2025.904929, 'Alphav2': 1072.367715} 24 | 25 | arenaDict = { 26 | "king": 985.3409644, 27 | "Shadow 0": 1117.177414, 28 | "Shadow 1": 1128.097398, 29 | "Shadow 2": 1198.126926, 30 | "CD": 1214.674512, 31 | "Shadow 3.1": 1335.581095, 32 | "Flying_Monk": 1373.37178, 33 | "Ace": 1383.680611, 34 | "Reactionaryv9": 1417.378483, 35 | "Subjugator": 1478.669299, 36 | "VNS_Chris_Tournament": 1522.210848, 37 | "Arabian_Knight": 1543.749224, 38 | "Vicky": 1583.402538, 39 | "Shadow 4.11": 1702.629269, 40 | "GamesGod": 1724.124926, 41 | "VNS_Halen_": 1782.395163, 42 | "BambiV030": 1819.853665, 43 | "Ranger 3.04": 1831.743734, 44 | "Crusade 4.42c": 1846.222101, 45 | "UlyssesWK": 1875.358588, 46 | "The_Unknown3": 1926.96583, 47 | "The Khanate": 1958.862525, 48 | "UnfairSteel": 2014.75918, 49 | "TRiBaL_Warrior": 2074.795807, 50 | "Barbarian": 2198.621863, 51 | "The Horde": 2206.336696 52 | } 53 | 54 | #ai_ladder = ["Shadow 3.1","NightMare3","farmertron"] 55 | ai_ladder = ["BambiV030","UlyssesWK","Barbarian","Promi"] 56 | #ai_ladder = ['The_Unknown3','Vicky','GamesGod','BambiV030','Ranger 3.04','VNS_Halen_','Shadow 4.11','Crusade 4.42c','UlyssesWK','The Khanate','UnfairSteel','TRiBaL_Warrior','Barbarian','The Horde'] 57 | #ai_ladder = ['Reactionaryv9', 'Arabian_Knight', 'BambiV030', 'Barbarian', 'Crusade 4.42c', 'GamesGod', 'Ranger 3.04', 'Shadow 4.11', 'Subjugator', 'The Horde', 'The Khanate', 'The_Unknown3', 'TRiBaL_Warrior', 'UlyssesWK', 'UnfairSteel', 'Vicky', 'VNS_Chris_Tournament', 'VNS_Halen_'] 58 | 59 | all_ai_list = ['Ace', 'Alphav2', 'Alphav3', 'Alphav4', 'alpha_drush', 'Arabian_Knight', 'AT_Scout_Rush', 'BambiV030', 'Barbarian', 'best', 'Cal', 'CD', 'Crusade 4.42c', 'Flying_Monk', 'GamesGod', 'Hun Krush', 'king', 'Ranger 3.04', 'Reactionaryv9', 'Rhapsody0004', 'Shadow 0', 'Shadow 1', 'Shadow 2', 'Shadow 3.1', 'Shadow 4.11', 'Subjugator', 'The Horde', 'The Khanate', 'The_Unknown3', 'TRiBaL_Warrior', 'UCS', 'UlyssesWK', 'UnfairSteel', 'Vermin Supreme', 'Vicky', 'VNS_Chris_Tournament', 'VNS_Halen_'] 60 | 61 | civ = 'huns' 62 | restart = False 63 | 64 | force_age_up = False 65 | allow_complex = False 66 | force_house = True 67 | allow_DUC = True 68 | 69 | if restart: 70 | villager_preset = True 71 | force_imperial_age = False 72 | force_barracks = True 73 | force_resign = False 74 | force_scout = True 75 | allow_attack_rules = False 76 | force_castle_age_units = False 77 | 78 | allow_units = True 79 | allow_towers = False 80 | 81 | else: 82 | villager_preset = True 83 | force_imperial_age = False 84 | force_barracks = True 85 | force_resign = True 86 | force_scout = True 87 | allow_attack_rules = True 88 | force_castle_age_units = False 89 | 90 | 91 | allow_units = True 92 | allow_towers = True 93 | 94 | #DUC 95 | #villager_preset = False 96 | #force_imperial_age = False 97 | #force_barracks = False 98 | #force_resign = False 99 | #force_scout = False 100 | #allow_attack_rules = True 101 | #force_castle_age_units = False 102 | # 103 | #allow_units = False 104 | #allow_towers = False 105 | --------------------------------------------------------------------------------