view gamelib/lab.py @ 115:ef63532cac13

Rearrange SCIENCE a bit.
author Jeremy Thurgood <firxen@gmail.com>
date Wed, 09 May 2012 23:10:29 +0200
parents 182fce9f70b6
children 14917385a0fd
line wrap: on
line source

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

from random import random, choice

from gamelib import research, schematics
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_schematics = get_subclasses(schematics.Schematic)

        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):
        sciences = init_data['science'].copy()
        for science in self.new_schematics + self.new_research:
            # Check if this science is one we should know.
            points = sciences.pop(science.save_name(), None)
            if points is not None:
                # It is! Learn it.
                self._gain_science(science(points))
        if sciences:
            # We're supposed to know an unknowable thing. :-(
            raise ValueError("Unknown science: %s" % (sciences.keys(),))

    def save_data(self):
        return {'science': dict(s.save_data() for s in self.science)}

    def _choose_initial_science(self):
        # We always get all starting schematics.
        for schematic in self.new_schematics[:]:
            if schematic.STARTING_PRODUCT:
                self._gain_science(schematic())

        # We start with Physics, because it's not Philately.
        physics = research.Physics()
        self._gain_science(physics)
        new_science = [physics]

        # We get two other random sciences with no prerequisites.
        for _ in range(2):
            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 schematics.
        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, schematics.Schematic):
            self.new_schematics.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, 1)
            thing.spend_point()

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

        # 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_schematics(self):
        available_schematics = []
        for schematic_class in self.new_schematics:
            if self.meet_requirements(schematic_class):
                available_schematics.append(schematic_class)
        return available_schematics

    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, researches):
        options = [schema for schema in self.find_new_schematics()
                   if schema.depends_on(researches)]
        breakthroughs = [schematic for schematic in options
                         if random() < schematic.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