Guest Post #2 – Nathan’s Magic Missile D&D spell in Python

Paul: Today we have a guest post by my friend Nathan Dibley. It's Magic Missile code in Python for all you D&D fans!

Nathan: Hi there. This is a D&D (5th edition) Magic Missile spell in Python, cast the spell with one die, or a die for each dart, depending on your persuasion.
Why? A wizard overheard some friends shooting-the-breeze about whether Magic Missile should be cast with one D4 and the result applied to each dart, or if a D4 should be rolled for each dart and result per die rolled applied to each dart. This gave the wizard an itch to scratch and so he wrote some Python code.
For those not familiar and left utterly confused, see a description of the spell here.
Disclaimer, this is code, not magic. Unlike magic, there may be bugs and silly things lurking!
The code consists of two Python scripts. The first ( contains the main logic for the spell and the second ( is used to cast it.

You can find all the source code on GitHub here.

import random
class MagicMissile:
    def __init__(self, spell_slot_lvl, spell_mode):
            self.lvl = int(spell_slot_lvl)
            raise TypeError(“spell_slot_level should be an integer”)
        if spell_mode == “roll_die” or spell_mode == “roll_dice”:
            self.mode = spell_mode
            raise Exception(“spell_mode should be ‘roll_die’, or ‘roll_dice'”)
    def _dart_num(self):
        if self.lvl == 0:
            print(“You clearly have no magic ability,\
 and are utterly weak”)
        elif self.lvl == 1:
            return 3
            bonus = self.lvl – 1
            return (3 + bonus)
    def _attack_damage(self):
        for x in range(1):
            return random.randint(1, 4)
    def _damage_roll_die(self):
        dart_num = self._dart_num()
        base_damage = self._attack_damage()
        damage_per_dart = (base_damage + 1)
        total_damage = damage_per_dart * dart_num
        return { “darts_fired”: dart_num,
                 “base_damage”: base_damage,
                 “damage_per_dart”: damage_per_dart,
                 “total_damage”: total_damage  }
    def _damage_roll_dice(self):
        dart_num = self._dart_num()
        base_damage_per_dart = {}
        total_damage_per_dart = {}
        for dart in range(dart_num):
            damage = self._attack_damage()
            base_damage_per_dart[“dart_{}”.format(dart + 1)]\
                = (damage)
            total_damage_per_dart[“dart_{}”.format(dart + 1)]\
                = (damage + 1)
        total_damage = sum(total_damage_per_dart.values())
        return { “darts_fired”: dart_num,
                 “base_damage_by_dart”: base_damage_per_dart,
                 “total_damage_by_dart”: total_damage_per_dart,
                 “total_damage_all_darts”: total_damage }
    def cast(self):
        if self.mode == “roll_die”:
            return self._damage_roll_die()
        elif self.mode == “roll_dice”:
            return self._damage_roll_dice()

#!/bin/env python3.9
import sys
from magic_missile import MagicMissile
def usage():
     print(“Usage: “)
     print(”     spell-slot level must be an integer”)
     print(”     spell-mode either ‘roll_die’ or ‘roll_dice'”)
     print(”       roll_die, rolls a 1d4 once, and result applied to each dart”)
     print(”       roll_dice, rolls a 1d4 for each dart”)
def main(argv):
    if len(argv) == 2:
        spell_slot_lvl = argv[0]
        spell_mode = argv[1]
            mm_spell = MagicMissile(spell_slot_lvl, spell_mode)
            for item, value in mm_spell.cast().items():
                print(“{}: {}”.format(item, value))
        except Exception as e:
            print(“Error casting MagicMissile, {}”.format(e))
if __name__ == ‘__main__’:

Some examples of use

[wizard@dev magic-missile]$ ./
     spell-slot level must be an integer
     spell-mode either ‘roll_die’ or ‘roll_dice’
       roll_die, rolls a 1d4 once, and result applied to each dart
       roll_dice, rolls a 1d4 for each dart
[wizard@dev magic-missile]$
wizard@dev magic-missile]$ ./ 0 roll_die
You clearly have no magic ability, and are utterly weak
[wizard@dev magic-missile]$
[wizard@dev magic-missile]$ ./ 1 roll_die
darts_fired: 3
base_damage: 1
damage_per_dart: 2
total_damage: 6
[wizard@dev magic-missile]$
[wizard@dev magic-missile]$ ./ 3 roll_dice
darts_fired: 5
base_damage_by_dart: {‘dart_1’: 4, ‘dart_2’: 2, ‘dart_3’: 3, ‘dart_4’: 2, ‘dart_5’: 4}
total_damage_by_dart: {‘dart_1’: 5, ‘dart_2’: 3, ‘dart_3’: 4, ‘dart_4’: 3, ‘dart_5’: 5}
total_damage_all_darts: 20
[wizard@dev magic-missile]$
So there you have it. All ready for Saturday night’s D&D session!

What code have you written for fun? Let me know in the comments below. And if you need a Unix wizard, you can find Nathan here.
If you enjoyed this article, please consider signing up for my newsletter on my homepage.

Leave a comment