Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python2.6
- # -*- coding: utf-8 -*-
- """
- Simulation for the Feudalism Problem: http://i.t4z.com.ar/4n5
- """
- __author__ = 'Santiago Coffey'
- __email__ = 'scoffey@alu.itba.edu.ar'
- __version__ = '0.1'
- __license__ = 'GNU General Public License 3'
- __docformat__ = 'restructuredtext en'
- import logging
- import optparse
- import sys
- logger = logging.getLogger(__name__)
- class SimulationInputError(ValueError):
- """ Exception for invalid simulation input """
- class Simulation(object):
- """ ABC for a simulation """
- iteration = 0
- def run(self, input_stream, output_stream):
- """ Runs the simulation loop """
- output = self.simulate("0 0")
- output_stream.write(output + '\n')
- output_stream.flush()
- while True:
- line = input_stream.readline()
- if not line:
- logger.debug('End of simulation (%d iterations run)', \
- self.iteration)
- break
- try:
- line = line.rstrip('\n')
- output = self.simulate(line)
- output_stream.write(output + '\n')
- output_stream.flush()
- self.iteration += 1
- except SimulationInputError:
- logger.error('Invalid input: %s', line)
- except Exception, e:
- logger.exception('Simulation error: %s', e)
- return self.iteration
- def simulate(self, data):
- """ Performs an iteration of the simulation """
- raise NotImplementedError('Abstract method')
- class BaseFeudalSimulation(Simulation):
- """ Simulation of the Feudalism problem: """
- CASTLES_ANNUAL_WEALTH = [700, 300]
- castle = None
- def simulate(self, data):
- """ Performs an iteration of the simulation """
- try:
- population = map(int, data.strip().split(' '))
- except Exception:
- raise SimulationInputError()
- logger.debug('Year %d population: %r', self.iteration, population)
- last_castle_name = self.get_castle_name(self.castle)
- self.castle = self.find_target_castle(population)
- next_castle_name = self.get_castle_name(self.castle)
- logger.debug('Moving from castle %s to %s', \
- last_castle_name, next_castle_name)
- return next_castle_name
- def find_target_castle(self, population):
- """ Finds the castle that pays best given the last population stats """
- salaries = []
- ws = self.CASTLES_ANNUAL_WEALTH
- for i, w, p in zip(range(len(ws)), ws, population):
- salary = self._compute_castle_salary(i, w, p)
- logger.debug('Salary at castle %s is %s', \
- self.get_castle_name(i), salary)
- salaries.append(salary)
- return self._compute_target_castle(salaries)
- def _compute_castle_salary(self, i, w, p):
- """ Computes current castle salary without further forecasts """
- return w / float(p or 1)
- def _compute_target_castle(self, salaries):
- """ Computes which castle pays best """
- return salaries.index(max(salaries))
- def get_castle_name(self, castle):
- """ Converts a castle index (int) to a castle name (str) """
- return '(none)' if castle is None else chr(ord('A') + castle)
- class FeudalSimulation(BaseFeudalSimulation):
- """ Extends BaseFeudalSimulation by considering the salary drop
- due to own move """
- def _compute_castle_salary(self, i, w, p):
- """ Computes castle salary considering own move """
- p += (0 if i == self.castle else 1)
- return w / float(p or 1)
- class PreventiveFeudalSimulation(FeudalSimulation):
- """ Extends FeudalSimulation by giving a weight to the predictable move """
- PREVENTIVE_FACTOR = 2
- def _compute_castle_salary(self, i, w, p):
- """ Computes castle salary giving weight to the predictable move """
- p += (0 if i == self.castle else 1) * self.PREVENTIVE_FACTOR
- return w / float(p or 1)
- class ReallyPreventiveFeudalSimulation(PreventiveFeudalSimulation):
- """ Same as PreventiveFeudalSimulation with a higher weight """
- PREVENTIVE_FACTOR = 4
- class RunnerUpFeudalSimulation(FeudalSimulation):
- """ Extends FeudalSimulation by selecting the second castle that
- pays best """
- def _compute_target_castle(self, salaries):
- """ Computes the second castle that pays best """
- target = salaries.index(max(salaries))
- salaries[target] = 0
- return salaries.index(max(salaries))
- def parse_options(args):
- """ Parses program args into options """
- parser = optparse.OptionParser('usage: %prog [options] ' \
- '[castles annual wealth values]')
- parser.add_option('-i', '--input', dest='input', \
- help='input filename')
- parser.add_option('-o', '--output', dest='output', \
- help='output filename')
- parser.add_option('-s', '--strategy', dest='strategy', \
- help='strategy number (0-4)', type='int', default=0)
- parser.add_option('-v', '--verbose', action='store_true', \
- help='log debug info (via stderr)', default=False)
- return parser.parse_args(list(args))
- def make_simulation(*args):
- """ Validates options and sets up the simulation """
- STRATEGIES = [
- BaseFeudalSimulation(), # ignores population changes
- FeudalSimulation(), # considers population changes
- PreventiveFeudalSimulation(), # prevents predictable move
- ReallyPreventiveFeudalSimulation(), # idem, more preventive
- RunnerUpFeudalSimulation(), # chooses second best castle
- ]
- (options, args) = parse_options(args)
- if options.verbose:
- logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
- else:
- logging.basicConfig(stream=sys.stderr, level=logging.INFO)
- if 0 < len(args) < 2:
- logger.warn('Invalid arguments for castles annual wealth: ' \
- 'Expected at least 2 integer values: %r', args)
- if len(args) < 2:
- args = [700, 300]
- if not (0 <= options.strategy < len(STRATEGIES)):
- logger.warn('Invalid strategy number: %s', options.strategy)
- options.strategy = 0
- input = open(options.input) if options.input else sys.stdin
- output = open(options.output, 'w') if options.output else sys.stdout
- simulation = STRATEGIES[options.strategy]
- simulation.CASTLES_ANNUAL_WEALTH = map(int, args)
- return (simulation, input, output)
- def main(program, *args):
- """ Main program """
- simulation, input, output = make_simulation(*args)
- with input as input_stream:
- with output as output_stream:
- simulation.run(input_stream, output_stream)
- if __name__ == '__main__':
- main(*sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement