Lanzar dados de mazmorras y dragones

20

Quiero jugar Dungeons and Dragons, ¡pero no tengo ningún dado! Su desafío es tirar algunos dados D&D.

La especificación del formato de entrada en formato Backus-Naur es:

<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>

El número entero opcional antes del des el número de dados a tirar; debe ser al menos 1, y su valor predeterminado es 1si no se proporciona.

El número entero requerido inmediatamente después de des el número de lados que tiene cada dado; debe ser al menos 1. Los lados de cada dado son enteros positivos consecutivos distintos que comienzan en 1.

El modificador opcional puede ser +0, y su valor predeterminado es +0si no se especifica.

Por ejemplo, para la entrada 2d10+5, genera dos números aleatorios del 1 al 10 inclusive, los suma y suma 5. Luego, generará el resultado.

Si recibe una entrada no válida, como por ejemplo 2d, d20+, 0d4, 2d5+1+2, 2+2, o cualquier otra cosa que no encaja en este formato, debe de salida " Invalid input". De lo contrario, debe generar solo un número entero aleatorio, ponderado de acuerdo con la entrada. Por ejemplo, 3d6debería producir más 10s que 4s .

Casos de prueba

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

Este es el , ¡así que el código más corto en bytes ganará!

Pomo de la puerta
fuente
1
¿Es 02d05+073una entrada válida?
MT0
2
La parte difícil de esta pregunta es validar la entrada, pero el párrafo que describe las reglas de validación es contradictorio. Describe ny pcomo opcional, pero entrada que elige no incluirlos ( d20+) como no válidos.
Peter Taylor
1
@PeterTaylor: Creo que el +signo solo debe agregarse si pse proporciona el modificador .
ProgramFOX
44
@Doorknob, Bueno, porque d13 y d17 no se usan dados en D&D. D&D usa d4, d6, d8, d10, d12 y d20. Además, ciertamente hay casos en los que una tirada incluiría diferentes tipos de dados (por ejemplo, 1d4+1d6para un pícaro que ataca con una daga) o que tiene un negativo p(por ejemplo, 1d20-1para una verificación de habilidad sin rangos / entrenamiento y un modificador de habilidad negativo).
Brian S
2
Vas a jugar dnd sin el uso de 2d8 + 1d6 + 4? Vas a pasar un mal momento
corsiKa

Respuestas:

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'

Requiere el -pconmutador, que representa dos de los bytes. Pruébelo en línea en Ideone .

Cómo funciona

  • Debido al -pcambio, se lee una línea de STDIN y se almacena en ella $_.

  • El comando s/^d/1d/antepone un 1 a $_si comienza con una d , es decir, si no se ha especificado el número de dados.

  • La expresión regular /^(\d+)d(\d+)(\+\d+)?/verifica si la línea consiste en un número, un literal d , otro número y, opcionalmente, un tercer número precedido por un signo + .

    Si hay una coincidencia, los números se guardarán en $1, $2y $3.

    En este caso, la entrada será válida si y solo si $1y $2son ambos positivos.

  • $d += 1 + rand $2 | 0agrega un número entero pseudoaleatoriamente elegido de 1 al número especificado de lados a $d(inicialmente tratado como cero).

  • for 1 .. $1 hace lo anterior una vez por cada entero entre 1 y el número de dados.

  • El comando $_ = $1 * $2 ? $d + $3 : 'Invalid input'hace lo siguiente:

    • Si $1 * $2es cero, se establece $_como entrada inválida .

    • De lo contrario, la entrada es válida y se establece $_en la suma de los dados y el modificador.

  • Debido al -pcambio, Perl imprime el contenido de $_.

  • Como no hay más líneas de entrada, el script sale.

Dennis
fuente
1
Creo que, en general, se considera que los parámetros adicionales de la línea de comando valen un byte cada uno, pero el guión es libre. En este caso, -psolo le costaría uno, lo que lo convierte en una solución de 108 bytes.
undergroundmonorail
2
Se pueden hacer 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: He visto personas que cuentan un solo interruptor de línea de comandos como uno, dos e incluso tres bytes (contando el espacio en blanco). Prefiero contarlo como uno, pero dos bytes me parecen justos.
Dennis
1
@Vynce Supongo que tú tampoco. Yo uso |0para convertir a int, ya que randdevuelve un flotante elegido al azar .
Dennis
1
@Vynce He agregado un enlace permanente a la pregunta ( ideone.com/gLJfhO ). -esería problemático aquí, a menos que reemplace las comillas simples con comillas dobles.
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 la escritura implícita ( i-ntodos son enteros, todo lo demás es real). Advertencia menor: la entrada debe estar separada por espacios, por lo que 2d10+5debe ingresarse como 2 d 10 + 5, de lo contrario obtendrá un input conversion error.

Kyle Kanos
fuente
4

Ruby, 116

Versión alternativa de Ruby. Estaba tratando de encontrar una manera de hacerlo sin las expresiones regulares, pero la validación que tienes que hacer es mucho más difícil sin ellas.

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 es 112, usando el 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
fuente
@ m.buettner Gracias! No sé por qué pensé que tenía 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)}

No puedo jugar al golf mejor que esto. Es hora de volver al trabajo.

Bocadillo
fuente
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)tiene solo 137 bytes.
Dennis
2
Según los comentarios sobre la pregunta, esta es una respuesta incorrecta porque rechaza la 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

Esto no solo es más corto que la primera versión, sino también más elegante. La parte que realmente tira el dado es

\{rand)+}+@*

El resto es principalmente validación de entrada y algunos caracteres para el análisis.

# 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

Demostración en línea con marco de prueba

Peter Taylor
fuente
Me pregunto por qué no usas n./? Quizás también 10,n*para un personaje menos.
Howard
@Howard, al principio, porque fue un truco de último minuto para pasar algunos casos de prueba y no pensé en jugarlo. Al segundo, eso haría que aceptara alguna entrada inválida.
Peter Taylor
2

J - 130 (45?) Char

Este desafío parece estar un poco sesgado hacia las expresiones regulares, especialmente al tener que diferenciar entradas no válidas. J tiene una biblioteca de expresiones regulares POSIX, por lo que no está tan mal, pero no está integrada como lo está con Perl, por lo que a J no le va mejor que a otros idiomas.

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

Si solo está implementando la lógica para expresiones válidas, como parecen ser las soluciones Python / PHP, son los 45 caracteres más razonables:

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

Bits notables:

  • 1!:1]1 es la entrada y (rxmatch rxfrom]) es la lógica que devuelve las coincidencias de subexpresión.

  • La coincidencia de expresiones regulares maneja si la entrada fue legal o no, por lo que podemos establecer los valores predeterminados para n y p con 0 1 1>. . Se ve hacia atrás (n es 1 por defecto y p es 0) porque tuvimos que invertir ( |.) la lista anteriormente, para que la lógica al final se ejecute en el orden correcto.

  • @.es la conjunción de Agenda , esencialmente una declaración de cambio J-ish. Si las coincidencias están vacías (si 0 es un elemento electrónico del $ hape:) 0 e.$, emitimos el mensaje de error, de lo contrario, pasamos a tirar los dados: #~para colocar los dados, 1+?tirar y +/@,agregar el modificador p y suma.

Algoritmo de tiburón
fuente
¿Funciona eso 01d01+01?
Cees Timmerman
@CeesTimmerman My bad. Lo hace ahora
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)

Las primeras cuatro líneas tratan el hecho de que "d" es un alias para la salida universal "inactiva" con un mensaje de falla incorporado cuando no existe; las salidas se analizan antes de los comandos definidos por el usuario. Las líneas restantes crean un objeto con un comando definido por el usuario que hace uso de la función incorporada die ().

Muqo
fuente
2

PHP, 129

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

Utiliza una expresión regular para crear una expresión que PHP luego evalúa. La entrada se introduce a través de url:? 0 = argumento . Asegúrese de urlencode el + a% 2b. Así es como se ve en una forma más legible:

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";');

La inversión bit a bit de las cadenas usando ~no solo guarda un carácter porque no necesita comillas (PHP asume que son cadenas) sino que también guarda caracteres porque no tiene que escapar de las barras diagonales inversas en la expresión regular.

El ?:operador es una forma especial del operador ternario. $foo = $a ? $a : $bes el mismo que $foo = $a ?: $b.

Tryth
fuente
1

Java, 378

Solo quería probar una solución con Java lejos de ser la mejor solución. Pero bueno: ¡Java no es un lenguaje de golf en ningún caso!

Obtiene la entrada de la línea de comando. El primer parámetro args[0]es el 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;}}

¿Sabías que decodees más corto que valueOf?

bobbel
fuente
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)

Pasa todas las pruebas. Si se permitieran cero dados, sería 6 bytes más corto al omitirlos (or q).

Cees Timmerman
fuente
Sin embargo, entendí mal el BNF. Esta página te ayuda.
Cees Timmerman
Para el beneficio de cualquier otra persona que se pregunte por qué la expresión regular está anclada en un extremo pero no en el otro: Python se re.matchancla implícitamente al principio pero no al final. No conozco ninguna otra biblioteca de expresiones regulares que haga eso.
Peter Taylor
1
Hay un pequeño ahorro al inicializar t=int(c or 0); y podría ser posible combinar su respuesta con la existente de Python (que usa menos espacios en blanco) para ahorrar un par más.
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.
fuente
Esto es muy similar a la respuesta de Snack
usuario12205
Bueno, hay similitudes, este es el mismo lenguaje / algoritmo ... Pero pensé que hay suficientes diferencias en mi código (y la expresión regular) para publicar una respuesta diferente.
Michael M.
Según los comentarios sobre la pregunta, esta es una respuesta incorrecta porque rechaza la entrada 02d05+073.
Peter Taylor
0

Rubí, 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

Utiliza una expresión regular para hacer todo el trabajo. Como estoy usando \d+, lo único que necesito para verificar si hay entradas no válidas es que hubo una coincidencia, que nni lo mhubo 0, y que hubo una m. Si se encuentra alguno de esos, aborta con un mensaje ( 'Invalid input'). Luego solo imprime el resultado, ya que ya habría abortado si la entrada no fuera válida.

La impresión de resultados no es tan interesante, pero ...

([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)

Más tarde cambié .inject(:+)a eval(...*?+), pero la idea es la misma.

Pomo de la puerta
fuente
0

Python3, 204B

Mine supera la respuesta de Python existente al agregar el manejo de errores requerido y la lectura d20en 1d20lugar 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)

editado para fijar 2 errores tipográficos: I(x) => I(c),Invalid Input => Invalid input

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

alexander-brett
fuente
Según la pregunta aclarada, esta es una respuesta incorrecta porque acepta la entrada 3d20+.
Peter Taylor
¡Buen punto! #filler
alexander-brett
Y no 01d01+01.
Cees Timmerman