def check_type(varname, obj, wtype, allow_none=False): """Check object for a wanted type. Parameters ---------- varname : str Variable name. obj : Object Object to be chekced. wtype : Type Wanted Type. Raises ------ TypeError Returns ------- None. """ if allow_none and obj is None: return if not isinstance(obj, wtype): err_msg = "%s should be %s type, not %s"%(varname, wtype, type(obj)) write_log(err_msg) raise TypeError(err_msg)
def _check_arrange(self, offense: Team, defense: Team, league_round=None): if offense.num_positions == defense.num_positions == 0: return offense_cpy = copy.deepcopy(offense) defense_cpy = copy.deepcopy(defense) t_beg = time.perf_counter() offense_cpy.arrange(defense_cpy) t_end = time.perf_counter() t_elapsed = t_end - t_beg t_limit = self._get_time_limit(league_round) if t_elapsed > t_limit: err_msg = "[%s] The duration of arrangement " \ "should be less than or " \ "equal to %f, not %f!"% \ ( offense.name, t_limit, t_elapsed ) write_log(err_msg) raise ArrangeTimeoutError(offense, err_msg) self._check_consistency(offense, offense_cpy, "arrangement")
def _try_evasion(self, target): evsr = target.evs / 100. # Evasion Rate (EVSR) rn = random.uniform(0, 1) if rn <= evsr: write_log("%s evades with %.4f! (RN: %.4f)."%(target.name, evsr, rn)) return True return False
def _clear_dead_units(self, team: Team): for i, unit in enumerate(team): if not unit: continue if unit.hp <= 0: team[i] = None write_log("%s.%s has been dead..."%(unit.team.name, unit.name))
def _check_unit_uniqueness(self, team: Team): set_ids = set([id(unit) for unit in team]) if len(set_ids) != len(team): err_msg = "[%s] Each unit should be unique! " \ "%s includes redundant unit instances."%(team.name, team.name) write_log(err_msg) raise RuntimeError(err_msg)
def check_nonnegative_float(varname, val): if not isinstance(val, float) and not isinstance(val, int): err_msg = "%s should be float type, not %s."%(varname, type(val)) write_log(err_msg) raise ValueError(err_msg) if val < 0.: err_msg = "%s cannot be negative."%(varname) write_log(err_msg) raise ValueError(err_msg)
def check_nonnegative_int(varname, val): if not isinstance(val, int): err_msg = "%s should be int type." % (varname) write_log(err_msg) raise ValueError(err_msg) if val < 0: err_msg = "%s cannot be negative." % (varname) write_log(err_msg) raise ValueError(err_msg)
def _check_consistency(self, origin: Team, copied: Team, situation: str): if len(origin) != len(copied): err_msg = "[%s] The size of the team " \ "has been changed in %s!"%(origin.name, situation) write_log(err_msg) raise TeamConsistencyError(origin, err_msg) if origin != copied: err_msg = "[%s] The units has been changed " \ "during %s!"%(origin.name, situation) write_log(err_msg) raise TeamConsistencyError(origin, err_msg)
def check_team_consistency(obj1, obj2, situation): if len(obj1) != len(obj2): err_msg = "Team size has been changed during %s!"%(situation) write_log(err_msg) raise RuntimeError(err_msg) set1_units = set([unit for unit in obj1]) set2_units = set([unit for unit in obj2]) if set1_units != set2_units: err_msg = "The units in the team %s " \ "has been changed during %s!"%(obj1.name, situation) write_log(err_msg) raise RuntimeError(err_msg)
def _apply_attack(self, offense: Team, defense: Team): for i, unit in enumerate(offense): target = defense[i] if unit and target: unit_cpy = copy.deepcopy(unit) target_cpy = copy.deepcopy(defense[i]) unit.attack(target) # Check consistency utils.attack(unit_cpy, target_cpy, Unit) if unit_cpy != unit: err_msg = "%s.attack() performs "\ "illegal behaviors!"%(unit.__class__) write_log(err_msg) raise RuntimeError(err_msg)
def _check_cons_range(self, unit_name, attr_name, attr_val, min_attr, max_attr): if (attr_val < min_attr) or (max_attr < attr_val): err_msg = "[%s] The %s of each unit should be " \ "within [%.2f, %.2f], not %.2f!"% \ ( unit_name, attr_name, min_attr, max_attr, attr_val ) write_log(err_msg) raise ValueError(err_msg)
def attack(self, target: Unit): utils.check_type("target", target, Unit) write_log( "Before attack, %s.%s.HP=%.2f, %s.%s.HP=%.2f"% ( self.team.name, self.name, self.hp, target.team.name, target.name, target.hp ) ) target.hp = max(0, target.hp - max(1, self.att - target.arm)) self.hp = max(0, self.hp - max(1, 0.5*target.att - self.arm)) write_log("%s.%s attacks %s.%s"%(self.team.name, self.name, target.team.name, target.name)) write_log( "After attack, %s.%s.HP=%.2f, %s.%s.HP=%.2f"% ( self.team.name, self.name, self.hp, target.team.name, target.name, target.hp ) )
def play(self, team1: Team, team2: Team, num_turns: int = 10, num_repeats: int = 10): utils.check_type("team1", team1, Team) utils.check_type("team2", team2, Team) if len(team1) != len(team2): err_msg = "The sizes of team1 and team2 dost not match!" write_log(err_msg) raise ValueError(err_msg) num_wins_team1 = 0 num_wins_team2 = 0 num_draws = 0 for i in range(num_repeats): team1_cpy = copy.deepcopy(team1) team2_cpy = copy.deepcopy(team2) if i % 2 == 0: offense, defense = team1_cpy, team2_cpy else: offense, defense = team2_cpy, team1_cpy for t in range(num_turns): write_log("[%s vs %s - Repeat #%d Turn #%d]" % (team1.name, team2.name, i + 1, t + 1)) self._examiner.check_play(offense, defense, self._league_round) # Arrange defense_cpy = copy.deepcopy(defense) offense.arrange(defense_cpy) # Attack self._apply_attack(offense, defense) self._clear_dead_units(offense) self._clear_dead_units(defense) write_log("#Units in %s=%d, #Units in %s=%d" % (team1.name, len(team1), team2.name, len(team2))) if len(offense) == 0 or len(defense) == 0: break offense, defense = defense, offense # end of for if len(team1_cpy) > len(team2_cpy): # Team #1 wins. num_wins_team1 += 1 elif len(team1_cpy) < len(team2_cpy): num_wins_team2 += 1 else: # Draw num_draws += 1 # end of for return num_wins_team1, num_wins_team2, num_draws
def _check_constraints(self, team: Team, league_round=None): constraints = self._constraints if not league_round: league_round = "ROUND-01" league_round = league_round.upper() if league_round == "ROUND-01": CONS_TEAM = constraints[league_round]['TEAM'] CONS_NUM_UNITS = CONS_TEAM['NUM_UNITS'] CONS_UNIT_MAX_EVS = CONS_TEAM['UNIT_MAX_EVS'] CONS_SUM_HP_ATT_ARM = CONS_TEAM['SUM_HP_ATT_ARM'] CONS_SUM_UNIT_EVS_DIV_ARM = CONS_TEAM['SUM_UNIT_EVS_DIV_ARM'] if len(team) != CONS_NUM_UNITS: err_msg = "[%s] The number of units should be" \ " %d, not %d"%(team.name, CONS_NUM_UNITS, len(team)) write_log(err_msg) raise ValueError(err_msg) sum_hp = 0 sum_att = 0 sum_arm = 0 sum_unit_evs_div_arm = 0 for unit in team: if unit.evs > CONS_UNIT_MAX_EVS: err_msg = "[%s] The evs of each unit should be " \ "less than or equal to %.2f, not %.2f!"% \ ( unit.name, CONS_UNIT_MAX_EVS, unit.evs ) write_log(err_msg) raise ValueError(err_msg) # end of if sum_hp += unit.hp sum_att += unit.att sum_arm += unit.arm sum_unit_evs_div_arm += (float(unit.evs) / float(unit.arm)) # end of for sum_hp_att_arm = sum_hp + sum_att + sum_arm if round(sum_hp_att_arm, 4) > CONS_SUM_HP_ATT_ARM: err_msg = "[%s] The summation of HP, ATT, and ARM " \ "of all units in a team should be less than " \ "or equal to %.2f, not %.2f!"% \ ( team.name, CONS_SUM_HP_ATT_ARM, sum_hp_att_arm ) write_log(err_msg) raise ValueError(err_msg) if round(sum_unit_evs_div_arm, 4) > CONS_SUM_UNIT_EVS_DIV_ARM: err_msg = "[%s] The summation of EVS/ARM of all units " \ "in a team should be less than or " \ "equal to %.2f, not %.2f!"% \ ( team.name, CONS_SUM_UNIT_EVS_DIV_ARM, sum_unit_evs_div_arm ) write_log(err_msg) raise ValueError(err_msg) elif league_round == "ROUND-02": CONS_TEAM = constraints[league_round]['TEAM'] CONS_NUM_UNITS = CONS_TEAM['NUM_UNITS'] CONS_UNIT_MAX_ATT = CONS_TEAM['UNIT_MAX_ATT'] CONS_UNIT_MIN_HP = CONS_TEAM['UNIT_MIN_HP'] CONS_UNIT_MAX_EVS = CONS_TEAM['UNIT_MAX_EVS'] CONS_UNIT_SUM_HP_ATT_1d5ARM = CONS_TEAM['UNIT_SUM_HP_ATT_1d5ARM'] if len(team) != CONS_NUM_UNITS: err_msg = "[%s] The number of units should be" \ " %d, not %d"%(team.name, CONS_NUM_UNITS, len(team)) write_log(err_msg) raise ValueError(err_msg) for unit in team: if unit.ATT > CONS_UNIT_MAX_ATT: err_msg = "[%s] The ATT of each unit should be " \ "less than or equal to %.2f, not %f!"% \ ( unit.name, CONS_UNIT_MAX_ATT, unit.ATT ) write_log(err_msg) raise ValueError(err_msg) # end of if if unit.HP < CONS_UNIT_MIN_HP: err_msg = "[%s] The HP of each unit should be " \ "greater than or equal to %.2f, not %f!"% \ ( unit.name, CONS_UNIT_MIN_HP, unit.HP ) write_log(err_msg) raise ValueError(err_msg) # end of if if unit.evs > 0: err_msg = "[%s] The evs of each unit should be zero, " \ "not %.2f!"%(unit.name, unit.evs) write_log(err_msg) raise ValueError(err_msg) # end of if sum_unit_hp_att_1d5arm = (unit.HP + unit.ATT + 1.5*unit.ARM) if sum_unit_hp_att_1d5arm > CONS_UNIT_SUM_HP_ATT_1d5ARM: err_msg = "[%s] The summation of HP, ATT and 1.5*ARM of " \ "each unit should be less than or " \ "equal to %.2f, not %f!"% \ ( team.name, CONS_UNIT_SUM_HP_ATT_1d5ARM, sum_unit_hp_att_1d5arm ) write_log(err_msg) raise ValueError(err_msg) # end of if # end of for elif league_round == "ROUND-03": CONS_TEAM = constraints[league_round]['TEAM'] CONS_UNIT_MAX_EVS = CONS_TEAM['UNIT_MAX_EVS'] for unit in team: if unit.evs > 0: err_msg = "[%s] The evs of each unit should be zero, " \ "not %.2f!"%(unit.name, unit.evs) write_log(err_msg) raise ValueError(err_msg) # end of if else: err_msg = "league_round=%s is not defined!"%(league_round) write_log(err_msg) raise ValueError(err_msg)
def _check_name(self, team: Team): if team.name.isspace() or len(team.name.split()) == 0: err_msg = "Team name cannot be whitespace!" write_log(err_msg) raise ValueError(err_msg)
def play(self, team1: Team, team2: Team, num_turns: int = 10, num_repeats: int =10, judge: Judge = None): utils.check_type("team1", team1, Team) utils.check_type("team2", team2, Team) utils.check_type("judge", judge, Judge, allow_none=True) if not judge: judge = MaxSurvivalJudge() if len(team1) != len(team2): err_msg = "The sizes of team1 and team2 dost not match!" write_log(err_msg) raise ValueError(err_msg) num_wins_team1 = 0 num_wins_team2 = 0 num_draws = 0 for i in range(num_repeats): team1_cpy = copy.deepcopy(team1) team2_cpy = copy.deepcopy(team2) if i % 2 == 0: offense, defense = team1_cpy, team2_cpy else: offense, defense = team2_cpy, team1_cpy judge.initialize() for t in range(num_turns): write_log("[%s vs %s - Repeat #%d Turn #%d]"%(team1.name, team2.name, i+1, t+1)) self._examiner.check_play(offense, defense, self._league_round) # Arrange defense_cpy = copy.deepcopy(defense) offense.arrange(defense_cpy) # Attack self._apply_attack(offense, defense) self._clear_dead_units(offense) self._clear_dead_units(defense) write_log("#Units in %s=%d, #Units in %s=%d"%(team1.name, len(team1_cpy), team2.name, len(team2_cpy))) judge.update(t, team1_cpy, team2_cpy) if len(offense) == 0 or len(defense) == 0: break offense, defense = defense, offense # end of for winner = judge.decide(team1_cpy, team2_cpy) if winner == team1.name: num_wins_team1 += 1 elif winner == team2_cpy.name: num_wins_team2 += 1 else: # Draw num_draws += 1 # end of for return num_wins_team1, num_wins_team2, num_draws
def _check_constraints(self, team: Team, league_round=None): constraints = self._constraints if not league_round: league_round = "ROUND-01" league_round = league_round.upper() if league_round == "ROUND-01": CONS_TEAM = constraints[league_round]['TEAM'] CONS_NUM_UNITS = CONS_TEAM['NUM_UNITS'] CONS_MAX_EVS = CONS_TEAM['MAX_EVS'] CONS_SUM_HP_ATT_ARM = CONS_TEAM['SUM_HP_ATT_ARM'] CONS_SUM_EVS_DIV_ARM = CONS_TEAM['SUM_EVS_DIV_ARM'] if len(team) != CONS_NUM_UNITS: err_msg = "[%s] The number of units should be" \ " %d, not %d"%(team.name, CONS_NUM_UNITS, len(team)) write_log(err_msg) raise ValueError(err_msg) sum_hp = 0 sum_att = 0 sum_arm = 0 sum_evs_div_arm = 0 for unit in team: if unit.evs > CONS_MAX_EVS: err_msg = "[%s] The evs of each unit should be " \ "less than or equal to %.2f, not %.2f!"% \ ( unit.name, CONS_MAX_EVS, unit.evs ) write_log(err_msg) raise ValueError(err_msg) # end of if sum_hp += unit.hp sum_att += unit.att sum_arm += unit.arm sum_evs_div_arm += (float(unit.evs) / float(unit.arm)) # end of for sum_hp_att_arm = sum_hp + sum_att + sum_arm if round(sum_hp_att_arm, 4) > CONS_SUM_HP_ATT_ARM: err_msg = "[%s] The summation of HP, ATT, and ARM " \ "of all units in a team should be less than " \ "or equal to %.2f, not %.2f!"% \ ( team.name, CONS_SUM_HP_ATT_ARM, sum_hp_att_arm ) write_log(err_msg) raise ValueError(err_msg) if round(sum_evs_div_arm, 4) > CONS_SUM_EVS_DIV_ARM: err_msg = "[%s] The summation of EVS/ARM of all units " \ "in a team should be less than or " \ "equal to %.2f, not %.2f!"% \ ( team.name, CONS_SUM_EVS_DIV_ARM, sum_evs_div_arm ) write_log(err_msg) raise ValueError(err_msg) else: err_msg = "league_round=%s is not defined!" % (league_round) write_log(err_msg) raise ValueError(err_msg)