AnyDice Help: Resolução semelhante a ORE

5

Estou procurando ajuda para criar um programa AnyDice que possa calcular as possibilidades da mecânica a seguir.

Os jogadores lançam um número de d6 e, em seguida, passam a somar todos os dados de um único conjunto de dados correspondentes.

Assim ...

... um teste de 3, 3, 5, 5, 6 geraria uma pontuação de 10 (5 + 5).

... um teste de 1, 1, 2, 2, 4 geraria uma pontuação de 4 (2 + 2).

Se nenhum conjunto correspondente for lançado, o dado mais alto é considerado um conjunto.

Assim ...

... um teste de 1, 2, 3, 4, 5 geraria uma pontuação de 5 (5).

De preferência, o programa deve produzir [mecânico] > = [mecânico].

Obrigado.

    
por RavenStag 06.06.2015 / 02:17

5 respostas

Editado usando o conselho de Ilmari Karonen.

Você só precisa fazer com que N seja o número de dados laminados.

Use o link ou copie o código você mesmo.

function: sum all I in ROLL:s{
 result: I*( ROLL=I )
}

function: myroll ROLL:s{

 A: [sum all 1 in ROLL]

 loop I over {2..6}{
  B: [sum all I in ROLL]
  A: [highest of A and B] 
 }

 result: A
}

output [myroll 5d6]
    
08.06.2015 / 21:52

Aqui está uma solução AnyDice mais simples , originalmente baseada em Código de Albert Masclans , mas quase inteiramente reescrito:

function: highest sum of equal dice in ROLL:s {
  MAX: 0
  loop I over ROLL {
    SUM: I * (ROLL = I)
    MAX: [highest of SUM and MAX] 
  }
  result: MAX
}
output [highest sum of equal dice in 5d6]

Você pode alterar 5d6 na última linha para o que quiser. Grandes pools de dados, no entanto, provavelmente expirarão, porque o código funciona inativamente percorrendo todos os rolos possíveis (é o que acontece no AnyDice quando você passa um dado para uma função esperando uma seqüência), e isso rapidamente se torna muito lento. / p>

Para pools de dados um pouco maiores, aqui está um programa em Python que faz o mesmo cálculo:

# ORE-like dice odds calculator for https://rpg.stackexchange.com/q/63120
import argparse
parser = argparse.ArgumentParser(description='Distribution of the maximum sum of identical dice in NdD.')
parser.add_argument('N', type=int, help='number of dice to roll')
parser.add_argument('D', type=int, default=6, nargs='?', help='number of sides per die')
args = parser.parse_args()

# generate all possible sorted NdD rolls and their probabilities
# see http://en.wikipedia.org/wiki/Multinomial_distribution for the math
factorial = [1.0]
def dice_pool(n, d):
    for i in xrange(len(factorial), n+1):
        factorial.append(factorial[i-1] * i)
    nom = factorial[n] / float(d)**n
    for roll, den in _dice_pool(n, d):
        yield roll, nom / den

def _dice_pool(n, d):
    if d > 1:
        for i in xrange(0, n+1):
            pair = (d, i)
            for roll, den in _dice_pool(n-i, d-1):
                yield roll + (pair,), den * factorial[i]
    else:
        yield ((d, n),), factorial[n]

# the actual calculation and output code starts here
dist = {}
for roll, prob in dice_pool(args.N, args.D):
    total = max(num * count for num, count in roll)
    if total not in dist: dist[total] = 0.0
    dist[total] += prob

max_prob = max(dist.values())
print "Maximum sum of identical dice in %dd%d:" % (args.N, args.D)
for total, prob in dist.iteritems():
    print "%4d %7.4f%% %s" % (total, 100 * prob, "#" * int(60 * prob / max_prob + 0.5))

A função dice_pool(n, d) que compõe a maior parte do código é, na verdade, uma ferramenta de propósito geral para calcular probabilidades envolvendo conjuntos de dados não ordenados. É um gerador que retorna sucessivamente cada rolo possível (no formato ((1, n1), (2, n2), (3, n3), ...) , onde n1 , n2 , n3 , etc. são o número de dados que rolou 1, 2, 3, etc. respectivamente) e a probabilidade de obter esse rolo. (Sim, o valor obtido é uma tupla contendo uma tupla de tuplas. É perfeitamente natural de usar.)

Para velocidade, eu escolhi usar floats para os cálculos de probabilidade, apesar da possibilidade de pequenos erros de arredondamento. Seria possível obter resultados exatos usando matemática inteira / racional em todos os lugares, mas o ganho provavelmente seria mínimo, já que não deveria haver oportunidades reais para cancelamento catastrófico aqui. Há também algumas outras otimizações, como o pré-cálculo dos fatoriais de números de 0 a N. Com as otimizações, calcular as probabilidades de 20d6 demora cerca de 0,8 segundos no meu laptop antigo, enquanto 50d6 leva cerca de 47 segundos.

    
29.07.2015 / 12:39

Eu não posso fazer isso no AnyDice, mas não é tão difícil de codificar em Python ...

Em Python

# OREishOdds.py 
# ©2015 William F. Hostman. CC by attribution
# res is output results array - a place to store our output.
res = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
# num is our running total of iterations
num = 0
# work is the array we do the math work upon.
work = [0,0,0,0]
# we now tell it what the die looks like...
diedef = [1,2,3,4,5,6]
# dice: for die in [array of faces]
for a in diedef:
    for b in diedef:
        for c in diedef:
            for d in diedef:
                 # work
                 work[0] = a
                 work[1] = b
                 work[2] = c
                 work[3] = d
                 # insert additional dice above
                 work.sort()
                 # counting
                 score = 0
                 for z in diedef:
                     tempscore = 0
                     for y in work:
                         if y == z:
                             tempscore += y
                     if tempscore > score:
                         score = tempscore
                 res[score] +=1
                 num += 1
                 # end of work
# output
temp = 0
for score in res:
    temp += 1
    print temp, score, (1000*score/num)

Estendendo isso

res precisa ter pelo menos tantos valores 0 quanto 1+ (lado * número).

Cada dado recebe um valor separado, mas você pode fazer um atalho definindo um dado como uma variável e substituindo os arrays de dados individuais por instruções com aquele array definido.

O bloco work precisa ser recuado. O Python depende do espaço em branco.

Para adicionar um dado: adicione outra linha for com outra letra, adicione outro 0 ao array work , adicione outra linha work[ ] = com o próximo número sequencial e a nova variável do dado.

Você não pode usar atalhos e simplesmente carregar work diretamente nas instruções for , porque a rotina de classificação estragaria a contabilidade.

A saída está no valor do resultado, no número de ocorrências e no resultado da permilação (porcentagem * 10). Se você quiser mais lugares, aumente o multiplicador na linha print .

Ajustando os valores dos dados

Se você quiser brincar com alguns resultados funky, por exemplo, substituindo rolls de 1 por 0 (então 1's count como nothing), basta substituir os 1's nas matrizes nas linhas for. Então os arrays seriam [0,2,3,4,5,6] .

Você muda para d8 usando [1,2,3,4,5,6,7,8] .

É um pouco mais feio codificar os resultados 2-d do ORE (largura e altura), mas já que você não estava perguntando sobre isso ... ahn, heck, eu vou discutir isso também.

E isso porque qualquer teste ORE verdadeiro tem a questão se a altura de priorização de uma pessoa (valor do número contado) ou largura (número de replicações no rolo).

Se alguém prioriza um ou outro, é fácil grampear. Caso contrário, é preciso marcar os vários locais da matriz em uma grade 2d. A soma das entradas seria, portanto, maior que a contagem de iterações.

Este não é um código particularmente rápido. (O Python não é particularmente rápido, e isso não é um código otimizado.) Mas é bom o suficiente desde que você o mantenha abaixo de 8 dados ou mais, e esses dados estão todos abaixo do d12.

Acima do código © 2015 William F. Hostman. Permissão para duplicar e usar para qualquer finalidade legal concedida, incluindo a inclusão em textos, desde que a atribuição seja dada. Código levou 10 minutos para escrever. Demorou mais ou mais para comentar.

    
07.06.2015 / 00:30

Não sei bem por que a resposta de Mrlemon foi rebaixada, pois eu tinha a mesma inclinação. No meu caso, porém, eu queria usar alguns dos itens mais novos encontrados na biblioteca padrão do Python3 para tornar o código um pouco mais simples:

# OREishOdds.py
# ©2015 William F. Hostman. CC by attribution
# Python3 version (c)2017 Tim Keating

from itertools import product

d6 = list(range(1, 7))

def compute_scores(num, die):
    score_counts = {}

    all_possible_outcomes = product(*([die] * num))
    for roll in all_possible_outcomes:
        roll_score = score(roll)
        score_counts.update({roll_score: score_counts.get(roll_score, 0)+1})

    return score_counts

def score(roll):
    scores = {}
    for die in roll:
        scores[die] = scores.get(die, 0) + die

    return max(scores.values())

def all_odds(num, die):
    total_num_outcomes = len(die) ** num
    total_values = len(die) * num
    score_counts = compute_scores(num, die)
    for n in range(1, total_values + 1):
        score = score_counts.get(n, 0)
        print('{:<4}{:>6}{:>10.2%}'.format(n, score, score / total_num_outcomes))

if __name__ == '__main__':
    all_odds(5, d6)
    
11.03.2017 / 22:59
Depois de ler a longa resposta de aramis (sem ofensa), não pude deixar de jogar o python em uma função legal. Este está usando estatísticas para simplificar, em vez de realmente calcular as probabilidades, mas funciona muito bem como uma estimativa.

def orestats(X,Y=6,N=1000):
    """
    X: Number of dice rolled
    Y: Number of Sides per Die
    N: Number of repetitions for statistics
    """

    def ore(X,Y):
        # Roll XdY and return the ORE result as a list
        from random import randint
        roll = [randint(1,Y) for _ in range(X)]
        return [i*roll.count(i) for i in range(1,Y+1)]

    # Calculate the results N times and deconvolve the list.
    result = [x for y in [ore(X,Y) for _ in range(N)] for x in y] 

    # Count all occurences of every possible result
    totals = [(i,result.count(i)/N*100) for i in range(1,X*Y+1)]

    # Print into a neat pseudo-AnyDice format (result, probability, bar graph)
    print('\n'.join('{:>3}: {:>4.1f} {}'.format(x[0],x[1],'█'*int(x[1])) for x in totals))

 # Example call for 10d12, executed 10000 times
 orestats(10,12,10000)

(Demorou mais de 10 minutos também).

    
11.06.2015 / 19:07

Tags