view gamelib/lab.py @ 45:1e8f7e694f0c

Refactor missions and sciences a bit to reduce duplication.
author Jeremy Thurgood <firxen@gmail.com>
date Mon, 07 May 2012 20:44:27 +0200
parents efc4f90cfd63
children d3d277a42ac6
line wrap: on
line source

# -*- test-case-name: gamelib.tests.test_lab -*-

from random import random, choice

from gamelib import research, products
from gamelib.game_base import get_subclasses


class Lab(object):
    BASIC_RESEARCH_SUCCESS_RATE = 0.05
    BASIC_RESEARCH_SUCCESS_MULTIPLIER = 2

    def __init__(self, init_data=None):
        self.science = []
        self.new_research = get_subclasses(research.ResearchArea)
        self.new_products = get_subclasses(products.Product)

        if init_data is not None:
            # Load stored state.
            self._load_data(init_data)
        else:
            # New game.
            self._choose_initial_science()

    def _load_data(self, init_data):
        for name, points in init_data['science'].iteritems():
            science_type, cls = name.split('.')
            if science_type == 'product':
                science = getattr(products, cls)
            elif science_type == 'research':
                science = getattr(research, cls)
            else:
                raise ValueError("Unknown science type: %s" % (science_type,))
            self._gain_science(science(points))

    def save_data(self):
        data = {'science': {}}
        for science in self.science:
            name = "%s.%s" % (science.SCIENCE_TYPE, type(science).__name__)
            data['science'][name] = science.points
        return data

    def _choose_initial_science(self):
        # We always get all starting products.
        for product in self.new_products[:]:
            if product.STARTING_PRODUCT:
                self._gain_science(product())

        # We get three random sciences with no prerequisites.
        new_science = []
        for _ in range(3):
            science = choice(self.find_new_research())()
            self._gain_science(science)
            new_science.append(science)

        # Add a point to each of our sciences, and see if we get products.
        self.spend_points(new_science, 0)

    def _gain_science(self, science):
        self.science.append(science)
        if isinstance(science, research.ResearchArea):
            self.new_research.remove(type(science))
        elif isinstance(science, products.Product):
            self.new_products.remove(type(science))

    def spend_points(self, things, basic_research):
        breakthroughs = []

        # First, allocate the points.
        for thing in things:
            assert thing in self.science
            assert thing.can_spend(self)
            thing.spend_point()

        # Next, check for product breakthroughs and upgrades
        for thing in things:
            if isinstance(thing, research.ResearchArea):
                breakthroughs.extend(self.apply_area_research(thing))

        # Finally, check for research breakthroughs.
        breakthroughs.extend(self.apply_basic_research(basic_research))

        return breakthroughs

    def _get_science(self, science_class):
        for science in self.science:
            if isinstance(science, science_class):
                return science
        return None

    def meet_requirements(self, science_class, extra=0):
        total_points = 0
        base_points = 0
        for science, level in science_class.PREREQUISITES:
            my_science = self._get_science(science)
            if my_science is None:
                return False
            if my_science.points < level:
                return False
            base_points += level
            total_points += my_science.points
        return total_points - base_points >= extra

    def find_new_products(self):
        available_products = []
        for product_class in self.new_products:
            if self.meet_requirements(product_class):
                available_products.append(product_class)
        return available_products

    def find_new_research(self):
        available_research = []
        for research_class in self.new_research:
            if self.meet_requirements(research_class):
                available_research.append(research_class)
        return available_research

    def apply_area_research(self, research):
        options = [product for product in self.find_new_products()
                   if type(research) in [p[0] for p in product.PREREQUISITES]]
        breakthroughs = [product for product in options
                         if random() < product.ACQUISITION_CHANCE]
        if breakthroughs:
            breakthrough = choice(breakthroughs)()
            self._gain_science(breakthrough)
            breakthroughs = [breakthrough]
        return breakthroughs

    def apply_basic_research(self, basic_research):
        if basic_research <= 1:
            return []

        options = self.find_new_research()
        success_chance = self.BASIC_RESEARCH_SUCCESS_RATE * (
            self.BASIC_RESEARCH_SUCCESS_MULTIPLIER ** basic_research)
        breakthroughs = [research for research in options
                         if random() < success_chance]
        if breakthroughs:
            breakthrough = choice(breakthroughs)(1)
            self._gain_science(breakthrough)
            breakthroughs = [breakthrough]
        return breakthroughs