Example #1
0
class Army(object):
    def __init__(self, units_config):
        self.units_config = units_config
        self.units = Units(units_config["units"])
        self.allies = Allies(units_config["allies"])
        self.battalions = Battalions(units_config["battalions"])
        self.all = [self.units, self.allies, self.battalions]

    def __str__(self):
        line = []
        for u in self.all:
            s = str(u)
            if s:
                line.append(s)
        return "{}: {}".format(self.points(), ", ".join(line))

    def __repr__(self):
        return str(self.units)

    def __len__(self):
        return len(self.units) + len(self.allies) + len(self.battalions)

    def fullstr(self, rules_config={}):
        points = self.points()
        if rules_config:
            line = [
                ("Points {} [{} to {}]".format(points,
                                               rules_config["points"] - points,
                                               rules_config["points"]))
            ]
        else:
            line = [("Points {}".format(points))]
        line.append(
            "\tWounds: {}, Models: {}, Bravery/Unit: {:.2f}, Save/Wound: {:.2f}+"
            .format(self.wounds(), self.unitsize(),
                    self.avg_bravery_per_unit(), self.avg_save_per_wound()))
        for u in self.all:
            s = u.fullstr()
            if s:
                line.append(s)
        line.append("Roles: {}".format(self.sum_roles_str(rules_config)))
        line.append("")
        return "\n".join(line)

    def __getitem__(self, index):
        for u in self.all:
            if index < len(u):
                return u[index]
            index = index - len(u)
        raise IndexError("index out of range")

    def __setitem__(self, index, item):
        for u in self.all:
            if index < len(u):
                u[index] = item
                return
            index = index - len(u)
        raise IndexError("index out of range")

    def add(self, name, unittype):
        if unittype == "unit":
            self.units.add(name)
        elif unittype == "ally":
            self.allies.add(name)
        elif unittype == "battalion":
            self.battalions.add(name)
        else:
            raise KeyError('Invalid unit type')

    def points(self):
        x = 0
        for u in self.all:
            x = x + u.points()
        return x

    def unitsize(self):
        x = 0
        for u in self.all:
            x = x + u.unitsize()
        return x

    def wounds(self):
        x = 0
        for u in self.all:
            x = x + u.wounds()
        return x

    def _bravery_sum(self):
        x = 0
        for u in self.all:
            x = x + u._bravery_sum()
        return x

    def _save_mul_wounds_sum(self):
        x = 0
        for u in self.all:
            x = x + u._save_mul_wounds_sum()
        return x

    def avg_bravery_per_unit(self):
        count = self.unitsize()
        if count == 0:
            return 0
        return self._bravery_sum() / float(count)

    def avg_save_per_wound(self):
        count = self.wounds()
        if count == 0:
            return 0
        return self._save_mul_wounds_sum() / float(count)

    def sum_roles(self, rules_config={}):
        r = {}
        for rulename, ruleactions in rules_config.get("units", {}).iteritems():
            r[rulename] = 0
        for u in self.all:
            u.sum_roles(r)
        return r

    def sum_roles_str(self, rules_config={}):
        roles = self.sum_roles(rules_config)
        line = []
        for role, count in roles.iteritems():
            rule = rules_config.get("units", {}).get(role, {})
            if rule:
                if rule["max"] == -1:
                    line.append("{} {} [{}+]".format(count, role, rule["min"]))
                else:
                    line.append("{} {} [{}->{}]".format(
                        count, role, rule["min"], rule["max"]))
            else:
                line.append("{} {}".format(count, role))
        return ", ".join(line)

    def __check_min_max(self, constraint, current_value, default_min,
                        default_max, restrict_config, final, showfails):
        con_min = restrict_config.get("min_" + constraint, default_min)
        con_max = restrict_config.get("max_" + constraint, default_max)

        if (current_value > con_max
                and con_max != -1) or (final and current_value < con_min):
            if showfails.value > PrintOption.SILENT.value:
                print "FAIL {}: {} {}->{} : {}".format(constraint,
                                                       current_value, con_min,
                                                       con_max, self)
            return False
        return True

    def is_valid(self,
                 rules_config,
                 restrict_config={},
                 final=True,
                 showfails=PrintOption.SILENT):

        if not self.__check_min_max(
                "points", self.points(), rules_config["points"],
                rules_config["points"], restrict_config, final, showfails):
            return False
        if not self.__check_min_max("wounds", self.wounds(), 0, -1,
                                    restrict_config, final, showfails):
            return False
        if not self.__check_min_max("models", self.unitsize(), 0, -1,
                                    restrict_config, final, showfails):
            return False
        if not self.__check_min_max("allies", self.allies.points(), 0,
                                    rules_config["allies"], restrict_config,
                                    final, showfails):
            return False

        #Default battalions to off until better support added
        if not self.__check_min_max("battalions", self.battalions.num(), 0, 0,
                                    restrict_config, final, showfails):
            return False

        for u in self.all:
            if not u.is_valid(restrict_config, final, showfails):
                return False

        # Check roles
        rules_check = self.sum_roles(rules_config)
        for role, count in rules_check.iteritems():
            # Check role meets min requirements
            if final and count < rules_config["units"][role]["min"]:
                if showfails.value > PrintOption.SILENT.value:
                    print "FAIL Role MIN {} {} {} : {}".format(
                        role, rules_config["units"][role]["min"], count, self)
                return False
            # Check role meets max requirements
            if rules_config["units"][role][
                    "max"] != -1 and count > rules_config["units"][role]["max"]:
                if showfails.value > PrintOption.SILENT.value:
                    print "FAIL Role MAX {} {} {} : {}".format(
                        role, rules_config["units"][role]["max"], count, self)
                return False

        return True