from flask import Flask from flask_restx import Api, Resource from marshmallow import Schema, fields, validate from models import dice_model, ability_model, hp_model, character_model from mutations import Mutations import random app = Flask(__name__) app.config.SWAGGER_UI_JSONEDITOR = True app.config['SWAGGER_UI_JSONEDITOR'] = True api = Api(app, version='1.0', title='Gamma World Dice', description='Rolled Dice As A Service') dice = api.namespace('dice', description='Dice operations') ability = api.namespace('ability', description='Ability operations') hp = api.namespace('hp', description='HP operations') character = api.namespace('character', description='Character operations') dice_model = dice.model('Dice', dice_model) ability_model = ability.model('Ability', ability_model) hp_model = hp.model('HP', hp_model) character_model = character.model('Character', character_model) class DiceSchema(Schema): quantity = fields.Int(required=True, validate=validate.Range(min=1), description='The number of dice to roll') geometry = fields.Int(required=True, validate=validate.Range(min=2), description='The number of sides on each die') discard_lowest = fields.Bool(default=False, description='Drop the lowest score') dice_schema = DiceSchema() class CharacterSchema(Schema): chartype = fields.String( required=True, validate=validate.OneOf(["human", "mutant", "android", "robot"]), description='The characters type of being' ) emphasis = fields.String( required=True, validate=validate.OneOf(["physical", "mental", "random"]), description='Valid inputs: physical, mental, random' ) character_schema = CharacterSchema() @api.route('/roll/dice', methods=['POST']) class RollDice(Resource): @dice.expect(dice_model) def post(self): data = api.payload errors = dice_schema.validate(data) if errors: return errors, 400 quantity = data.get('quantity') geometry = data.get('geometry') discard_lowest = data.get('discard_lowest') if quantity is None or geometry is None: return {"message": "Required dice data not provided"}, 400 return roll_dices(quantity, geometry, discard_lowest), 200 @api.route('/roll/ability', methods=['POST']) class RollAbility(Resource): @ability.expect(ability_model) def post(self): data = api.payload chartype = data.get('chartype') if chartype == 'human': geometry = 8 else: geometry = 6 return roll_dices(4, geometry, True), 200 @api.route('/roll/hp', methods=['POST']) class RollHP(Resource): @hp.expect(hp_model) def post(self): data = api.payload chartype = data.get('chartype') conscore = data.get('conscore') if conscore is None: return {"message": "A constitution score is required"}, 400 if chartype == 'human': geometry = 8 else: geometry = 6 return roll_dices(conscore, geometry, False), 200 @api.route('/roll/tohit', methods=['GET']) class RollToHit(Resource): def get(self): return roll_dices(1, 20, False), 200 @api.route('/roll/chance', methods=['GET']) class RollChance(Resource): def get(self): return roll_dices(1, 100, False), 200 @api.route('/coinflip', methods=['GET']) class RollCoinflip(Resource): def get(self): return random.choice(['Heads', 'Tails']), 200 @api.route('/character/generate') class GenerateCharacter(Resource): @character.expect(character_model) def post(self): data = api.payload errors = character_schema.validate(data) if errors: return errors, 400 chartype = data.get('chartype') char_emphasis = data.get('emphasis') character_sheet = {} if chartype == 'human': geometry = 8 else: geometry = 6 scores = roll_ability_scores(chartype) assigned_abilities = assign_ability_scores(scores, char_emphasis) character_sheet['abilities'] = assigned_abilities character_sheet['hp'] = roll_hp(chartype, assigned_abilities['constitution']) character_sheet['gold'] = roll_dices(4, 4, False).get('result') character_sheet['domar'] = roll_dices(2, 4, False).get('result') if chartype == 'mutant': character_sheet['mutations'] = ( roll_mutations(assigned_abilities['constitution'], assigned_abilities['intelligence']) ) return character_sheet, 200 def roll_dices(dice_count, dice_sides, discard_lowest): roll_results = [] discarded = [] for _ in range(dice_count): roll_results.append(random.randint(1, dice_sides)) if discard_lowest and len(roll_results) > 0: discarded.append(min(roll_results)) roll_results.remove(min(roll_results)) if discarded: net_dice_count = dice_count - 1 mnemonic = str(dice_count) + "(-1)d" + str(dice_sides) else: mnemonic = str(dice_count) + 'd' + str(dice_sides) net_dice_count = dice_count result = { 'dice-set': { 'mnemonic': mnemonic, 'min-roll': net_dice_count, 'max-roll': dice_sides * net_dice_count }, 'rolls': roll_results, 'result': sum(roll_results) } if discarded: result['discarded'] = discarded return result def roll_ability_scores(chartype): if chartype == 'human': geometry = 8 else: geometry = 6 scores = [] for _ in range(6): scores.append(roll_dices(4, geometry, True)['result']) return scores def assign_ability_scores(scores, char_emphasis): ability_scores = {} if char_emphasis == 'physical': ability_list = ['p-strength', 'constitution', 'dexterity', 'charisma', 'intelligence', 'm-strength'] for ab in ability_list: max_score = max(scores) ability_scores[ab] = max_score scores.remove(max_score) elif char_emphasis == 'mental': ability_list = ['m-strength', 'intelligence', 'charisma', 'constitution', 'dexterity', 'p-strength'] for ab in ability_list: max_score = max(scores) ability_scores[ab] = max_score scores.remove(max_score) else: ability_list = ['p-strength', 'constitution', 'dexterity', 'charisma', 'intelligence', 'm-strength'] random.shuffle(scores) for ab, score in zip(ability_list, scores): ability_scores[ab] = score return ability_scores def roll_hp(chartype, conscore): if chartype == 'human': geometry = 8 else: geometry = 6 return roll_dices(conscore, geometry, False).get('result') def roll_mutations(conscore, intscore): """ :param conscore: modifier for physical mutation determination :param intscore: modifier for mental mutation determination :return: table of mutations (in json form) """ mutations_table = {} mental_mutations_cnt = roll_dices(1,4, False).get('result') physical_mutations_cnt = roll_dices(1, 4, False).get('result') mutations_table['count'] = {'mental': mental_mutations_cnt, 'physical': physical_mutations_cnt} mental_mutations_scores = [] physical_mutations_scores = [] for _ in range(mental_mutations_cnt): mscore = roll_dices(1, 100, False).get('result') + intscore if mscore in mental_mutations_scores: mscore = mscore-3 if mscore > 100: mscore = 100 mental_mutations_scores.append(mscore) for _ in range(physical_mutations_cnt): pscore = roll_dices(1, 100, False).get('result') + conscore if pscore in physical_mutations_scores: pscore = pscore-3 if pscore > 100: pscore = 100 physical_mutations_scores.append(pscore) mut = Mutations() mmut = [] pmut = [] for score in mental_mutations_scores: mutation = mut.get_mental_mutation(score) mmut.append(mutation) mutations_table['mental'] = mmut for score in physical_mutations_scores: mutation = mut.get_physical_mutation(score) pmut.append(mutation) mutations_table['physical'] = pmut return mutations_table if __name__ == '__main__': app.run()