Rolar dados de Dungeons and Dragons

20

Quero jogar Dungeons and Dragons, mas não tenho dados! Seu desafio é rolar alguns dados de D&D.

A especificação do formato de entrada no formato Backus-Naur é:

<valid-input>  ::= <opt-integer> "d" <integer> <opt-modifier>
<opt-integer>  ::= | <integer>
<opt-modifier> ::= | "+" <integer>
<integer>      ::= "0" | "1" | "2" | "3" | "4" | "5" |
                   "6" | "7" | "8" | "9" | <integer> <integer>

O número inteiro opcional antes de dé o número de dados a serem lançados; deve ser pelo menos 1e o padrão é 1se não for fornecido.

O número inteiro necessário imediatamente após o dé o número de lados que cada dado possui; deve ser pelo menos 1. Os lados de cada dado são números inteiros positivos consecutivos distintos começando em 1.

O modificador opcional pode ser +0e o padrão é +0se não for especificado.

Por exemplo, para a entrada 2d10+5, você gera dois números aleatórios de 1 a 10 inclusive, os soma e adiciona 5. Em seguida, você produzirá o resultado.

Se você receber uma entrada inválida, como 2d, d20+, 0d4, 2d5+1+2, 2+2, ou qualquer outra coisa que não se encaixa neste formato, você deve saída " Invalid input". Caso contrário, você deve gerar apenas um único número inteiro aleatório, ponderado de acordo com a entrada. Por exemplo, 3d6deve produzir mais 10s do que4 s .

Casos de teste

Input      Minimum possible output    Maximum possible output
d1         1                          1
d6         1                          6
d1+3       4                          4
d20+3      4                          23
2d1        2                          2
2d6+2      4                          14
d01        1                          1
d01+0      1                          1
01d01+01   2                          2
3d20+10    13                         70

d          Invalid input
d0         Invalid input
d+0        Invalid input
d0+0       Invalid input
0d1        Invalid input
0d1+1      Invalid input
d1+        Invalid input
1d         Invalid input
1d1+       Invalid input
1d+1       Invalid input
2d+2d      Invalid input
d2+d2      Invalid input
d2+2+2     Invalid input
d2-1       Invalid input
-d2        Invalid input
-2d2       Invalid input
4*3        Invalid input
4*d2       Invalid input

Isso é , então o código mais curto em bytes vencerá!

Maçaneta da porta
fonte
1
É 02d05+073uma entrada válida?
MT0
2
A parte difícil dessa questão é validar a entrada, mas o parágrafo que descreve as regras de validação é auto-contraditório. Ele descreve ne pcomo opcional, mas a entrada que escolhe não incluí-los ( d20+) como inválidos.
Peter Taylor
1
@ PeterTaylor: Eu acho que o +sinal só deve ser adicionado se o modificador pfor fornecido.
ProgramFOX
4
@ Doorknob, bem, porque d13 e d17 não são dados usados ​​em D&D. O D&D usa d4, d6, d8, d10, d12 e d20. Além disso, certamente existem casos em que um teste incluiria diferentes tipos de dados (por exemplo, 1d4+1d6para uma invasão desonesta atacar com uma adaga) ou ter um resultado negativo p(por exemplo, 1d20-1para uma verificação de habilidades sem classificações / treinamento e um modificador de habilidades negativo).
Brian S
2
Você vai jogar dnd sem o uso de 2d8 + 1d6 + 4? Você vai ter um mau tempo
corsiKa

Respostas:

12

Perl, 109 95 93 96 89 bytes

s/^d/1d/;/^(\d+)d(\d+)(\+\d+)?$/;$d+=1+rand$2|0for
1..$1;$_=$1*$2?$d+$3:'Invalid input'

Requer a -popção, que responde por dois dos bytes. Experimente online no Ideone .

Como funciona

  • Por causa da -popção, uma linha é lida em STDIN e armazenada em $_.

  • O comando s/^d/1d/anexa um 1 para $_se começar com um d , isto é, se o número de dados não tiver sido especificado.

  • A expressão regular /^(\d+)d(\d+)(\+\d+)?/verifica se a linha consiste em um número, um literal d , outro número e, opcionalmente, um terceiro número precedido por um sinal de + .

    Se houver uma correspondência, os números serão salvos em $1, $2e $3.

    Nesse caso, a entrada será válida se e somente se $1e $2forem ambas positivas.

  • $d += 1 + rand $2 | 0adiciona um número inteiro escolhido pseudo-aleatoriamente de 1 ao número especificado de lados de $d(inicialmente tratado como zero).

  • for 1 .. $1 faz o acima uma vez para cada número inteiro entre 1 e o número de dados.

  • O comando $_ = $1 * $2 ? $d + $3 : 'Invalid input'faz o seguinte:

    • Se $1 * $2for zero, ele define $_como Entrada inválida .

    • Caso contrário, a entrada é válida e é definida $_como a soma dos dados e do modificador.

  • Por causa da -popção, Perl imprime o conteúdo de $_.

  • Como não há mais linhas de entrada, o script é encerrado.

Dennis
fonte
1
Acredito que, em geral, os parâmetros extras da linha de comando são considerados valendo um byte cada, mas o hífen é livre. Nesse caso, -pcustaria apenas um, tornando esta uma solução de 108 bytes.
Undergroundmonorail
2
Podem ser feitas de 96 caracteres,/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?$/||die"Invalid input$/";$a+=1+int rand$2for(1..$1||1);$_=$a+$3
Hasturkun
1
@undergroundmonorail: já vi pessoas contando uma única opção de linha de comando como um, dois e até três (contando o espaço em branco) bytes. Prefiro contar como um, mas dois bytes me parecem justos.
Dennis
1
@ Lynce Eu suponho que você também não. Eu uso |0para converter para int, pois randretorna um float pseudo-aleatoriamente escolhido .
Dennis
1
@ Vynce Adicionei um permalink à pergunta ( ideone.com/gLJfhO ). -eseria problemático aqui, a menos que você substitua as aspas simples por aspas duplas.
Dennis
4

Fortran: 145

character(1)a;read(*,*)s,a,j,a,k;n=0;if(k<0.or.a=="-")then;print*,"error k<0";stop;endif;do l=1,int(s);n=n+int(s*rand(0)+1);enddo;print*,n+k;end;

Abusa de digitação implícita ( i-ntodos são inteiros, todo o resto é real). Advertência menor: a entrada deve ser separada por espaço, portanto 2d10+5deve ser inserida como 2 d 10 + 5, caso contrário, você receberá um input conversion error.

Kyle Kanos
fonte
4

Ruby, 116

Versão alternativa do Ruby. Eu estava tentando encontrar uma maneira de fazer isso sem as expressões regulares, mas a validação que você precisa fazer é muito mais difícil sem elas.

gets=~/^(\d+)?d(\d+)(\+\d+)?$/
a=$1||?1
puts$~&&a>?0?eval("r=#{$3||0};#{a}.times{r+=rand(#$2)+1};r"):'Invalid input'

Este é o 112, usando o inteligente algoritmo Perl de Dennis:

$p='(\d*[1-9]\d*)'
puts~/^#$p?d#$p(\+\d+)?$/?eval("r=#{$3||0};#{$1||1}.times{r+=rand(#$2)+1};r"):'Invalid input'
Paul Prestidge
fonte
@ m.buettner Obrigado! Não sei por que pensei que tinha que ser> 0.
Paul Prestidge
3

Javascipt, 158

m=prompt().match(/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?$/);if(!m)alert("Invalid input");else{for(s=+m[3]|0,i=0;i<(+m[1]||1);i++)s+=Math.random()*+m[2]+1|0;alert(s)}

Não é possível jogar golfe melhor que isso. É hora de voltar ao trabalho.

Lanche
fonte
1
s="Invalid input";if(m=prompt().match(/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?$/))for(s=m[3]|0,i=0;i<(m[1]||1);i++)s+=Math.random()*m[2]+1|0;alert(s)possui apenas 137 bytes.
7894 Dennis
2
De acordo com os comentários sobre a pergunta, esta é uma resposta incorreta porque rejeita a entrada 02d05+073.
Peter Taylor
3

GolfScript ( 120 106 bytes)

.100?!1`*\+.43?)!'+0'*+.10,'d+':^*-!*.10,''*-^=*^1/{/n*}/~].,3=*3,or:x~;*{x~\{rand)+}+@*}'Invalid input'if

Isso não é apenas mais curto que a primeira versão, mas também mais elegante. A parte que realmente faz o rolamento é:

\{rand)+}+@*

O restante é principalmente validação de entrada e alguns caracteres para análise.

# Start by converting valid inputs into valid inputs with all optional bits.
# Prepend a '1' if the string starts with 'd'.
.100?!1`*\+
# Append '+0' if there's no '+' in the string.
.43?)!'+0'*+
# Now we start knocking out the invalid inputs.
# If it contains a character other than [0-9d+], replace the string with ''.
.10,'d+':^*-!*
# If it doesn't contain exactly one 'd', exactly one '+', and the 'd' before the '+',
# replace the string with ''.
.10,''*-^=*
# Now we either have a valid string, an empty string, or a string which is almost valid
# but has some empty substrings which should be integers, or a forbidden 0 integer value.
# Replace the 'd' and '+' with newlines, eval the result, and gather into an array.
^1/{/n*}/~]
# If we had any empty parts, we'll have fewer than 3 items on the stack.
# In that case, replace with integer values which will fail the final validation step.
.,3=*3,or
# Final validation: number of dice * number of sides per die != 0.
:x~;*
# If we pass, do the actual die rolling. Otherwise give the error message.
{x~\{rand)+}+@*}'Invalid input'if

Demonstração online com estrutura de teste

Peter Taylor
fonte
Eu estou querendo saber por que você não usa n./? Talvez também 10,n*para um personagem a menos.
7274 Howard
@ Howard, para o primeiro, porque foi um truque de última hora para passar em alguns casos de teste e não pensei em jogar golfe. Para o segundo, isso aceitaria alguma entrada inválida.
Peter Taylor
2

J - 130 (45?) Char

Esse desafio parece ser um pouco tendencioso em relação a expressões regulares, principalmente por ter que diferenciar entradas inválidas. J possui uma biblioteca de expressões regulares POSIX, portanto não é tão ruim, mas não está integrada como é com Perl, portanto, J não se sai melhor do que outras linguagens.

+/@,`(1+?@#~)/`('Invalid input'"_)@.(0 e.$)0 1 1>.".>|.}.((,'?d','(\+[0-9]+)?$',~}.)'^([0-9]*[1-9][0-9]*)')(rxmatch rxfrom])1!:1]1

Se você está apenas implementando a lógica para expressões válidas, como as soluções Python / PHP parecem, são os 45 caracteres mais razoáveis:

+/,(1+[:?@#/1>.".;._2@,&'d')`".;._1'+',1!:1]1

Bits notáveis:

  • 1!:1]1é a entrada e (rxmatch rxfrom])é a lógica que retorna as correspondências da subexpressão.

  • Se a entrada é legal ou não é tratada pela correspondência de regex, para que possamos definir os padrões para n e p com 0 1 1>.. Ele olha para trás (n é 1 por padrão ep é 0) porque tivemos que inverter ( |.) a lista anteriormente, para que a lógica no final seja executada na ordem correta.

  • @.é a conjunção da Agenda , essencialmente uma declaração J-ish switch. Se os jogos estão vazias (se 0 é um e.lement dos US $ hape: 0 e.$), que emitem a mensagem de erro, o mais que passar com rolando os dados: #~para definir os dados, 1+?de rolo, e +/@,para adicionar o p modificador e soma.

algoritmshark
fonte
Isso funciona 01d01+01?
amigos estão dizendo sobre
@CeesTimmerman My bad. Faz agora.
algorithmshark
2

TinyMUSH , 239

@dig/t +
@op d=+
@lo d=d
@fail d=Invalid input
@cr .
@set .=com
&d .=$*:\ifelse(regmatch(%0,^(\\\\d+)?d(\\\\d+)(\\\\+\\\\d+)?$,0 1 2 3),ifzero(and(or(not(strlen(%q1)),%q1),%q2),Invalid input,add(die(usetrue(%q1,1),%q2),%q3)),Invalid input)

As quatro primeiras linhas tratam do fato de que "d" é um alias para a saída "inativa" universal com uma mensagem de falha interna quando ela não existe; as saídas são varridas antes dos comandos definidos pelo usuário. As linhas restantes criam um objeto com um comando definido pelo usuário, utilizando a função die () interna.

Muqo
fonte
2

PHP, 129

<?eval(preg_filter(~Сף›ÔÖÀ›×£›Ö×£Ô£›ÔÖÀÛÐ,~ÛžÂÝÛÎÝÀÅÎÄ™×ÄÛ–ÔÔÃÛžÄیԞ‘›×ÎÓÛÍÖÖÄšœ—ÛŒÛÌÄ,$_GET[0])?:~šœ—ݶ‘‰ž“–›ß–‘Š‹ÝÄ);

Usa um regex para criar uma expressão que o PHP avalia. A entrada é alimentada via url:? 0 = argumento . Certifique-se de codificar o + para% 2b. Aqui está o que parece de uma forma mais legível:

eval(preg_filter('/^(\\d)?d(\\d)(\\+\\d)?$/','$a="$1"?:1;for(;$i++<$a;$s+=rand(1,$2));echo$s$3;',$_GET[0])?:'echo"Invalid input";');

A inversão de bits em bits usando ~não apenas salva um caractere porque você não precisa de aspas (o PHP assume que são cadeias), mas também salva caracteres porque não é necessário escapar das barras invertidas na expressão regular.

O ?:operador é uma forma especial do operador ternário. $foo = $a ? $a : $bé o mesmo que $foo = $a ?: $b.

Tryth
fonte
1

Java, 378

Só queria tentar uma solução com Java longe da melhor solução. Mas ei: Java não é uma linguagem de golfe em qualquer caso!

Obtém a entrada da linha de comando. O primeiro parâmetro args[0]é o valor de entrada.

class A{public static void main(String[]s){System.out.print(s[0].matches(
"(0+\\d+|[1-9]\\d*|)d(0+\\d+|[1-9]\\d*)(\\+\\d+)?")?z(s[0]):"Invalid input");}static int
z(String s){String[]a=s.split("d");String[]b=a[1].split("\\+");int c=a[0].isEmpty()?1:Byte.
decode(a[0]);int d=b.length<2?0:Byte.decode(b[1]);while(c-->0)d+=new java.util.Random().
nextInt(Byte.decode(b[0]))+1;return d;}}

Você sabia que isso decodeé mais curto que valueOf?

bobina
fonte
1

Python 3, 184 bytes

import random,re
try:a,b,c=re.findall("^(\d*)d(\d+)(\+\d+)?$",input())[0];t=int(c or 0)+(sum(random.randint(1,int(b))for i in range(int(a or 1)))or q)
except:t="Invalid input"
print(t)

Passa em todos os testes. Se zero dado fosse permitido, seriam 6 bytes mais curtos deixando de fora (or q).

Cees Timmerman
fonte
Eu não entendi bem o BNF. Esta página ajuda.
perfil completo de Cees Timmerman
Para o benefício de qualquer pessoa que se pergunte por que o regex está ancorado em um extremo, mas não no outro: o Python re.matchancora implicitamente no início, mas não no final. Não conheço nenhuma outra biblioteca regex que faça isso.
Peter Taylor
1
Há uma pequena economia ao inicializar t=int(c or 0); e pode ser possível combinar sua resposta com a existente do Python (que usa menos espaço em branco) para economizar mais alguns.
22413 Peter Peter Taylor
0

JavaScript 134

m=prompt().match(/^((?!0)\d*)d((?!0)\d+)(\+\d+)?$/);alert(m?eval('for(o=m[3]|0,i=m[1]||1;i--;)o+=m[2]*Math.random()+1|0'):'Invalid input')
Michael M.
fonte
Isso é tão semelhante à resposta do Snack
#
Bem, existem semelhanças, essa é a mesma linguagem / algoritmo ... Mas eu pensei que há diferenças suficientes no meu código (e na regex) para postar uma resposta diferente.
267 Michael M.
De acordo com os comentários sobre a pergunta, esta é uma resposta incorreta porque rejeita a entrada 02d05+073.
27568 Peter
0

Ruby, 167 147

/^(\d+)?d(\d+)(\+\d+)?$/.match gets
abort'Invalid input'if !$~||$1==?0||!$2||$2==?0
p eval(([0]*($1||1).to_i).map{rand($2.to_i)+1}*?+)+($3||0).to_i

Usa uma regexp para fazer todo o trabalho. Desde que eu estou usando \d+, as únicas coisas que eu preciso para verificar se há uma entrada inválida são de que houve uma partida, que nem n, nem mforam 0, e que havia uma m. Se algum desses for encontrado, ele será abortado com uma mensagem ( 'Invalid input'). Em seguida, apenas imprime o resultado, pois já teria sido abortado se a entrada fosse inválida.

A impressão de resultados não é tão interessante, mas ...

([0]*($1||1).to_i)    # create an array of n elements (1 if there is no n)
.map{rand($2.to_i)+1} # fill it up with random numbers, where the number x is 1 < x < m+1
.inject(:+)           # add them all up
+($3||0).to_i         # and finally add the modifier (0 if there is none)

Mais tarde mudei .inject(:+)para eval(...*?+), mas a ideia é a mesma.

Maçaneta da porta
fonte
0

Python3, 204B

O Mine supera a resposta existente do Python adicionando o tratamento e a leitura de erros necessários, d20em 1d20vez de 0d20:)

import random,re
try:a,b,c=re.findall('^([1-9]\d*)?d(\d+)(\+\d+)?$',input())[0];I=int;R=sum(random.randrange(I(b))+1for x in[0]*(1if a==''else I(a)))+(0if c==''else I(c))
except:R='Invalid input'
print(R)

editadas para corrigir erros de digitação 2: I(x) => I(c),Invalid Input => Invalid input

editado para corrigir o regex: \+?(\d*) => (\+\d+)?

alexander-brett
fonte
De acordo com a pergunta esclarecida, esta é uma resposta incorreta porque aceita a entrada 3d20+.
27568 Peter
Bom ponto! #filler
alexander-Brett
E não 01d01+01.
amigos estão dizendo sobre