class Era: count_dice = Dice(max_value=4) years_dice = Dice(max_value=10) events = [] @classmethod def generate_years(cls): count = cls.count_dice.roll() return (sum(cls.years_dice.roll()) for _ in count) @classmethod def generate(cls): for year in cls.generate_years(): event = random.choice(cls.events) yield Event(year, event())
class TestDice(unittest.TestCase): def setUp(self): self.dice = Dice() def test_roll(self): for _ in range(50): result = self.dice.roll() self.assertTrue(1 <= result <= 6)
def test_too_small_die(): TEST = ( "1d0", "3d1", ) for dice_str in TEST: with pytest.raises(ValueError) as err_info: Dice(dice_str) match_str = r"Die size of [0-9]+ is less than 2." assert err_info.match(match_str)
def test_too_large_local_mod(): TEST = ( "1(d6-7)", "3(d2-2)-2H", ) for dice_str in TEST: with pytest.raises(ValueError) as err_info: Dice(dice_str) match_str = r"Local mod -[0-9]+ is larger than die size [0-9]+; all rolls would be 0!" assert err_info.match(match_str)
def __init__(self, id=0, name="", description="", ability_type=0, dice_type=20, dice_count=1, base_power=1, mana_cost=10, price=0): self.id = id self.name = name self.description = description self.ability_type = ability_type self.dice = Dice(1, dice_type) self.dice_count = dice_count self.base_power = base_power self.mana_cost = mana_cost self.price = price self.luck = -1
def test_too_few_dice(): TEST = ( "0d0", "0d1", "0dF", ) for dice_str in TEST: with pytest.raises(ValueError) as err_info: Dice(dice_str) match_str = r"Number of dice .?[0-9]+ is less than 1." assert err_info.match(match_str)
def test_too_many_drops(): TEST = ( "1d6-L", "3d6-2L-2H", "3dF-4H", ) for dice_str in TEST: with pytest.raises(ValueError) as err_info: Dice(dice_str) match_str = r"Number of dice dropped \([0-9]+\) greater than or equal to number rolled \([0-9]+\)." assert err_info.match(match_str)
def add_encounters(daily=1, nightly=1): next_roll = CONFIG.get('roll_on_double', False) dice1, dice2 = Dice(count=2).roll() print(dice1, dice2) if dice1 != dice2: next_roll = False elif dice1 % 2: nightly += 1 else: daily += 1 return add_encounters(daily, nightly) if next_roll else (daily, nightly)
def test_dice_size(): TEST_PAIRS = ( ("3d6", 6), ("4dF", "F"), ("10d7+4", 7), ("8d12-3", 12), ("4d2-2L", 2), ("23d24-5H", 24), ("7(d20+1)-L-2H", 20), ("4(dF-1)-L", "F"), ("5(d10-1)+15-3L-H", 10), ("4d2-2l", 2), ("23d24-5h", 24), ("7(d20+1)-l-2h", 20), ("5(d10-1)+15-3l-h", 10), ) for dice_str, answer in TEST_PAIRS: d = Dice(dice_str) assert d.size == answer
def test_dice_lowestmod(): TEST_PAIRS = ( ("3d6", 0), ("4dF", 0), ("10d7+4", 0), ("8d12-3", 0), ("4d2-2L", 2), ("23d24-5H", 0), ("7(d20+1)-L-2H", 1), ("4(dF-1)-L", 1), ("5(d10-1)+15-3L-H", 3), ("4d2-2l", 2), ("23d24-5h", 0), ("7(d20+1)-l-2h", 1), ("5(d10-1)+15-3l-h", 3), ) for dice_str, answer in TEST_PAIRS: d = Dice(dice_str) assert d.lowest_mod == answer
def test_dice_number(): TEST_PAIRS = ( ("3d6", 3), ("4dF", 4), ("10d7+4", 10), ("8d12-3", 8), ("4d2-2L", 4), ("23d24-5H", 23), ("7(d20+1)-L-2H", 7), ("4(dF-1)-L", 4), ("5(d10-1)+15-3L-H", 5), ("4d2-2l", 4), ("23d24-5h", 23), ("7(d20+1)-l-2h", 7), ("5(d10-1)+15-3l-h", 5), ) for dice_str, answer in TEST_PAIRS: d = Dice(dice_str) assert d.number == answer
class Modern(Era): """ These are events that have occurred within the lifespan of the last living generation; only those living under the setting’s rocks will not have some memory of these events, either as a participant, a live observer, or as a direct relation to same. Because people tend to have relatively short attention spans, and because the effects of these events may still be felt, these events have artificial prominence that the stuff of older histories do not. As a rule of thumb, each of these events should form the basis for a current adventure hook, either as stand-alone instance or as an extension of an event from a previous era. Roll 2d6 events, each spaced 1d10 years apart: :return: """ count_dice = Dice(2, 6) years_dice = Dice(max_value=10) events = [ lambda: "Война ({})".format( random.choice(( "успешная оборона от захватчиков", "успешное завоевание чужой армии", "поражение на чужих берегах", "ведется дипломатически", "продолжающаяся оборона", "продолжающееся вторжение", ))), lambda: "Бедствие ({})".format( random.choice(("пожар", "потоп", "голод", "болезнь", "землетрясение", "погодные катаклизмы"))), lambda: "Увеличение королевства {}".format( random.choice(( "через завоевания", "через завоевания", "через колонизацию", "через колонизацию", "дипломатическими средствами", "дипломатическими средствами", ))), lambda: "Религия ({})".format( random.choice(( "увеличивается религиозный раскол", "возрождение православия", "вынуждена уцйти в подполье", "приобретает светскую власть", "чистка \"неверных\"", "поглощен связаный культ", ))), lambda: "Астрологическое событие ({})".format( random.choice(( "комета", "метеоритный дождь", "метеоритный дождь", "затмение", "совмещение", "совмещение", ))), lambda: "Скандал ({})".format( random.choice(( "religious head", "religious head", "ruling family", "ruling family", "military leader", "high-level bureaucrat", ))), lambda: "Образование графства (или другого подразделения королевства)", lambda: "Ликвидация графства (или другого подразделения королевства)", lambda: "Восстание ({}) возглавляемое {}".format( random.choice(( "успешное", "продолжающееся", "продолжающееся", "продолжающееся", "подавленое", "подавленое", )), random.choice(( "крестьянством", "крестьянством", "анархистами", "анархистами", "рабами", "заключенными", )), ), lambda: "Политическая партия {}".format( random.choice(( "challenged", "challenged", "created", "reformed", "replaced", "dissolved", ))), lambda: "Культ {}".format( random.choice(( "formed", "formed", "forced underground", "forced underground", "rises to legitimacy", "rooted out", ))), lambda: "Лидер {}".format( random.choice(( "found insane", "scandalised", "heralds prosperity", "assassinated", "abdicates", "roots out injustice", ))), lambda: "Раскол между {}".format( random.choice(( "political contenders", "noble families", "noble families", "religious factions", "religious factions", "guilds", ))), lambda: "Политическая реформа {} {}".format( random.choice(( "improves", "improves", "improves", "worsens", "worsens", "worsens", )), random.choice(( "tax rates", "tax rates", "minority rights", "laws of conscription", "slavery", "system of law", )), ), lambda: "Крупные чудовища {}".format( random.choice(( "population increases", "population increases", "hunted down", "hunted down", "establishes wilderness foothold", "eradicated from setting", ))), lambda: "Экономика ({})".format( random.choice(( "tax increase", "raider activity rising", "economic boom", "recession", "trade route discovered", "guild unrest", ))), lambda: "Population {}".format( random.choice(( "boom", "boom", "decline", "divided", "divided", "whispers revolt", ))), lambda: "Создание нового {}".format( random.choice(( "food production", "manufacturing", "weaponry", "medicine", "defence", "transport/communication", ))), lambda: "Преступность {}".format( random.choice(( "rises in urban areas", "rises in urban areas", "plagues the countryside", "plagues the countryside", "is ruthlessly quashed", "prompts new laws", ))), lambda: "Этническое меньшинство {}".format( random.choice(( "seeks diplomatic sovereignty", "seeks diplomatic sovereignty", "suffers persecution", "foments sedition", "afforded special legal status", "migrates out of area", ))), ]
def build_game(): dice_roller = DiceRoller(Dice(), Dice()) payback_calculator = PaybackCalculator() game = DiceGame(dice_roller, payback_calculator) return game
def test_fate_rolls(): dice = Dice("1dF") for _ in range(100): assert -1 <= dice.roll()[0] <= 1
def test_dice_rolls_with_local_mod(): dice = Dice("1(d6+3)") # Make sure our rolls are as we expect for _ in range(100): assert 4 <= dice.roll()[0] <= 9
def test_fate_rolls_with_local_mod(): dice = Dice("1(dF+3)") for _ in range(100): assert 2 <= dice.roll()[0] <= 4
class Game: 'define the game state' state = 0 playerOne = None #Warrior(1) playerTwo = None #Goblin(1) d = Dice() def __init__(self, command): if command == "test1": print("Test One: Warrior v Goblin") self.playerOne = Warrior(1) self.playerTwo = Goblin(1) #print(self.playerTwo.act.prof) #for x in self.playerTwo.act.prof: # z = x.getAbl() # y = z.getFx() # print(y) # print(y.getName()) # print(str(y.getPow())) return def step(self): input("") r = self.d.roll(20) print("Roll is " + str(r)) if self.state == 0: abilities = self.playerOne.goRoll(r) for a in abilities: print(a) print("PlayerOne uses ability " + a.getVerb() + "!") x = a.getFx() print(x) print("PlayerOne " + x.getName() + "s for " + str(x.getPow())) self.playerTwo.addFx(x) self.state = 1 elif self.state == 1: abilities = self.playerTwo.goRoll(r) print(abilities) for a in abilities: print(a) print("PlayerTwo uses ability " + a.getVerb() + "!") x = a.getFx() print(x) print("PlayerTwo " + x.getName() + "s for " + str(x.getPow())) self.playerOne.addFx(x) self.playerTwo.takeEffect() self.playerOne.takeEffect() self.state = 0 return def run(self): while (self.playerOne.isAlive() and self.playerTwo.isAlive()): self.step() if (self.playerOne.isAlive()): "Player One Wins" elif (self.playerTwo.isAlive()): "Player Two Wins" else: "???" return
def test_dice_rolls_with_global_mod(): dice = Dice("1d6+3") # Make sure our rolls are as we expect for _ in range(100): assert 4 <= dice.roll() <= 9
def hours(self): if self.__hours is None: self.__hours = next(Dice(max_value=6).roll()) - 1 return self.__hours % self.max_time
def distance(self): if self.__distance is None: self.__distance = next(Dice(max_value=20).roll()) return self.__distance % self.max_distance + 1
def generate(cls, *fractions, distances=None): for fraction in fractions: fraction.surprised = next(Dice().roll()) < 2 distances = distances or cls.distances encounter = cls(distance_type=random.choice(distances), ) return encounter
def test_fate_rolls_with_global_mod(): dice = Dice("1dF+3") for _ in range(100): assert 2 <= dice.roll() <= 4
def test_drop_high_low(): dice = Dice("3d6-L-H") # Make sure our rolls are as we expect for _ in range(100): assert len(dice.roll()) == 1
def test_dice_rolls(): dice = Dice("1d6") # Make sure our rolls are as we expect for _ in range(100): assert 1 <= dice.roll()[0] <= 6
class Past(Era): """ These events occurred after the setting’s ancient period but before the last living generation. About two-thirds of these events should provide general support for the setting’s background, while the remaining third forms the foundation for present-day adventure hooks. Depending on the setting’s age, this period can span a really long time; I recommend rolling 4d6 events, each spaced 1d10x10 years apart, but add as many more as you feel are necessary to represent sufficiently the era’s duration: :return: """ count_dice = Dice(4, 6) years_dice = Dice(max_value=10, multiplier=10) events = [ lambda: "Открытие {}".format( random.choice(( "передового сельского хозяйства", "источника энергии", "промышленного материала", "передового производства", "драгоценного метала", "необычного вещества", ))), lambda: "Историческая личность ({})".format( random.choice(( "мудрец", "мудрец", "изобретатель", "исследователь", "художник", "советник", ))), lambda: "Герой войны ({})".format( random.choice(( "блестящий генерал", "блестящий генерал", "лидер элитного подразделения", "мастер шпионажа", "героиня боя", "средний солдат", ))), lambda: "Война ({})".format( random.choice(( "успешная оборона от захватчиков", "успешное завоевание чужой армии", "успешное завоевание чужой армии", "поражение от захватчиков", "неудавшееся вторжение", "неудавшееся вторжение", ))), lambda: "Процветание королевства благодаря {}".format( random.choice(( "территориальной экспансии", "территориальной экспансии", "территориальной экспансии", "избытку ресурсов", "избытку ресурсов", "победе над врагом", ))), lambda: "Упадок королевство из-за {}".format( random.choice(( "сокращения территорий", "сокращения территорий", "потери торговых партнеров", "потери торговых партнеров", "потери источника основного ресурса", "потери источника основного ресурса", ))), lambda: "Стихийное бедствие ({})".format( random.choice(("пожар", "потоп", "землетрясение", "метеориты", "вулканы", "ужасные ураганы"))), lambda: "Рукотворное бедствие ({})".format( random.choice(("пожар", "потоп", "голод", "мор", "загрязнение", "загрязнение"))), lambda: "Увеличение королевства {}".format( random.choice(( "через завоевания", "через завоевания", "через колонизацию", "через колонизацию", "дипломатическими средствами", "дипломатическими средствами", ))), lambda: "Религия ({})".format( random.choice(( "основана новая религия", "увеличивается религиозный раскол", "увеличивается религиозный раскол", "основан культ", "основан культ", "исчезла старая религия", ))), lambda: "Астрологическое событие ({})".format( random.choice(( "комета", "метеоритный дождь", "затмение", "затмение", "вспышка на солнце", "совмещение", ))), lambda: "Расцвет королевства", lambda: "Падение королевства", lambda: "Восстание ({}) возглавляемое {}".format( random.choice(( "успешное", "успешное", "успешное", "успешное", "подавленое", "подавленое", )), random.choice(( "армией", "крестьянством", "колониями", "анархистами", "рабами", "заключенными", )), ), lambda: "Политическая система {}".format( random.choice(( "столкнулась с испытанием", "столкнулась с испытанием", "создана", "реформирована", "изменена", "развалилась", ))), lambda: "Культ {}".format( random.choice(( "основан", "основан", "основан", "искоренен", "искоренен", "приобретает власть", ))), lambda: "Сильный лидер {}".format( random.choice(( "у власти", "у власти", "умирает по естественным причинам", "убит", "канонизирован", "отрекается от власти", ))), lambda: "Слабый лидер {}".format( random.choice(( "у власти", "умирает по естественным причинам", "убит", "убит", "насильно свергнут", "насильно свергнут", ))), lambda: "Геноцид {}".format( random.choice(( "местного расового меньшинства", "местного расового меньшинства", "местного расового меньшинства", "группы иностранцев", "группы иностранцев", "религиозной секты", ))), lambda: "Население уезжает {}".format( random.choice(( "на поиски ресурсов", "на поиски ресурсов", "за изобретением", "из-за катастрофы", "из-за репрессий", "в результате войны", ))), ]
class Ancient(Era): """ These events occurred during the setting’s distant past, typically describing the rise (and possible fall) of an ancestral race or explaining the remnants of a now-extinct culture. Like prehistory, ancient events are offered with a fair amount of speculation. However, unlike prehistory, there is more evidence of ancient culture (in the form of monuments, burial mounds and tombs, lost scrolls, et al.) to inspire scholarly debate and varied interpretation. As a result, there is usually enough doubt surrounding ancient events to prevent taking them at face value. Roll 2d6 events, each spaced 1d10x100 years apart: :return: """ count_dice = Dice(2, 6) years_dice = Dice(max_value=10, multiplier=100) events = [ lambda: "Катаклизм ({})".format( random.choice( ("технология вышла из под контроля", "технология вышла из под контроля", "магия вышла из под контроля", "вмешательство демонов", "естественная катастрофа", "космическое событие"))), lambda: "Возникновение империи", lambda: "Падение империи", lambda: "Массовая миграция {} в регион".format( random.choice(( "человеческих племен", "человеческих племен", "человеческих племен", "человеческих племен", "гуманоидов", "чудовищ", ))), lambda: "Массовый исход из региона ({})".format( random.choice(( "человеческих племен", "человеческих племен", "гуманоидов", "гуманоидов", "чудовищ", "чудовищ", ))), lambda: "Открытие {}".format( random.choice(( "передового сельского хозяйства", "источника энергии", "промышленного материала", "передового производства", "драгоценного метала", "необычного вещества", ))), lambda: "Историческая личность ({})".format( random.choice(( "волшебник", "мудрец", "пророк", "изобретатель", "художник", "раб", ))), lambda: "Война ({})".format( random.choice(( "успешная оборона от захватчиков", "успешная оборона от захватчиков", "успешное завоевание чужой армии", "успешное завоевание чужой армии", "поражение от захватчиков", "неудавшееся вторжение", ))), lambda: "Процветание империи благодаря {}".format( random.choice(( "территориальной экспансии", "территориальной экспансии", "золотому веку", "золотому веку", "возрождению", "возрождению", ))), lambda: "Упадок империи из-за {}".format( random.choice(( "сокращения территории", "сокращения территории", "имперского декаданса", "имперского декаданса", "технологической отсталости", "отсталости в военном плане", ))), lambda: "Религия ({})".format( random.choice(( "основана новая религия", "основана новая религия", "основана новая религия", "основана новая религия", "исчезла старая религия", "исчезла старая религия", ))), lambda: "Астрологическое событие ({})".format( random.choice(( "комета", "парад звезд", "парад планет", "возникновение новой звезды", "вспышка на солнце", "совмещение", ))), ]
class Prehistory(Era): """ Any event that occurred before recorded history. In most cases, this era is described in speculative terms, typically describing how the setting came to be peopled and serving as a precursory justification of the setting’s ancient period (see below). As a result, prehistory events may or may not have actually happened, but because they’re so broadly foundational and there is little evidence (or need) to dispute them, they are offered with the weight of scholarly truth or religious faith. Roll 1d4 events, each spaced 1d10x1000 years apart: :return: """ count_dice = Dice(max_value=4) years_dice = Dice(max_value=10, multiplier=1000) events = [ lambda: "Божественное очищение через {}".format( random.choice(( "потоп", "землетрясение", "извержение", "болезнь", "космическое событие", "магическую энергию", ))), lambda: "Расцвет {}".format( random.choice(( "человеческой подрасы", "человеческой подрасы", "гуманоидной расы", "гуманоидной расы", "монстров", "планарных существ", ))), lambda: "Упадок {}".format( random.choice(( "человеческой подрасы", "гуманоидной расы", "гуманоидной расы", "монстров", "монстров", "планарных существ", ))), lambda: "Прибытие {}".format( random.choice(( "новых богов", "планарных существ", "планарных существ", "человеческих племен", "гуманоидных племен", "пришельцев", ))), lambda: "Исчезновение {}".format( random.choice(( "старых богов", "старых богов", "планарных существ", "планарных существ", "популяции монстров", "могущественной смертной расы", ))), lambda: "Смертные получают знания {}".format( random.choice(( "о письме", "об основных технологиях", "об основных технологиях", "о божественном порядке", "о новых технологиях", "о псионике", ))), lambda: "Смертные были наказаны за {}".format( random.choice(( "чрезмерную гордость", "падение нравов", "поклонение ложным богам", "поклонение злым богам", "игнорирование богов", "то, что поддались греху", ))), lambda: "Смертным угрожают {}".format( random.choice(( "планарные существа", "злобные чудовища", "злобные чудовища", "неконтролируемая магическая энергия", "неконтролируемая технология", "превосходящие виды", ))), ]
class Encounter: description = None distances = [ Distance( "Нос к носу (ближний бой возможен с ходу)", Dice(1, 6), 1, True, ), Distance( "На расстоянии нескольких шагов (минимальный предел дистанционного боя без штрафов)", Dice(1, 6), 3, True, ), Distance( "Среднее (1d6 х 30 футов, на дистанции прямого выстрела)", Dice(1, 6), 30, ), Distance( "Дальнее (2d6+6 х 30 футов, вне прямой видимости)", Dice(2, 6, modifier=6), 30, ), Distance( "Сверхдальнее (2d6+12 х 30 футов, вне прямой видимости)", Dice(2, 6, modifier=12), 30, ), Distance( "На пределе видимости (10 минут бодрым шагом, вне прямой видимости)", Dice(1, 3), 1200, ), ] def __init__( self, distance_type=None, distance=None, ): self.distance_type = distance_type self.__distance = distance @property def distance(self): if self.__distance is None: self.__distance = self.distance_type.generate( ) if self.distance_type else None return self.__distance @distance.setter def distance(self, value): self.__distance = value @property def meters(self): return int(self.distance / 3) @classmethod def generate(cls, *fractions, distances=None): for fraction in fractions: fraction.surprised = next(Dice().roll()) < 2 distances = distances or cls.distances encounter = cls(distance_type=random.choice(distances), ) return encounter def __str__(self): return "Расстояние:\t{} м\n{}\n".format(self.distance, self.description)
def setUp(self): self.dice = Dice()
class Abilities(): """ there are 3 type of ability: Attack = 0, Heal = 1, General = 2. \ndice can be one of these numbers 20, 12, 10, 8, 6, 4. """ def __init__(self, id=0, name="", description="", ability_type=0, dice_type=20, dice_count=1, base_power=1, mana_cost=10, price=0): self.id = id self.name = name self.description = description self.ability_type = ability_type self.dice = Dice(1, dice_type) self.dice_count = dice_count self.base_power = base_power self.mana_cost = mana_cost self.price = price self.luck = -1 def execute(self, executor, target, dungeon, difficulty=1): result = self.calculate_luck(executor, target, difficulty) if (self.mana_cost > 0 and executor.mana > self.mana_cost) or self.mana_cost <= 0: executor.mana = executor.mana - self.mana_cost if self.ability_type == 0: damage = math.floor( (self.base_power * self.luck) / (self.dice.last * self.dice_count)) + executor.base_atk target.take_damage(damage, executor, dungeon, result) return result elif self.ability_type == 1: heal = math.floor((self.base_power * self.luck) / (self.dice.last * self.dice_count)) target.healing(heal, executor, result) return result elif self.ability_type == 2: result["Log"].append( ("#ffffff", f"{executor.name} {self.name} {target.name}")) if self.luck >= (self.dice.last * difficulty) - 4: target.locked = False result["IsLocked"] = target.locked if not target.locked: result["Log"].append(("#008000", " with success\n")) else: result["Log"].append(("#ff0000", " failed\n")) return result else: return result["Log"].append(( "#ff0000", f"you can't preform {self.name} without at least {self.mana_cost} mp" )) def calculate_luck(self, executor, target, difficulty): self.luck = 0 if self.dice_count > 1: self.luck = self.dice.roll_multiply(self.dice_count, difficulty)[1] else: self.luck = self.dice.roll(difficulty) total = self.dice.last * difficulty * self.dice_count result = { "Executor": executor.name, "Ability": self.name, "Target": target.name, "Luck": self.luck, "Total": total, "Log": [("#ffffff", f"Name: {executor.name} | "), ("#ffff00", f"Ability: {self.name}"), ("#ffffff", f" | "), ("#ff0000", f"On: {target.name}"), ("#ffffff", f" | "), ("#ffff00", f"Luck: {self.luck}/{total}\n")] } return result def __str__(self): return self.name + ": " + self.description def to_dict(self): _dict = self.name return _dict