Home World Forum
Stars! AutoHost web forums

Jump to Stars! AutoHost


 
 
Home » Stars! Clones, Extensions, Modding » Stars! Nova - Open Discussion and Help » Stars! Nova 0.4.0 (developmental) Release
Re: Stars! Nova 0.4.0 (developmental) Release Fri, 25 June 2010 14:21 Go to previous messageGo to previous message
sirgwain is currently offline sirgwain

 
Senior Chief Petty Officer

Messages: 86
Registered: March 2004
Location: Tucson
Quote:

I have seen a web based race designer but I haven't seen the code/guts/algorithm, can you point me in the right direction?

Ready, aim... go!
Also, this old FreeStars thread full of pointers. Deal

I had to actually decompile the flash race wizard to get the algorithm for planet hab calculations. It's a pain in the neck.

For posterity, here is the python code in all its glory. This is the Race class which has the calculations for advantage points. I haven't been working on this in a while. It's almost a straight copy of the code I decompiled from the flash version and I tried to add comments where I could figure out what was going on. It still makes my head spin a bit. It's also TERRIBLY slow. I don't know if you'd see this in a compiled language, but in a scripting language like python it takes a long time to generate all those fake planets for the hab values. Given how fast and lightweight the original Stars! was, I wonder if this algorithm is entirely accurate.

class Race(object):
    """
    A race the user uses in the game.  Each user can have multiple
    races, and when joining a game a copy of the race is made and assigned to the game
    """
    
    def __init__(self, name, plural_name=None):
        self.name = name
        if plural_name is None:
            self.plural_name = str(name) + 's'
        else:
            self.plural_name = plural_name
            
        self.game = None
        self.user = None
        self.prt = None
        self.lrts = []
        self.hab_low = Hab()
        self.hab_high = Hab()
        self.growth_rate = None
        self.colonists_per_resource = None
        self.factory_output = None
        self.factory_cost = None
        self.num_factories = None
        self.factories_cost_less = None
        self.mine_output = None
        self.mine_cost = None
        self.num_mines = None
        self.techs_start_high = None
        self.spend_leftovers_on = None
        self.immune_grav = None
        self.immune_temp = None
        self.immune_rad = None
        self.research_cost = ResearchCost()
        
        # hab points used in race point calcs
        self.habpoints = None

    def __repr__(self):
        return "<Race: %s (%s) PRT: %s, LRTs: %s, Hab(%s -> %s immune: (%s %s %s)), rc: %s>" % (self.name, self.plural_name, self.prt, self.lrts, self.hab_low, self.hab_high, self.immune_grav, self.immune_temp, self.immune_rad, self.research_cost)

    @classmethod
    def humanoid(cls, user):
        """
        Create an instance of a Race with default Humanoid properties
        """
        race = Race('Humanoid', 'Humanoids')
        race.prt = PRT.JoaT
        race.lrts = []
        race.hab_low = Hab(15, 15, 15)
        race.hab_high = Hab(85, 85, 85)
        
        race.growth_rate = .15
        race.colonists_per_resource = 1000
        race.factory_output = 10
        race.factory_cost = 10
        race.num_factories = 10
        race.factories_cost_less = False
        race.mine_output = 10
        race.mine_cost = 5
        race.num_mines = 10
        race.techs_start_high = False
        race.immune_grav = False
        race.immune_temp = False
        race.immune_rad = False
        race.spend_leftovers_on = SpendLeftoversOn.SurfaceMinerals
        
        race.research_cost = ResearchCost(ResearchCostLevel.Standard, ResearchCostLevel.Standard, ResearchCostLevel.Standard, ResearchCostLevel.Standard, ResearchCostLevel.Standard, ResearchCostLevel.Standard)
        
        race.user = user
        race.init()
        return race
        
        
    def habcenter(self, index):
        """
        Return the center point of this hab, i.e. for
        hab 25 to 75 the center is 50
        hab 60 to 100 the center is 80
        """
        #return (self.hab_high[index] + self.hab_low[index]) / 2
        return self._habcenter[index]
        
    def habwidth(self, index):
        """
        The habwidth of a race (away from center)
        for 25 to 75 the width is 25 (25 away from 50)
        for 60 to 100 the width is 20        
        """
        #return (self.hab_high[index] - self.hab_low[index]) / 2
        return self._habwidth[index]
        
    def immune(self, index):
        """
        Return true if this race is immmune to the given index of hab
        type, i.e. 0 == immune_grav
        """
        if index == 0:
            return self.immune_grav
        elif index == 1:
            return self.immune_temp
        else:
            return self.immune_rad
    
    def research_cost_for_level(self, field, level):
        """
        Get the research cost for a tech field/level
        """
        cost = Consts.tech_research_cost[level]
        rcl = self.research_cost[field]
        if rcl == ResearchCostLevel.Extra:
            cost *= (1.75)
        elif rcl == ResearchCostLevel.Less:
            cost *= (.5)

        return int(cost)
    
    def haslrt(self, lrt):
        return self.lrts is not None and lrt in self.lrts
        
    def advantage_points(self):
        """
        Compute the advantage points for this race
        """
        points = Consts.race_starting_points
        
        if self.habpoints is None:
            self.habpoints = self._hab_points() / 2000;
        
        gr_factor = int(self.growth_rate * 100 + 0.5);   # use raw growth rate, otherwise HEs pay for GR at 2x
        gr_rate = gr_factor;

        # update the points based on growth rate
        if gr_factor <= 5:
            points += (6 - gr_factor) * 4200;
        elif gr_factor <= 13:
            if gr_factor == 6: points += 3600
            if gr_factor == 7: points += 2250
            if gr_factor == 8: points += 600
            if gr_factor == 9: points += 225
            gr_factor = gr_factor * 2 - 5;
        elif gr_factor < 20:
            gr_factor = (gr_factor - 6) * 3
        else:
            gr_factor = 45;

        points -= int(self.habpoints * gr_factor + .5) / 24
        
        # give points for off center habs
        immunities = 0
        for habtype in xrange(0, 3):
            if self.immune(habtype):
                immunities += 1
            else:
                points += abs(self.habcenter(habtype) - 50) * 4
            
        # multiple immunities are penalized extra            
        if immunities > 1:
            points -= 150
            
        # determine factory costs
        oper_points = self.num_factories;
        prod_points = self.factory_output;

        if oper_points > 10 or prod_points > 10:
            oper_points -= 9
            if oper_points < 1:
                oper_points = 1
            prod_points -= 9
            if prod_points < 1:
                prod_points = 1

            # HE penalty, 2 for all PRTs execpt 3 for HE
            if self.prt == PRT.HE:
                factory_production_cost = 3
            else:
                factory_production_cost = 2
            
            prod_points *= factory_production_cost

            # additional penalty for two- and three-immune
            if immunities >= 2:
                points -= ((prod_points * oper_points) * gr_rate) / 2
            else:
                points -= ((prod_points * oper_points) * gr_rate) / 9
    
        # pop efficiency
        popeff = self.colonists_per_resource / 100
        if popeff > 25: popeff = 25
        
        if popeff <=  7: points -= 2400
        elif popeff == 8: points -= 1260
        elif popeff == 9: points -= 600
        elif popeff > 10: points += (popeff - 10) * 120
        
        # factory points (AR races have very simple points)
        if self.prt == PRT.AR:
            points += 210
        else:
            prodpoints = 10 - self.factory_output
            costpoints = 10 - self.factory_cost
            operpoints = 10 - self.num_factories
            tmppoints = 0
            
            if prodpoints > 0:
                tmppoints = prodpoints * 100
            else:
                tmppoints = prodpoints * 121
            
            if costpoints > 0:
                tmppoints += costpoints * costpoints * -60
            else:
                tmppoints += costpoints * -55
            
            if operpoints > 0:
                tmppoints += operpoints * 40
            else:
                tmppoints += operpoints * 35
            
            # limit low factory points
            llfp = 700
            if tmppoints > llfp:
                tmppoints = (tmppoints - llfp) / 3 + llfp
            
            
            if operpoints <= -7:
                if operpoints < -11:
                    if operpoints < -14:
                        tmppoints -= 360
                    else:
                        tmppoints += (operpoints + 7) * 45
                else:
                    tmppoints += (operpoints + 6) * 30
            
            if operpoints <= -3:
                tmppoints += (prodpoints + 2) * 60
            """
            I have this commented out in my code.  I wish I could remember why.  I don't think it's needed but I can't remember where it came from.  Dang.
            if operpoints > 14:
                tmppoints -= 360
            elif operpoints > 11:
                tmppoints -= (operpoints - 7) * 45
            elif operpoints > =7:
                tmppoints -= (operpoints - 6) * 30
            
            if prodpoints >= 3:
                tmppoints -= (prodpoints -2) * 60
            """
            points += tmppoints
            
            if self.factories_cost_less:
                points -= 175
            
            # mines
            prodpoints = 10 - self.mine_output
            costpoints = 3 - self.mine_cost
            operpoints = 10 - self.num_mines
            tmppoints = 0
            
            if prodpoints > 0:                
                tmppoints = prodpoints * 100
            else:
                tmppoints = prodpoints * 169
            
            if costpoints > 0:
                tmppoints -= 360
            else:
                tmppoints += (costpoints * -65 + 80)
            
            if operpoints > 0:
                tmppoints += operpoints * 40
            else:
                tmppoints += operpoints * 35
            
            points += tmppoints
    
        # prt and lrt point costs
        points -= prt_point_cost[self.prt]
        if self.lrts is not None:
            for lrt in self.lrts:
                points -= lrt_point_cost[lrt]
    
        # too many lrts
        goodlrts = 0
        badlrts = 0
        
        # figure out how many bad vs good lrts we have.
        if self.lrts is not None:
            for lrt in self.lrts:
                if lrt_point_cost[lrt] > 0:
                    goodlrts += 1
                else:
                    badlrts += 1
        
        if badlrts + goodlrts > 4:
            points -= (badlrts + goodlrts) * (badlrts + goodlrts - 4) * 10
        if badlrts - goodlrts > 3:
            points -= (badlrts - goodlrts - 3) * 60
        if goodlrts - badlrts > 3:
            points -= (goodlrts - badlrts - 3) * 40
        
        # No Advanced scanners is penalized in some races
        if self.lrts is not None and LRT.NAS in self.lrts:
            if self.prt == PRT.PP:
                points -= 280
            elif self.prt == PRT.SS:
                points -= 200
            elif self.prt == PRT.JoaT:
                points -= 40
        
        # techs
        techcosts = 0
        for rc in self.research_cost:
            if rc == ResearchCostLevel.Extra:
                techcosts -= 1
            elif rc == ResearchCostLevel.Less:
                techcosts += 1
        if techcosts > 0:
            points -= (techcosts**2)*130
            if techcosts >= 6:
                points += 1430 # already paid 4680 so true cost is 3250
            elif techcosts == 5:
                points += 520 # already paid 3250 so true cost is 2730
        elif techcosts < 0:
            techcosts -= techcosts
            points += techcosts * (techcosts + 9) * 15
            if techcosts >= 6:
                points += 30
            if techcosts > 4 and self.colonists_per_resource < 1000:
                points -= 190
        
        if self.techs_start_high:
            points -= 180
        
        if self.prt == PRT.AR and self.research_cost.energy == ResearchCostLevel.Extra:
            points -= 100
        
        return points / 3
    
    def _hab_points(self):
        rp = RacePoints(self)
        return rp.points()
        
        
class RacePoints(object):
    
    def __init__(self, race):
        self.race = race        
        
    def points(self):
        # create a blank planet with no name at (0, 0)
        self.planet = Planet('', 0, 0)
        # create a blank Habitability setting
        self.planet.hab = Hab()
        
        self.test_hab_width = [50, 50, 50]
        self.test_hab_start = [20, 20, 20]
        self.iternum = [11, 11, 11]
        self.tfdone = [0, 0, 0]
        if self.race.lrts is not None and LRT.TT in self.race.lrts:
            self.ttfactor = [0, 8, 17]
        else:
            self.ttfactor = [0, 5, 15]
                    
        points = 0.0
        
        for h in xrange(0, 3):
            if h == 0:
                desire_factor = 7
                ttfactor = self.ttfactor[0]
            elif h == 1:
                desire_factor = 5
                ttfactor = self.ttfactor[1]
            else:
                desire_factor = 6
                ttfactor = self.ttfactor[2]
    
            for habtype in xrange(0, 3):
                if self.race.immune(habtype):
                    self.test_hab_start[habtype] = 50
                    self.test_hab_width[habtype] = 11
                    self.iternum[habtype] = 1
                else:
                    self.test_hab_start[habtype] = self.race.habcenter(habtype) - self.race.habwidth(habtype) - ttfactor
                    if self.test_hab_start[habtype] < 0:
                        self.test_hab_start[habtype] = 0
                    tmphab = self.race.habcenter(habtype) + self.race.habwidth(habtype) + ttfactor
                    if tmphab > 100:
                        tmphab = 100
                    self.test_hab_width[habtype] = tmphab - self.test_hab_start[habtype];
            points += self.points_recursion(0, desire_factor, ttfactor)
        
        return int(points / 10.0 + .5)   
        
    def points_recursion(self, habtype, desire_factor, ttfactor):
        sumoverhab = 0.0
        
        if habtype < 3:
            for i in xrange(0, self.iternum[habtype]):
                if i == 0 or self.iternum[habtype] <= 1:
                    tmphab = self.test_hab_start[habtype]
                else:
                    tmphab = self.test_hab_width[habtype] * i / (self.iternum[habtype] - 1) + self.test_hab_start[habtype]
                
                # account for terraforming
                if ttfactor != 0 and not self.race.immune(habtype):
                    tmphab2 = self.race.habcenter(habtype) - tmphab
                    if abs(tmphab2) <= ttfactor:
                        tmphab2 = 0
                    elif tmphab2 < 0:
                        tmphab2 += ttfactor
                    else:
                        tmphab2 -= ttfactor
                    
                    self.tfdone[habtype] = tmphab2
                    tmphab = self.race.habcenter(habtype) - tmphab2
                
                self.planet.hab[habtype] = tmphab
                sumoverhab += self.points_recursion(habtype + 1, desire_factor, ttfactor)
            
            if not self.race.immune(habtype):
                sumoverhab = sumoverhab * self.test_hab_width[habtype] / 100;
            else:
                # if immune to this trait, the value is always the same
                # so just sum it all 11 times
                sumoverhab *= 11;
        else:

            sumoverhab = self.planet.hab_value(self.race)#, pall, rall, iftall)
            maxterra = 0
            for i in xrange(0, 3):
                maxterra += self.tfdone[i]
            
            if maxterra > ttfactor:
                sumoverhab -= maxterra - ttfactor
                if sumoverhab < 0:
                    sumoverhab = 0
            sumoverhab *= sumoverhab
            sumoverhab *= desire_factor
        
        return sumoverhab



Report message to a moderator

 
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Read Message
Previous Topic: Stars! Nova - May Update
Next Topic: Stars! Nova 0.4.1 Developmental Release
Goto Forum:
  


Current Time: Sun May 05 15:17:44 EDT 2024