from flask import Flask from flask_restx import Api, Resource, fields from marshmallow import Schema, fields, validate from models import dice_model, ability_model, hp_model 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') dice_model = dice.model('Dice', dice_model) ability_model = ability.model('Ability', ability_model) hp_model = hp.model('HP', hp_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() @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/chance', methods=['GET']) class RollChance(Resource): def get(self): return roll_dices(1, 100, False), 200 @api.route('/roll/tohit', methods=['GET']) class RollToHit(Resource): def get(self): return roll_dices(1, 20, False), 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 if __name__ == '__main__': app.run()