Skip to content

Instantly share code, notes, and snippets.

@sheagcraig
Last active August 29, 2015 14:22
Show Gist options
  • Save sheagcraig/265db8b7d7508b8a3463 to your computer and use it in GitHub Desktop.
Save sheagcraig/265db8b7d7508b8a3463 to your computer and use it in GitHub Desktop.
Old Skool
#!/usr/bin/env python
"""oldskool.py
Generate character stats using old school methods.
"""
# I wanted to quickly generate some old school character stats. And I
# wanted to express each method as tersely as possible in python.
# As it turns out, this is pretty simple to do as one-liners, aside
# from the differences in whether the player is allowed to reorder the
# stats or not.
from collections import OrderedDict
from random import randint
ATTRIBUTES = ("Strength",
"Intelligence",
"Wisdom",
"Dexterity",
"Constitution",
"Charisma")
PALADIN = {"Strength": 12, "Intelligence": 9, "Wisdom": 13, "Constitution": 9,
"Charisma": 17}
RANGER = {"Strength": 13, "Intelligence": 13, "Wisdom": 14, "Constitution": 14}
ILLUSIONIST = {"Intelligence": 15, "Dexterity": 16, "Constitution": 9,
"Charisma": 17}
MONK = {"Strength": 15, "Wisdom": 15, "Dexterity": 15, "Constitution": 11}
def d6(num=1):
"""Roll a d6, num times."""
return sum([randint(1, 6) for x in xrange(num)])
def evaluate(attrs):
"""Evaluate a list of scores to see if it is really bad or not."""
# Handle (in this case) OrderedDict data:
if isinstance(attrs, dict):
scores = attrs.values()
elif len(attrs) == 6:
scores = attrs
else:
raise TypeError("Too many scores!")
# Really good.
if scores.count(18) + scores.count(17) > 2 or sum(scores) / 6 > 14:
print "%s\n%s" % (u'\U0001F63B', score_string(attrs))
# Really bad.
elif sum(scores) < 48 or max(scores) < 14 or sum(scores) / 6 < 10:
print "%s\n%s" % (u'\U0001F4A9', score_string(attrs))
# Okay.
else:
print "%s\n%s" % (u'\U0001F352', score_string(attrs))
def score_string(attrs):
"""Nicely represent scores of different types."""
if isinstance(attrs, dict):
result = ["%s: %s" % (key, attrs[key]) for key in attrs]
else:
result = [str(attr) for attr in attrs]
return "\n".join(result)
def method_zero():
"""Basic D&D / AD&D 2e Method I.
Roll 3d6 in order. Deal with it.
"Iron Man" method.
"""
return OrderedDict((attr, d6(3)) for attr in ATTRIBUTES)
def method_one():
"""AD&D 1e method I.
All scores are recorded and arranged in the order the player
desires. 4d6 are rolled, and the lowest die (or one of the lower) is
discarded.
"""
return [sum(sorted([d6() for x in xrange(4)])[1:]) for y in xrange(6)]
def method_two():
"""AD&D 1e method II.
All scores are recorded and arranged as in Method I. 3d6 are rolled
12 times and the highest 6 scores are retained.
"""
return sorted([d6(3) for x in xrange(12)])[6:]
def method_three():
"""AD&D 1e method III.
Scores rolled are according to each ability category, in order,
STRENGTH, INTELLIGENCE, WISDOM, DEXTERITY, CONSTITUTION, CHARISMA.
3d6 are rolled 6 times for each ability and the highest score in
each category is retained for that category.
"""
return OrderedDict((attr, max((d6(3) for x in xrange(6)))) for attr in
ATTRIBUTES)
def method_four():
"""AD&D 1e method IV.
3d6 are rolled sufficient times to generate the 6 ability scores, in
order, for 12 characters. The player then selects the single set of
scores which he or she finds most desirable and these scores are
noted on the character record sheet.
"""
return [method_zero() for x in xrange(12)]
def rolls_for_a_class(c_class, method):
"""Return num of rolls it takes for stats elibible for paladinhood"""
is_eligible = False
ctr = 1
while not is_eligible:
is_eligible = meets_minimums(method(), c_class)
ctr += 1
return ctr
def percentage_of_class(c_class, method, trials=1000000):
return [meets_minimums(method(), c_class) for _ in
xrange(trials)].count(True) / float(trials) * 100
def meets_minimums(stats, required):
"""Return bool whether a set of stats meet minimums."""
result = True
# Stats rolled are in order.
if isinstance(stats, dict) and isinstance(required, dict):
for stat in required:
if stats[stat] < required[stat]:
result = False
break
# Stats are unordered.
elif isinstance(required, dict):
sorted_requirements = sorted(required.values())
sorted_requirements.reverse()
sorted_stats = sorted(stats)
for requirement in sorted_requirements:
if sorted_stats.pop() < requirement:
result = False
break
else:
raise AttributeError("Incorrect required type.")
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment