class MagicItemDistribution(object): # these are the names (and order) of the stats that all magical # items will have stats_names = ("dexterity", "constitution", "strength", "intelligence", "wisdom", "charisma") def __init__(self, bonus_probs, stats_probs, rso=np.random): """Initialize a magic item distribution parameterized by `bonus_probs` and `stats_probs`. Parameters ---------- bonus_probs: numpy array of length m The probabilities of the overall bonuses. Each index in the array corresponds to the bonus of that amount (e.g. index 0 is +0, index 1 is +1, etc.) stats_probs: numpy array of length 6 The probabilities of how the overall bonus is distributed among the different stats. `stats_probs[i]` corresponds to the probability of giving a bonus point to the ith stat, i.e. the value at `MagicItemDistribution.stats_names[i]`. rso: numpy RandomState object (default: np.random) The random number generator """ # Create the multinomial distributions we'll be using self.bonus_dist = MultinomialDistribution(bonus_probs, rso=rso) self.stats_dist = MultinomialDistribution(stats_probs, rso=rso) def sample(self): """Sample a random magical item. Returns ------- dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. """ stats = self._sample_stats() item_stats = dict(zip(self.stats_names, stats)) return item_stats def log_pmf(self, item): """Compute the log probability the given magical item. Parameters ---------- item: dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. Returns ------- float The value corresponding to log(p(item)) """ # First pull out the bonus points for each stat, in the # correct order, then pass that to _stats_log_pmf. stats = np.array([item[stat] for stat in self.stats_names]) log_pmf = self._stats_log_pmf(stats) return log_pmf def pmf(self, item): """Compute the probability the given magical item. Parameters ---------- item: dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. Returns ------- float The value corresponding to p(item) """ return np.exp(self.log_pmf(item)) def _sample_bonus(self): """Sample a value of the overall bonus. Returns ------- integer The overall bonus """ # The bonus is essentially just a sample from a multinomial # distribution with n=1; i.e., only one event occurs. sample = self.bonus_dist.sample(1) # `sample` is an array of zeros and a single one at the # location corresponding to the bonus. We want to convert this # one into the actual value of the bonus. bonus = np.argmax(sample) return bonus def _sample_stats(self): """Sample the overall bonus and how it is distributed across the different stats. Returns ------- numpy array of length 6 The number of bonus points for each stat """ # First we need to sample the overall bonus bonus = self._sample_bonus() # Then, we use a different multinomial distribution to sample # how that bonus is distributed. The bonus corresponds to the # number of events. stats = self.stats_dist.sample(bonus) return stats def _bonus_log_pmf(self, bonus): """Evaluate the log-PMF for the given bonus. Parameters ---------- bonus: integer The total bonus. Returns ------- float The value corresponding to log(p(bonus)) """ # Make sure the value that is passed in is within the # appropriate bounds if bonus < 0 or bonus >= len(self.bonus_dist.p): return -np.inf # Convert the scalar bonus value into a vector of event # occurrences x = np.zeros(len(self.bonus_dist.p)) x[bonus] = 1 return self.bonus_dist.log_pmf(x) def _stats_log_pmf(self, stats): """Evaluate the log-PMF for the given distribution of bonus points across the different stats. Parameters ---------- stats: numpy array of length 6 The distribution of bonus points across the stats Returns ------- float The value corresponding to log(p(stats)) """ # There are never any leftover bonus points, so the sum of the # stats gives us the total bonus. total_bonus = np.sum(stats) # First calculate the probability of the total bonus logp_bonus = self._bonus_log_pmf(total_bonus) # Then calculate the probability of the stats logp_stats = self.stats_dist.log_pmf(stats) # Then multiply them together (using addition, because we are # working with logs) log_pmf = logp_bonus + logp_stats return log_pmf
class MagicItemDistribution(object): # these are the names (and order) of the stats that all magical # items will have stats_names = ("dexterity", "constitution", "strength", "intelligence", "wisdom", "charisma") def __init__(self, bonus_probs, stats_probs, rso=np.random): """Initialize a magic item distribution parameterized by `bonus_probs` and `stats_probs`. Parameters ---------- bonus_probs: numpy array of length m The probabilities of the overall bonuses. Each index in the array corresponds to the bonus of that amount (e.g. index 0 is +0, index 1 is +1, etc.) stats_probs: numpy array of length 6 The probabilities of how the overall bonus is distributed among the different stats. `stats_probs[i]` corresponds to the probability of giving a bonus point to the ith stat, i.e. the value at `MagicItemDistribution.stats_names[i]`. rso: numpy RandomState object (default: np.random) The random number generator """ # Create the multinomial distributions we'll be using self.bonus_dist = MultinomialDistribution(bonus_probs, rso=rso) self.stats_dist = MultinomialDistribution(stats_probs, rso=rso) def sample(self): """Sample a random magical item. Returns ------- dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. """ stats = self._sample_stats() item_stats = dict(zip(self.stats_names, stats)) return item_stats def log_pmf(self, item): """Compute the log probability the given magical item. Parameters ---------- item: dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. Returns ------- float The value corresponding to log(p(item)) """ # First pull out the bonus points for each stat, in the # correct order, then pass that to _stats_log_pmf. stats = np.array([item[stat] for stat in self.stats_names]) log_pmf = self._stats_log_pmf(stats) return log_pmf def pmf(self, item): """Compute the probability the given magical item. Parameters ---------- item: dictionary The keys are the names of the stats, and the values are the bonus conferred to the corresponding stat. Returns ------- float The value corresponding to p(item) """ return np.exp(self.log_pmf(item)) def _sample_bonus(self): """Sample a value of the overall bonus. Returns ------- integer The overall bonus """ # The bonus is essentially just a sample from a multinomial # distribution with n=1, i.e., only one event occurs. sample = self.bonus_dist.sample(1) # `sample` is an array of zeros and a single one at the # location corresponding to the bonus. We want to convert this # one into the actual value of the bonus. bonus = np.argmax(sample) return bonus def _sample_stats(self): """Sample the overall bonus and how it is distributed across the different stats. Returns ------- numpy array of length 6 The number of bonus points for each stat """ # First we need to sample the overall bonus. bonus = self._sample_bonus() # Then, we use a different multinomial distribution to sample # how that bonus is distributed. The bonus corresponds to the # number of events. stats = self.stats_dist.sample(bonus) return stats def _bonus_log_pmf(self, bonus): """Evaluate the log-PMF for the given bonus. Parameters ---------- bonus: integer The total bonus. Returns ------- float The value corresponding to log(p(bonus)) """ # Make sure the value that is passed in is within the # appropriate bounds. if bonus < 0 or bonus >= len(self.bonus_dist.p): return -np.inf # Convert the scalar bonus value into a vector of event # occurrences x = np.zeros(len(self.bonus_dist.p)) x[bonus] = 1 return self.bonus_dist.log_pmf(x) def _stats_log_pmf(self, stats): """Evaluate the log-PMF for the given distribution of bonus points across the different stats. Parameters ---------- stats: numpy array of length 6 The distribution of bonus points across the stats Returns ------- float The value corresponding to log(p(stats)) """ # There are never any leftover bonus points, so the sum of the # stats gives us the total bonus. total_bonus = np.sum(stats) # First calculate the probability of the total bonus logp_bonus = self._bonus_log_pmf(total_bonus) # Then calculate the probability of the stats logp_stats = self.stats_dist.log_pmf(stats) # Then multiply them together (using addition, because we are # working in log-space) log_pmf = logp_bonus + logp_stats return log_pmf
class MagicItemDistribution(object): """rpg游戏中杀死怪物后掉落魔法装备的概率采样""" stats_names = ('dexterity', 'constitution', 'strength', 'intelligence', 'wisdom', 'charisma') def __init__(self, bonus_probs, stats_probs, rso=np.random): """初始化魔法装备的随机分布 :param bonus_probs: 奖励的概率 :param stats_probs: 奖励在玩家的各个属性中的分布 """ self.bonus_dist = MultinomialDistribution(bonus_probs, rso=rso) self.stats_dist = MultinomialDistribution(stats_probs, rso=rso) def _sample_bonus(self): """采样奖励""" # 只有一个事件发生 sample = self.bonus_dist.sample(1) bonus = np.argmax(sample) return bonus def _sample_stats(self): """采样所有的奖励,以及奖励在不同的属性中如何分布""" bonus = self._sample_bonus() stats = self.stats_dist.sample(bonus) return stats def sample(self): """采样获得一个随机的魔法装备""" stats = self._sample_stats() item_stats = dict(zip(self.stats_names, stats)) return item_stats def log_pmf(self, item): """计算给定的魔法装备的概率质量函数对数""" stats = np.array([item[stat] for stat in self.stats_names]) log_pmf = self._stats_log_pmf(stats) return log_pmf def pmf(self, item): """计算给定装备的概率质量函数""" return np.exp(self.log_pmf(item)) def _stats_log_pmf(self, stats): total_bonus = np.sum(stats) logp_bonus = self._bonus_log_pmf(total_bonus) logp_stats = self.stats_dist.log_pmf(stats) log_pmf = logp_bonus + logp_stats return log_pmf def _bonus_log_pmf(self, bonus): if bonus < 0 or bonus >= len(self.bonus_dist.p): return -np.inf x = np.zeros(len(self.bonus_dist.p)) x[bonus] = 1 return self.bonus_dist.log_pmf(x)