Git Product home page Git Product logo

alldice's Introduction

Alldice is a RESTful service along with a NSQ service that allows you to query it with dice rolling expressions (currently only scheme) and it will compute the roll and then return the result.

Syntax

| ---------- | --------------------------- | ----------------- |
| Syntax     | Explaination                | Example           |
| ---------- | --------------------------- | ----------------- |
| (dice n)   | Roll a n side dice (1 .. n) | (dice 6)          |
| (roll n d) | Roll n dices                | (roll 2 (dice 6)) |
| (+ d y)    | Add y to the dice roll      | (+ (dice 6) 2)    |
| ---------- | --------------------------- | ----------------- |

Limitations

There are currently no firm limitation implemented as it is. This software is probably quite unsecure so use it at your own risk! Eventually I would like to implement controllable limits such as:

  1. Max of N dice rolls in one expression
  2. Max amount of memory consumed in one expression
  3. Max amount of cpu consumed in one expression

Configuration

There is currently no real configuration available so the rough setup is:

  1. Logging stream to StdErr
  2. Ekg instance running at localhost:8081
  3. RESTful Dice API at 0.0.0.0:8080
  4. NSQ queue reader at: lethalcode.com on the alldice queue.

RESTful Dice API

http://localhost:8080?src="(dice 6)"

NSQ API

Request:

{
	"expression": "(dice 6)",
	"replyQueue": "foobar"
}

Reply:

{
	"expression": "(dice 6)",
	"result": "4",
	"error": ""
}

Future features

  1. Extended scheme buildins to support more fancy dice types such as: dice-pool types; sum, exploding (mxn), "open roll", "Collective tests", Additive dice pool, target number dice pool, highest-die dice pools
  2. Additional arthmetic such as; min, max, least x, largest x
  3. Probabilistic programming/rolls in which it computes the probability of the particular dice expression.
  4. Implement other dice system such as PCGen, Roll20, and so on.

Reference

  1. http://www.diku.dk/hjemmesider/ansatte/torbenm/Troll/quickRef.html
  2. http://www.diku.dk/hjemmesider/ansatte/torbenm/Troll/manual.pdf
  3. http://anydice.com/
  4. http://divnull.com/rollplay/
  5. http://semistable.com/dicelab/dicelab_manual.pdf
  6. https://github.com/neilslater/games_dice
  7. http://rpg.stackexchange.com/questions/20107/common-dice-mechanics
  8. http://rpg.stackexchange.com/questions/15971/is-it-possible-to-produce-a-bowl-shaped-probability-curve-with-dice-rolls
  9. http://rpg.stackexchange.com/questions/7131/systems-with-a-dice-mechanic-that-handles-very-easy-and-very-difficult-tasks
  10. http://www.darkshire.net/jhkim/rpg/systemdesign/

alldice's People

Contributors

pharaun avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

ashburn2k

alldice's Issues

Separate the internal interpreter state from the lisp environment state.

Right now I am injecting the random generator into the lisp environment, this relays on the interpreter to make sure it does not get trampled upon and special cases here and there, so I think its better to extract this out of the LispEnv into its own Env.

Also I'm wanting to add in 2 more environment items;

  1. Execution logging for debugging any execution issues, it would be neat to get a full traceback of the previous execution.
  2. Dice roll logging so that we can log what a roll was for and so forth.

Build system

Right now the build system is a bit of a mess.

Should be able to use: browserify to do stuff with npm and avoid bower.
should be able to migrate off grunt to say hakyll/shake to stay with haskell for build system.

Basically what I want to be able to do at this point is:

  1. Require cabal for installing+building up all of the haskell dependency
  2. Use npm to handle all of the js/node/etc deps (like libsass, etc...)
  3. Use something like shake to build up the entire site.

One of the main gotchas is probably going to be static deps like "libsass/sassc", so would be good if we can find a haskell wrapper (http://hackage.haskell.org/package/hsass)

Improve parsing.

12:11:10 <@timzilla> !dice 1d5
12:11:11 < sonOfDemislave> timzilla: your roll:[1d5] result:[1], description:[Scheme Dice Roll]

Improving the LispVal/LispError code

Comment/suggestion on improving the type for LispVal/LispError:
    < augur> i dont know what you're doing with it tho
    < pharaun> oh
    < pharaun> data LispError = NumArgs Integer [Fix LispVal]
    < augur> why?
    < pharaun> instead of [LispVal s] its [Fix LispVal] so i remove the state
    < augur> you need state in it tho
    < pharaun> I flatten it
    < pharaun> into a tree
    < augur> eh.. you risk producing infinite trees that way
    < pharaun> well its running now
    < pharaun> but I can probably improve part of it
    < pharaun> augur: yah, right now the flattening out part terminates early after a few loop
    < augur> you should have some way to indicate you've stopped
    < pharaun> I'll add that, good idea
    < augur> it ought to be possible to error on a cyclic list, for instance
    < augur> so you need something like..
    < augur> data LispVal a = ... | Cycle
    < pharaun> ooh
    < pharaun> didn't think of that
    < pharaun> >_<
    < augur> and then Fix LispVal will put Cycle in place of the cyclic references
    < augur> but you have to traverse the tree left-to-right
    < pharaun> in the flatter i just terminated it with (String "<Terminated>")
    < augur> because not all refs are cyclic
    < augur> sometimes you have weird cross-references which arent obviously cyclic but which do lead to cyclicity
    < augur> consider for instance a list analogous to this in haskell:   let xs = (a, ys) ; ys = (b, xs) in (xs,ys)
    < augur> its cyclic, but in a weird way -- the references go sideways, not obviously vertically
    < augur> tho you could treat it like its just cyclic, you could also just block at the top ys
    < augur> it depends on how much repetition you want, really, pharaun
    < pharaun> augur: yeah right now i just terminate after a few step but yeah
    < augur> pharaun: if you just treat it cyclically, you should print   ((a,(b,...)),(b,(a,...)))
    < pharaun> that would be much better
    < augur> but you could also just print   ((a,(b,...)),...)
    < augur> pharaun: if you're really clever, you might even want to print something like
    < augur> ( loop0: (a,(b,loop0)), loop1: (b,(a,loop1)) )
    < augur> pharaun: that wouldnt be too hard, i dont think
    < augur> pharaun: you'd have to do something like have an aux function that returns not just the printed val, but a list of refs that it
     cut short due to looping, and then as you receive the returned list, you check if its the current ref, and if so you mark it
    < augur> pharaun: this might be a good reason to have a custom type for flattened trees
    < augur> pharaun: something like LispVal but recursive, and with extra constructors for cycle handling
    < augur> pharaun: then you can write a function flatten :: LispVal s -> FlatVal
    < augur> pharaun: in general its good practice to use data types to express intention :)
    < augur> honestly i dont think you should parameterize LispVal at all, but
    < pharaun> augur: don't i need to because of ST, cuz of LispEnv?
    < augur> im not sure really. the purpose of the param is to sneak in the param of ST/STRef
    < augur> pharaun: you might just be able to do LispVal s = ...  | STRef s (LispVal s)

Convert the dice to stop returning Integer and return a Typeclass/probability instead.

Right now it is returning a Integer in the range of 1 and N as defined by randInt.

Now here's the idea. we want to also be able to compute the probability of a dice roll right. Replace the Integer returned by randInt with a type-class that implements the majority of what a Integer can do.

Then create a "probability" implementation of these typeclasses for probability, so that for ex (randProb 6) would emit a probability of (1/6, 1/6, 1/6 for 1-6) then each time its summed or what so ever, it will compute the new probability.

The main sticky point is how to deal with branching point, such as IF, may just want to make sure the program execute all possible branches upto some reasonable depth limits and terminate.

Also another issue is how to deal with for example raw integer, IE +6. One solution could be to have these shift the probability around from say 1-6 to 7-12 for ex.

I am not sure how scalable this solution is, the main issue would be having to head down each branch and determine their probability.... and the issue of termination. But I suspect we can just have it terminate past a certain number of iteration down a branch or have it be bounded by a timeout.

The other approach would be to do a monte-carlo approach and just repeat it thousands of time to build up the probability of the result.

Improve error reporting.

Right now its kinda of a pain to debug because error reporting is quite bad.

Invest some solid time into improving this, such as annotating the AST with various metadata such as line and col number, what expression, any type issues, so forth..

Implement more in depth limits so that the roller can't be DoS'd

Right now there's not much limits other than the fact that the entire interpreter is "pure" plus the addition of ST for the LispEnv.

Need at least the following:

  1. Reasonable upper bound on dice rolls
  2. Reasonable upper bound on number of rolls
  3. Reasonable upper bound on memory consumption
  4. Reasonable upper bound on execution time (probably via kernel limits and forking)

PCGEN dice roller.

PCGEN dice roller rules:

PCGEN RollInfo Dice:

ndm([/\|mM]a)*([+-]b){0,1}([tT]c){0,2}

    n - dices
    m - sides
    /a - keep top a dices
    \a - keep bottom a dices
    ma - Reroll if below a.
    Ma - Reroll if above a.
    |a,b,c - keep a,b,c dice (1-indexed sorted ascending)
    +b - Add b
    -b - Minus b
    tc - Min of c total.
    Tc - Max of c total.

Examples:
    d6
        One roll of a d6 dice

    2d6
        Two roll of a d6 dice summed

    4d6/3
        Four roll of a d6, keep highest 3

    4d6\3
        Four roll of a d6, keep lowest 3

    5d6|1,4
        Five roll of d6, keep roll 1, 4 (1-indexed sorted ascending)

    2d6m2
        Two roll of a d6, min of 2

    2d6M2
        Two roll of a d6, max of 2

    d6+3
        One roll of a d6 dice + 3

    10d6t10
        10 roll of d6 dice, with a min of 10 total.

    10d6T20
        10 roll of d6 with a max of 20 total.


PCGEN Data Dice:
min(v1,v2)
max(v1,v2)
pow(base, exponent)
roll(times, sides)
roll(times, sides, [keep])
roll(times, [sides])
roll(times, [sides], [keep])
roll(times, sides, numToKeep, reroll, modifier)

NOTES:
    [keep] - This is an array of 0-indexed dices to keep in the result. The rolls are generated
        and stored in an array, then they are sorted in ascending order (lowest to highest), then the
        values in [keep] are used as index to pull out the dice rolls to keep.

    [sides] - (doc has it as shape), This is an array of dice sides such as for d6 - [1, 2, 3, 4, 5, 6] in which
        the side is randomly selected out of this array and used as a dice-roll.

    d% - 1d100

    dF - fudge dice (1-2 -, 3-4 0, 5-6 +)

    numToKeep - (highest x dices to keep)

    reroll - roll(sides - reroll) + reroll; - Badly done (if not greater than x, reroll)

    modifier - the sum to add to the dice (ie 3d6 + modifier)


Examples:
    roll("3d6")
        Simulates rolling 3 six-sided dice.

    roll("1d20+10")
        Simulates rolling a twenty-sided die and adds 10.

    roll("min(4,7)")
        Returns a value of "4".

    roll("max(4,7)")
        Returns a value of "7".

    roll("pow(10,2)")
        Returns a value of "100".

    roll("roll(3,6)")
        Simulates rolling 3 six-sided dice.

    roll("roll(4,6,[2,3,4])")
        Simulates rolling 4 six-sided dice and keeps the 3 highest rolls.

    roll("roll(1,[3,5,7,9])")
        Similates rolling one 3, 5, 7 or 9 sided die, randomly selected.

    roll("roll(3,[2,3,4,5,6],[2,3])")
        Simulates rolling three 2, 3, 4, 5, or 6 sided dice, randomly selected and keeps the highest 2.

    roll("roll(4, 6, top(3), reroll(1))")
        Simulates rolling four 6 sided dices and picking top 3 and rerolling if not at least 1.

Example rolls:

roll(")
roll("10+d10")
roll("1d10")
roll("1d20+10")
roll("max(4,7)")
roll("min(4,7)")
roll("pow(10,2)")
roll("roll(1,[3,5,7,9])")
roll("roll(3,6)")
roll("roll(3,[2,3,4,5,6],[2,3])")
roll("roll(4,6,[2,3,4])")
roll("x")
roll()
roll(1,20,|VAR|,'text\n')
roll(4,5,[2,3,4])
roll(4,6,reroll(1))
roll(4,6,top(3))
roll(4,6,top(3),reroll(1))

Citation:

Use quasi-quoting on the stdlib for scheme.

This is so that we can validate/test the stdlib code quality before generating it to the filesystem. Or alternatively just compile it directly to AST and store it in the binary.

Performance is *ok* but it could be improved.

There has been absolutely no performance tuning, it is performing ok for probably moderate load, but past that...

It would be awesome to start to build up a test suite of benchmarks for various stage of the interpreter, web service and so on to start nailing down performance issues.

For example - wrk was used to slam the http endpoint and it performed ok but it had high amount of connection dropped under heavy load which seems sub-optimal.

Implement and provide NSQ bindings.

Would be pretty neat to also provide a NSQ api endpoint for apps to send in dice requests so if they already have a NSQ bindings they can just send one instead of also having to set up a http client.

Implement SubDiceParser for alternative dice syntax.

Should provide a simple way to parse alternative dice syntax such as roll20.net, pcgen and so forth.

Basically it would take an input string, parse then emit an equivalent instruction but in the scheme AST/Raw Scheme and feed it into the primary dice engine so that we can have a clean scheme core that is extendable with other syntax form.

I would suspect one such example uri routing could be like:

/roll/{subparser}/etc....

Then the person who is interacting with the http api can probably just issue an common call to various sub-parser such as pcgen, roll20, troll, etc... and put the dice expression into the src="" field and have it parsed and transformed into an scheme ast for execution.

Improve the public web-api for rolling dices

Would be good to provide more flexible options such as requesting a log of rolls.

  1. log of rolls
  2. log of execution
  3. debugging facilities such as amount of memory consumed/time consumed and limits.

Look into yesod static asset ingestion.

See if its possible to do some sort of ingest of static asset upon build and upon deploy have the binary have a way to "dump" a default version of the asset locally for local modification for stuff like say the scheme stdlib, the html, couple other assets for quick test/modify cycle, but mostly in deploy mode.

Might be better to just do it as production vs test mode, where test mode just ingest local files, and production makes everything build first (ie shake) then ingest the file for deploy.

Random generator test suite

Should implement a test suite for testing and ensuring that the random generator is of the highest possible quality.

Improve the EKG (stats) instrumenting of the system.

We have basic EKG instrument of GHC/runtime but we don't have any for the performance of the interpreter, the system, requests/sec and so on. It would be extremely awesome to add these kind of stats to EKG so that we can keep a eye on the service's performance.

Build a list of various odd and ends for dice rolls from various reference materials.

Basically what good is a dice generator if it can't do all sorts of odd rolls.

Look up in various source books and finding what types of rolls are out there and implement support for it.
1.Roll a m sided dice (d20)
2. Roll multiple (n) dices of (m) sides (2d6)
3. add n
4. subtract n
5. best n
6. worst n
7. exploding dice (ie if you roll max dice, you roll additional dice till you stop rolling max dices)
8. Additive vs success roll (Ie 2d6 = d6 + d6), and success where (success = count_true([x > 3 for x in [d6, d6, d6]) > 2)

Logging.

Right now there is absolutely no logging outside of stdout messages that are generated from yesod-routes/warp itself. We should absolutely extend it to include all other useful information such as program submitted, amount of time taken to execute programs and so on.

Eventually start working on a JIT/LLVM target.

I've found some good source doc for code gen via LLVM and having a form of primitive JIT. It would be pretty cool to look into code generation so that we can execute custom programs even faster.

Probably can set up a form of caching in that if a incoming program has been seen previously (say md5sum or something) re-execute the cached compiled code so that there's only an initial cost upon the first request.

Add command line options (or build options) for dealing with production versus deployment

Right now I am baking in hsass, shake, and a process control library for dealing with running elm-reactor + building the entire system in a self-contained manner.

Would be good to make these bits optional (via cabal dep + build flag) or as an command line argument for ex. Then that way we can have a "dev" version and a "deployment" version which builds the entire thing and ingest all assets into the binary.

Eventually provide a database of "dice programs"

Basically right now we are hard-coding some primtives and a form of standard library in a file that we load into the interpreter.

It would be pretty awesome to eventually look into a form of database in which we can store programs in the database and then call up these programs to composite larger program. This way that people can develop custom or weird dice programs that other people can then invoke.

Almost like moving (define func) into a database in which each row is a function or some such....

Rebuild the AST, the current one is suboptimal.

Right now the parsing stage has to be ran inside the ST monad because of the fact that it parses into a stateful AST.

The parsing step and various steps does not need to be LispEnv stateful.

Look into breaking up the AST into a few phases or looking into some form of tagging or alternative AST so that we can tag various pieces of useful information to the AST.

I can envision wanting to tag the following items:

  1. Line/source code (for traceback)
  2. LispEnv state for closures
  3. Annotation for various interpreter stages.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.