Giter VIP home page Giter VIP logo

slack-poker-bot's Introduction

Slack Poker Bot Build Status

A bot that turns Slack into a legitimate Texas Hold'em client. Start a game in any channel or private group with 2-10 players. PokerBot will deal hands, direct message players with their hole cards, query players for their action, determine the winning hand, and handle the pot.

See it in action.

Getting Started

  1. Create a new bot integration
  2. Follow the steps to deploy the bot to Heroku or run it locally
  3. Once the bot is running, start a game with: @<your-bot-name>: deal

One-Click Heroku

Click this button:

Deploy

Manual Heroku

  1. Install Heroku toolbelt
  2. Create a new bot integration (as above)
  3. heroku create
  4. heroku config:set SLACK_POKER_BOT_TOKEN=[Your API token]
  5. git push heroku master

To Run Locally

  1. Create a token.txt file and paste your API token there
  2. npm install
  3. node src/main.js

Directions

  • To start a game, @<your-bot-name>: deal.
  • To end a game, @<your-bot-name>: quit game. The game will end once the current hand finishes. Note that any player can end a game at any time with this command, so Be Honorable™.
  • To configure some bot options, @<your-bot-name>: config <name-of-option>=<value>. The supported options are:
timeout: Sets the duration (in seconds) before a player times out. 
Set to 0 to specify no timeout.

So, to start a game without any player timeout, say:

@<your-bot-name>: config timeout=0
@<your-bot-name>: deal

Check the open issues for some planned enhancements.

AI Players

Although this client was built for managing human players in a Slack channel, it has some support for AI players. To add a bot player:

  1. Add a bot class under the ai/ folder
  2. Implement getAction, which is called whenever it is the bot's turn
  3. Modify the addBotPlayers method in src/bot.js to add your bot to every game

Test All The Things

To run tests, simply do:

  1. gulp

The tests produce legible output that matches what users in Slack would see. This is the same test suite that is run on each pull request. This is very helpful when diagnosing a logic bug:

Dependencies

  • NodeJS Slack Client node-slack-client abstracts the basics of a Slack bot, including authentication, getting messages from players, and posting messages or attachments to the channel.

  • RxJS The majority of this client is written using RxJS. It simplifies many of the complex player polling interactions, that would otherwise be Death By Timers, into very legible code.

  • Imgur / Lightweight Image Processor Each card is a separate image, and board images are created on the fly by pasting several cards onto a single canvas (with the help of lwip). The resulting image is than uploaded to imgur, which gives us a single URL that can be passed as an attachment to the Slack API. This route was chosen to avoid uploading 318,505,200 images to the cloud, and allows us to modify the card assets easily.

  • Poker Evaluator poker-evaluator is used for evaluating the winning hand when it comes time to show down. Here it has been extended to calculate the best 5-card hand from any 7-card hand.

  • MochaJS Most of the tricky client logic is backed up by tests, which were written using MochaJS.

  • Vector Playing Cards

slack-poker-bot's People

Contributors

charliehess avatar douglascalhoun avatar gangstead avatar hubdotcom avatar mcmillan avatar peterkinmond avatar stephenyeargin avatar walmsley avatar wgpsutherland avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slack-poker-bot's Issues

Unable to start the bot due to error

Here's the output of running node src/main.js after npm install

➜  slack-poker git:(master) ✗ node src/main.js

module.js:340
    throw err;
          ^
Error: Cannot find module 'babel-core/register'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/home/ben/apps/slack-poker/node_modules/babel/register.js:1:80)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)

Started a game with two people and got an error

This happened as soon as it said "let's start the game"

/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:579
    throw e;
          ^
TypeError: Cannot read property 'send' of undefined
    at TexasHoldem.dealPlayerCards (/Users/nwalke/projects/slack-poker-bot/src/texas-holdem.js:503:11)
    at TexasHoldem.playHand (/Users/nwalke/projects/slack-poker-bot/src/texas-holdem.js:116:10)
    at /Users/nwalke/projects/slack-poker-bot/src/texas-holdem.js:70:27
    at /Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:4924:20
    at tryCatcher (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:567:29)
    at InnerObserver.onNext (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:4806:43)
    at InnerObserver.tryCatcher (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:567:29)
    at AutoDetachObserverPrototype.next (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:5274:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:1710:35)
    at scheduleItem (/Users/nwalke/projects/slack-poker-bot/node_modules/rx/dist/rx.js:2961:16)

Why my code won't escape the time out?

the operation of .where .map .where. does not work! In your PlayerInteraction.js line 63 for interaction
//mine
var userAction = rx.Observable.fromEvent(room.getEmitter(),'userOperation',function(data){ console.log(data);return data;});
//playerAction.subscribe(x=>console.log(x));
var playerAction = userAction.where(function(e) {return e.user == player.uid;})
.map(function(e) {return that.actionFromMessage(e, availableActions);} )
.where(function(action) {return action != null;}).publish();
playerAction.connect();

// yours
let playerAction = messages.where(e => e.user === player.id)
.map(e => PlayerInteraction.actionFromMessage(e.text, availableActions))
.where(action => action !== null)
.publish();

playerAction.connect();

@stephenyeargin @gangstead @minalecs @CharlieHess Please help!

[Feature] Provide feedback when Slack token is wrong

Lost quite a bit of time debugging while trying to update our bot today. If you provide a bogus (read: somebody regenerated it) SLACK_POKER_BOT_TOKEN, the error output isn't terribly descriptive as to why the application refuses to start.

Ideally, a sanity check would be done on initialization to verify that the token can actually connect to the API. If not, the logs should contain a descriptive "Invalid token, please check your credentials." message instead of Uncaught, unspecified "error" event.

2015-12-02T18:20:00.718608+00:00 heroku[web.1]: Starting process with command `npm start`
2015-12-02T18:20:03.643019+00:00 app[web.1]: 
2015-12-02T18:20:03.643031+00:00 app[web.1]: > [email protected] start /app
2015-12-02T18:20:03.643032+00:00 app[web.1]: > node src/main.js
2015-12-02T18:20:03.643033+00:00 app[web.1]: 
2015-12-02T18:20:09.833272+00:00 app[web.1]: events.js:87
2015-12-02T18:20:09.833276+00:00 app[web.1]:       throw Error('Uncaught, unspecified "error" event.');
2015-12-02T18:20:09.833277+00:00 app[web.1]:             ^
2015-12-02T18:20:09.833279+00:00 app[web.1]: Error: Uncaught, unspecified "error" event.
2015-12-02T18:20:09.833279+00:00 app[web.1]:     at Error (native)
2015-12-02T18:20:09.833280+00:00 app[web.1]:     at Client.emit (events.js:87:13)
2015-12-02T18:20:09.833282+00:00 app[web.1]:     at Client._onLogin (/app/node_modules/slack-client/src/client.js:74:14)
2015-12-02T18:20:09.833283+00:00 app[web.1]:     at /app/node_modules/slack-client/src/client.js:2:59
2015-12-02T18:20:09.833284+00:00 app[web.1]:     at IncomingMessage.<anonymous> (/app/node_modules/slack-client/src/client.js:670:22)
2015-12-02T18:20:09.833284+00:00 app[web.1]:     at IncomingMessage.emit (events.js:129:20)
2015-12-02T18:20:09.718519+00:00 app[web.1]: [Wed Dec 02 2015 18:20:09 GMT+0000 (UTC)] INFO Connecting...
2015-12-02T18:20:09.866927+00:00 app[web.1]: npm ERR! Linux 3.13.0-66-generic
2015-12-02T18:20:09.867429+00:00 app[web.1]: npm ERR! argv "/app/.heroku/node/bin/node" "/app/.heroku/node/bin/npm" "start"
2015-12-02T18:20:09.867728+00:00 app[web.1]: npm ERR! node v0.12.7
2015-12-02T18:20:09.833285+00:00 app[web.1]:     at _stream_readable.js:908:16
2015-12-02T18:20:09.833286+00:00 app[web.1]:     at process._tickCallback (node.js:355:11)
2015-12-02T18:20:09.862903+00:00 app[web.1]: 
2015-12-02T18:20:09.873029+00:00 app[web.1]: npm ERR! Please include the following file with any support request:
2015-12-02T18:20:09.873184+00:00 app[web.1]: npm ERR!     /app/npm-debug.log
2015-12-02T18:20:09.868218+00:00 app[web.1]: npm ERR! npm  v2.11.3
2015-12-02T18:20:09.868462+00:00 app[web.1]: npm ERR! code ELIFECYCLE
2015-12-02T18:20:09.868676+00:00 app[web.1]: npm ERR! [email protected] start: `node src/main.js`
2015-12-02T18:20:09.868843+00:00 app[web.1]: npm ERR! Exit status 1
2015-12-02T18:20:09.869045+00:00 app[web.1]: npm ERR! 
2015-12-02T18:20:09.869227+00:00 app[web.1]: npm ERR! Failed at the [email protected] start script 'node src/main.js'.
2015-12-02T18:20:09.869432+00:00 app[web.1]: npm ERR! This is most likely a problem with the slack-poker-bot package,
2015-12-02T18:20:09.869598+00:00 app[web.1]: npm ERR! not with npm itself.
2015-12-02T18:20:09.869768+00:00 app[web.1]: npm ERR! Tell the author that this fails on your system:
2015-12-02T18:20:09.869941+00:00 app[web.1]: npm ERR!     node src/main.js
2015-12-02T18:20:09.870104+00:00 app[web.1]: npm ERR! You can get their info via:
2015-12-02T18:20:09.870271+00:00 app[web.1]: npm ERR!     npm owner ls slack-poker-bot
2015-12-02T18:20:09.870438+00:00 app[web.1]: npm ERR! There is likely additional logging output above.

Card images

The image of the cards does not match the cards symbols sent by the bot:
capture d ecran 2015-09-07 a 14 33 33

Crashed when failed to create board images

Creating board image timed out
Creating board image timed out
Creating board image timed out

/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:579
    throw e;
          ^
TypeError: Cannot call method 'toString' of undefined
    at /media/james.hartig/slack-poker-bot/src/hand-evaluator.js:24:54
    at Array.map (native)
    at Function.evaluateHands (/media/james.hartig/slack-poker-bot/src/hand-evaluator.js:24:37)
    at PotManager.endHandWithShowdown (/media/james.hartig/slack-poker-bot/src/pot-manager.js:184:34)
    at AnonymousObserver._onNext (/media/james.hartig/slack-poker-bot/src/texas-holdem.js:450:27)
    at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:1778:12)
    at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:1710:35)
    at AnonymousObserver.tryCatcher (/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:567:29)
    at AutoDetachObserverPrototype.next (/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:5274:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/media/james.hartig/slack-poker-bot/node_modules/rx/dist/rx.js:1710:35)```

Cannot read property 'send' of undefined

Got the bot working. Started a game. Getting this now:

[Tue Sep 01 2015 21:38:13 GMT+0000 (UTC)] INFO Connecting...
Welcome to Slack. You are johnny of Giant Swarm
You are in: general
Your open DM's: xxxx
/poker/node_modules/rx/dist/rx.js:579
    throw e;
          ^
TypeError: Cannot read property 'send' of undefined
    at TexasHoldem.dealPlayerCards (/poker/src/texas-holdem.js:503:11)
    at TexasHoldem.playHand (/poker/src/texas-holdem.js:116:10)
    at /poker/src/texas-holdem.js:70:27
    at /poker/node_modules/rx/dist/rx.js:4924:20
    at tryCatcher (/poker/node_modules/rx/dist/rx.js:567:29)
    at InnerObserver.onNext (/poker/node_modules/rx/dist/rx.js:4806:43)
    at InnerObserver.tryCatcher (/poker/node_modules/rx/dist/rx.js:567:29)
    at AutoDetachObserverPrototype.next (/poker/node_modules/rx/dist/rx.js:5274:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/poker/node_modules/rx/dist/rx.js:1710:35)
    at scheduleItem (/poker/node_modules/rx/dist/rx.js:2961:16)

Crashes trying to create DM channels for new game

Deployed to heroku. On first match (and every subsequent match), app crashed before game begun.

Getting DM channel for jeremy
Getting DM channel for eric
No open channel found, opening one using U0P8WHNMP
/app/node_modules/rx/dist/rx.js:579
    throw e;
          ^
TypeError: undefined is not a function
    at _loop (/app/src/texas-holdem.js:491:34)
    at TexasHoldem.dealPlayerCards (/app/src/texas-holdem.js:484:45)
    at TexasHoldem.playHand (/app/src/texas-holdem.js:103:10)
    at /app/src/texas-holdem.js:56:27
    at /app/node_modules/rx/dist/rx.js:4924:20
    at tryCatcher (/app/node_modules/rx/dist/rx.js:567:29)
    at InnerObserver.onNext (/app/node_modules/rx/dist/rx.js:4806:43)
    at InnerObserver.tryCatcher (/app/node_modules/rx/dist/rx.js:567:29)
    at AutoDetachObserverPrototype.next (/app/node_modules/rx/dist/rx.js:5274:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/app/node_modules/rx/dist/rx.js:1710:35)

npm ERR! Linux 3.13.0-85-generic
npm ERR! argv "/app/.heroku/node/bin/node" "/app/.heroku/node/bin/npm" "start"
npm ERR! node v0.12.7
npm ERR! npm  v2.11.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] start: `node src/main.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script 'node src/main.js'.
npm ERR! This is most likely a problem with the slack-poker-bot package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node src/main.js
npm ERR! You can get their info via:
npm ERR!     npm owner ls slack-poker-bot
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /app/npm-debug.log

Cannot find module 'babel/register'

I've been trying to build a Dockerfile based deployment of this for the past hour and can't get past this error when running. I'm using the latest version of NodeJS for the server. I've also tried to do a:

npm cache clean
npm install -g babel

Node Version(s) Supported?

I'm using Node 0.12.2 (on Ubuntu 14.04) and when I try to npm install, I get an error like I've never seen before:

❯❯❯ npm install

> [email protected] install /home/propervillain/poker/node_modules/slack-client/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

make: Entering directory `/home/propervillain/poker/node_modules/slack-client/node_modules/ws/build'
  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
make: Leaving directory `/home/propervillain/poker/node_modules/slack-client/node_modules/ws/build'
npm WARN optional dep failed, continuing [email protected]
[1]    3076 killed     npm install

Heroku app sleeping

Hi,

I used the "lazy" method of deploying to Heroku and the app appears as "sleeping" on my Personal Heroku app list. I've put the token and I configured a BOT on Slack but the bot appears to be offline and I don't really know how to start it. Any tips?

Thanks!

Use :hearts:, etc. emoticons for your DM'd hand.

Slack supports the use of ♥️, ♠️, ♦️ and ♣️ emoticons. Looking at the code, it looks like a simple parse and lookup of the card (eg. 3h) would net an output that looked a bit more familiar:

3 of ♥️

If I have a bit of time, I may attempt a PR. Disclaimer: I'm a Python guy. :)

Cash game structure

Right now the game is structured in tournament form; each player starts with $200 and blinds are fixed at $1/2. It would be cool to have the option for a cash game, where players can leave at any time or re-buy.

Provide command to modify game-join timeout

While there's a command for the player action timeout, there doesn't appear to be a command to modify the countdown for new players to join a new game. The default is 30 seconds

 // Returns an {Observable} that will `onNext` for each player that joins and
  // `onCompleted` when time expires or the max number of players join.
  static pollPotentialPlayers(messages, channel, scheduler=rx.Scheduler.timeout, timeout=30, maxPlayers=10) {
    let formatMessage = t => `Who wants to play? Respond with 'yes' in this channel in the next ${t} seconds.`;
    let timeExpired = PlayerInteraction.postMessageWithTimeout(channel, formatMessage, scheduler, timeout);

Instruction issues

Hi, you may want to add "git add -A" and "git commit -m 'xxx'" to your instructions. It may not be clear to some.

Thank you!
Great repo!

Only deals 1 card

After several rounds of playing the pokerbot deals select users only one card instead of two.

Unable to complete `npm install` due node-gyp issue on OSX El Capitan

Attaching log

npm WARN deprecated [email protected]: This package is no longer maintained. See its readme for upgrade details.

> [email protected] install /path/to/Documents/github-repos/slack-poker-bot_local/node_modules/lwip
> node-gyp rebuild

  CXX(target) Release/obj.target/lwip_decoder/src/decoder/init.o
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:13:
../node_modules/nan/nan.h:324:27: error: redefinition of 'NanEnsureHandleOrPersistent'
  NAN_INLINE v8::Local<T> NanEnsureHandleOrPersistent(const v8::Local<T> &val) {
                          ^
../node_modules/nan/nan.h:319:17: note: previous definition is here
  v8::Handle<T> NanEnsureHandleOrPersistent(const v8::Handle<T> &val) {
                ^
../node_modules/nan/nan.h:344:27: error: redefinition of 'NanEnsureLocal'
  NAN_INLINE v8::Local<T> NanEnsureLocal(const v8::Handle<T> &val) {
                          ^
../node_modules/nan/nan.h:334:27: note: previous definition is here
  NAN_INLINE v8::Local<T> NanEnsureLocal(const v8::Local<T> &val) {
                          ^
../node_modules/nan/nan.h:757:13: error: no member named 'smalloc' in namespace 'node'
    , node::smalloc::FreeCallback callback
      ~~~~~~^
../node_modules/nan/nan.h:768:12: error: no matching function for call to 'New'
    return node::Buffer::New(v8::Isolate::GetCurrent(), data, size);
           ^~~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/node_buffer.h:31:40: note: candidate function not viable:
      no known conversion from 'uint32_t' (aka 'unsigned int') to 'enum encoding' for 3rd argument
NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate,
                                       ^
/path/to/.node-gyp/4.2.1/include/node/node_buffer.h:43:40: note: candidate function not viable:
      2nd argument ('const char *') would lose const qualifier
NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate,
                                       ^
/path/to/.node-gyp/4.2.1/include/node/node_buffer.h:28:40: note: candidate function not viable:
      requires 2 arguments, but 3 were provided
NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate, size_t length);
                                       ^
/path/to/.node-gyp/4.2.1/include/node/node_buffer.h:36:40: note: candidate function not viable:
      requires 5 arguments, but 3 were provided
NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate,
                                       ^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:13:
../node_modules/nan/nan.h:772:12: error: no viable conversion from 'v8::MaybeLocal<v8::Object>' to
      'v8::Local<v8::Object>'
    return node::Buffer::New(v8::Isolate::GetCurrent(), size);
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/v8.h:210:7: note: candidate constructor
      (the implicit copy constructor) not viable: no known conversion from
      'v8::MaybeLocal<v8::Object>' to 'const v8::Local<v8::Object> &' for 1st argument
class Local {
      ^
/path/to/.node-gyp/4.2.1/include/node/v8.h:210:7: note: candidate constructor
      (the implicit move constructor) not viable: no known conversion from
      'v8::MaybeLocal<v8::Object>' to 'v8::Local<v8::Object> &&' for 1st argument
class Local {
      ^
/path/to/.node-gyp/4.2.1/include/node/v8.h:214:13: note: candidate template ignored: could not
      match 'Local' against 'MaybeLocal'
  V8_INLINE Local(Local<S> that)
            ^
/path/to/.node-gyp/4.2.1/include/node/v8.h:326:13: note: candidate template ignored: could not
      match 'S *' against 'v8::MaybeLocal<v8::Object>'
  V8_INLINE Local(S* that)
            ^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:13:
../node_modules/nan/nan.h:779:26: error: no member named 'Use' in namespace 'node::Buffer'
    return node::Buffer::Use(v8::Isolate::GetCurrent(), data, size);
           ~~~~~~~~~~~~~~^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:11:
In file included from /path/to/.node-gyp/4.2.1/include/node/node.h:42:
/path/to/.node-gyp/4.2.1/include/node/v8.h:221:5: error: assigning to 'v8::Primitive *volatile'
      from incompatible type 'v8::Value *'
    TYPE_CHECK(T, S);
    ^~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/v8.h:180:37: note: expanded from macro 'TYPE_CHECK'
    *(static_cast<T* volatile*>(0)) = static_cast<S*>(0);      \
                                    ^ ~~~~~~~~~~~~~~~~~~
../node_modules/nan/nan.h:501:12: note: in instantiation of function template specialization
      'v8::Local<v8::Primitive>::Local<v8::Value>' requested here
    return NanEscapeScope(NanNew(v8::Undefined(v8::Isolate::GetCurrent())));
           ^
../node_modules/nan/nan.h:483:30: note: expanded from macro 'NanEscapeScope'
# define NanEscapeScope(val) scope.Escape(Nan::imp::NanEnsureLocal(val))
                             ^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:11:
In file included from /path/to/.node-gyp/4.2.1/include/node/node.h:42:
/path/to/.node-gyp/4.2.1/include/node/v8.h:221:5: error: assigning to 'v8::Boolean *volatile'
      from incompatible type 'v8::Value *'
    TYPE_CHECK(T, S);
    ^~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/v8.h:180:37: note: expanded from macro 'TYPE_CHECK'
    *(static_cast<T* volatile*>(0)) = static_cast<S*>(0);      \
                                    ^ ~~~~~~~~~~~~~~~~~~
../node_modules/nan/nan.h:511:12: note: in instantiation of function template specialization
      'v8::Local<v8::Boolean>::Local<v8::Value>' requested here
    return NanEscapeScope(NanNew(v8::True(v8::Isolate::GetCurrent())));
           ^
../node_modules/nan/nan.h:483:30: note: expanded from macro 'NanEscapeScope'
# define NanEscapeScope(val) scope.Escape(Nan::imp::NanEnsureLocal(val))
                             ^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:11:
In file included from /path/to/.node-gyp/4.2.1/include/node/node.h:42:
/path/to/.node-gyp/4.2.1/include/node/v8.h:221:5: error: assigning to 'v8::Function *volatile'
      from incompatible type 'v8::Value *'
    TYPE_CHECK(T, S);
    ^~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/v8.h:180:37: note: expanded from macro 'TYPE_CHECK'
    *(static_cast<T* volatile*>(0)) = static_cast<S*>(0);      \
                                    ^ ~~~~~~~~~~~~~~~~~~
../node_modules/nan/nan.h:1645:12: note: in instantiation of function template specialization
      'v8::Local<v8::Function>::Local<v8::Value>' requested here
    return NanEscapeScope(NanNew(handle)->Get(kCallbackIndex)
           ^
../node_modules/nan/nan.h:483:30: note: expanded from macro 'NanEscapeScope'
# define NanEscapeScope(val) scope.Escape(Nan::imp::NanEnsureLocal(val))
                             ^
In file included from ../src/decoder/init.cpp:1:
In file included from ../src/decoder/decoder.h:11:
In file included from /path/to/.node-gyp/4.2.1/include/node/node.h:42:
/path/to/.node-gyp/4.2.1/include/node/v8.h:221:5: error: assigning to 'v8::Object *volatile'
      from incompatible type 'v8::Value *'
    TYPE_CHECK(T, S);
    ^~~~~~~~~~~~~~~~
/path/to/.node-gyp/4.2.1/include/node/v8.h:180:37: note: expanded from macro 'TYPE_CHECK'
    *(static_cast<T* volatile*>(0)) = static_cast<S*>(0);      \
                                    ^ ~~~~~~~~~~~~~~~~~~
../node_modules/nan/nan.h:1776:12: note: in instantiation of function template specialization
      'v8::Local<v8::Object>::Local<v8::Value>' requested here
    return NanEscapeScope(
           ^
../node_modules/nan/nan.h:483:30: note: expanded from macro 'NanEscapeScope'
# define NanEscapeScope(val) scope.Escape(Nan::imp::NanEnsureLocal(val))
                             ^
10 errors generated.
make: *** [Release/obj.target/lwip_decoder/src/decoder/init.o] Error 1
gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:270:23)
gyp ERR! stack     at emitTwo (events.js:87:13)
gyp ERR! stack     at ChildProcess.emit (events.js:172:7)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
gyp ERR! System Darwin 15.0.0
gyp ERR! command "/usr/local/Cellar/node/4.2.1/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /path/to/Documents/github-repos/slack-poker-bot_local/node_modules/lwip
gyp ERR! node -v v4.2.1
gyp ERR! node-gyp -v v3.0.3
gyp ERR! not ok 
npm ERR! Darwin 15.0.0
npm ERR! argv "/usr/local/Cellar/node/4.2.1/bin/node" "/usr/local/bin/npm" "install"
npm ERR! node v4.2.1
npm ERR! npm  v2.14.7
npm ERR! code ELIFECYCLE

npm ERR! [email protected] install: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] install script 'node-gyp rebuild'.
npm ERR! This is most likely a problem with the lwip package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node-gyp rebuild
npm ERR! You can get their info via:
npm ERR!     npm owner ls lwip
npm ERR! There is likely additional logging output above.

Anyone else getting this on El Capitan? Any thing wrong with my OSX?

Thanks
Cheers

Add support for sitting out / leaving a game

People should be able to stop the game. It shouldn't require restarting the app to stop the game.

Also, people should be able to leave the game, so that they don't hold up the game when they don't respond.

Configurable timeouts

Would you be interested in a pull request that makes the polling timeouts configurable via some file? My usecase being: I'd like to play this with buddies at work asynchronously.

Add a Dockerfile

To further support deployability it might be nice to have a Dockerfile and perhaps an automated build through the docker registry.

How do I play the game?

Hello.

I've deployed this bot to Heroku.
So, I wanna play the game. But I can't it. Why??

Log
Me: "@PokerBot: deal"
PokerBot: "Who wants to play? Respond with 'yes' in this channel in the next N seconds. "
Me: "yes"
PokerBot: "xxxxxx has joined the game"
My Friend: "yes"
PokerBot: "xxxxxx has joined the game"
Me: "bet 8"
Me: "@PokerBot: bet 8"

Remove uncalled bets from the pot

Right now when a player bets, it is added to the pot immediately. If all players fold, the winning player gets their bet back in the pot, but the player wins $x message is misleading, since the amount they won should not include the uncalled bet. E.g.,

Heroku builds failing, apparently because of lwip failure

I tried to deploy the poker bot to heroku (using both the one-click and then the manual CLI options) and both deploys failed with the following error:

       > [email protected] install /tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/node_modules/lwip
       > node-gyp rebuild

       make: Entering directory `/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/node_modules/lwip/build'
         CXX(target) Release/obj.target/lwip_decoder/src/decoder/init.o
       In file included from ../src/decoder/decoder.h:13:0,
                        from ../src/decoder/init.cpp:1:
       ../node_modules/nan/nan.h:324:27: error: redefinition of ‘template<class T> v8::Local<T> Nan::imp::NanEnsureHandleOrPersistent(const v8::Local<T>&)’
          NAN_INLINE v8::Local<T> NanEnsureHandleOrPersistent(const v8::Local<T> &val) {
                                  ^
       ../node_modules/nan/nan.h:319:17: error: ‘template<class T> v8::Handle<T> Nan::imp::NanEnsureHandleOrPersistent(v8::Handle<T>&)’ previously declared here
          v8::Handle<T> NanEnsureHandleOrPersistent(const v8::Handle<T> &val) {
                        ^
       ../node_modules/nan/nan.h:344:27: error: redefinition of ‘template<class T> v8::Local<T> Nan::imp::NanEnsureLocal(v8::Handle<T>&)’
          NAN_INLINE v8::Local<T> NanEnsureLocal(const v8::Handle<T> &val) {
                                  ^
       ../node_modules/nan/nan.h:334:27: error: ‘template<class T> v8::Local<T> Nan::imp::NanEnsureLocal(const v8::Local<T>&)’ previously declared here
          NAN_INLINE v8::Local<T> NanEnsureLocal(const v8::Local<T> &val) {
                                  ^
       ../node_modules/nan/nan.h:757:13: error: ‘node::smalloc’ has not been declared
            , node::smalloc::FreeCallback callback
                    ^
       ../node_modules/nan/nan.h:757:35: error: expected ‘,’ or ‘...’ before ‘callback’
            , node::smalloc::FreeCallback callback
                                          ^
       ../node_modules/nan/nan.h: In function ‘v8::Local<v8::Object> NanNewBufferHandle(char*, size_t, int)’:
       ../node_modules/nan/nan.h:761:50: error: ‘callback’ was not declared in this scope
                v8::Isolate::GetCurrent(), data, length, callback, hint);
                                                         ^
       ../node_modules/nan/nan.h:761:60: error: ‘hint’ was not declared in this scope
                v8::Isolate::GetCurrent(), data, length, callback, hint);
                                                                   ^
       ../node_modules/nan/nan.h: In function ‘v8::Local<v8::Object> NanNewBufferHandle(const char*, uint32_t)’:
       ../node_modules/nan/nan.h:768:67: error: call of overloaded ‘New(v8::Isolate*, const char*&, uint32_t&)’ is ambiguous
            return node::Buffer::New(v8::Isolate::GetCurrent(), data, size);
                                                                          ^
       ../node_modules/nan/nan.h:768:67: note: candidates are:
       In file included from ../src/decoder/decoder.h:12:0,
                        from ../src/decoder/init.cpp:1:
       /app/.node-gyp/4.1.1/include/node/node_buffer.h:31:40: note: v8::MaybeLocal<v8::Object> node::Buffer::New(v8::Isolate*, v8::Local<v8::String>, node::encoding) <near match>
        NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate,
                                               ^
       /app/.node-gyp/4.1.1/include/node/node_buffer.h:31:40: note:   no known conversion for argument 3 from ‘uint32_t {aka unsigned int}’ to ‘node::encoding’
       /app/.node-gyp/4.1.1/include/node/node_buffer.h:43:40: note: v8::MaybeLocal<v8::Object> node::Buffer::New(v8::Isolate*, char*, size_t) <near match>
        NODE_EXTERN v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate,
                                               ^
       /app/.node-gyp/4.1.1/include/node/node_buffer.h:43:40: note:   no known conversion for argument 2 from ‘const char*’ to ‘char*’
       In file included from ../src/decoder/decoder.h:13:0,
                        from ../src/decoder/init.cpp:1:
       ../node_modules/nan/nan.h: In function ‘v8::Local<v8::Object> NanNewBufferHandle(uint32_t)’:
       ../node_modules/nan/nan.h:772:61: error: could not convert ‘node::Buffer::New(v8::Isolate::GetCurrent(), ((size_t)size))’ from ‘v8::MaybeLocal<v8::Object>’ to ‘v8::Local<v8::Object>’
            return node::Buffer::New(v8::Isolate::GetCurrent(), size);
                                                                    ^
       ../node_modules/nan/nan.h: In function ‘v8::Local<v8::Object> NanBufferUse(char*, uint32_t)’:
       ../node_modules/nan/nan.h:779:12: error: ‘Use’ is not a member of ‘node::Buffer’
            return node::Buffer::Use(v8::Isolate::GetCurrent(), data, size);
                   ^
       make: *** [Release/obj.target/lwip_decoder/src/decoder/init.o] Error 1
       make: Leaving directory `/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/node_modules/lwip/build'
       gyp ERR! build error 
       gyp ERR! stack Error: `make` failed with exit code: 2
       gyp ERR! stack     at ChildProcess.onExit (/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:270:23)
       gyp ERR! stack     at emitTwo (events.js:87:13)
       gyp ERR! stack     at ChildProcess.emit (events.js:172:7)
       gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
       gyp ERR! System Linux 3.13.0-61-generic
       gyp ERR! command "/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.heroku/node/bin/node" "/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
       gyp ERR! cwd /tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/node_modules/lwip
       gyp ERR! node -v v4.1.1
       gyp ERR! node-gyp -v v3.0.3
       gyp ERR! not ok 
       npm ERR! Linux 3.13.0-61-generic
       npm ERR! argv "/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.heroku/node/bin/node" "/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.heroku/node/bin/npm" "install" "--unsafe-perm" "--userconfig" "/tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/.npmrc"
       npm ERR! node v4.1.1
       npm ERR! npm  v2.14.4
       npm ERR! code ELIFECYCLE

       npm ERR! [email protected] install: `node-gyp rebuild`
       npm ERR! Exit status 1
       npm ERR! 
       npm ERR! Failed at the [email protected] install script 'node-gyp rebuild'.
       npm ERR! This is most likely a problem with the lwip package,
       npm ERR! not with npm itself.
       npm ERR! Tell the author that this fails on your system:
       npm ERR!     node-gyp rebuild
       npm ERR! You can get their info via:
       npm ERR!     npm owner ls lwip
       npm ERR! There is likely additional logging output above.

       npm ERR! Please include the following file with any support request:
       npm ERR!     /tmp/build_d8e912fc9dc4649b59fea3b9f9e7edf6/CharlieHess-slack-poker-bot-1d80836/npm-debug.log
-----> Build failed

After some digging, I believe it is caused by the following lwip issue: EyalAr/lwip#169

Not sure if this is something you want to attempt to address (not sure if there would be a solution beyond migrating off of lwip or fixing their issue for them), but wanted to let you know that it seems like currently slack-poker-bot isn't deployable 😢

Position is broken post-flop when playing heads up.

Button should always be the SB when playing heads up, meaning they should act first preflop and 2nd postflop. Currently the SB does act first pre but for some reason acts 1st post as well. Bug only appears when playing heads up as far as i can tell.

Cards stopped dealing mid-game, returned empty set

Odd behavior here. Halfway through a trial game, the card response began rendering as only , rather than as the text representation and images. Screenshots and log output from Heroku below.

screen shot 2015-09-02 at 10 13 26 am


2015-09-02T14:28:42.799364+00:00 app[web.1]: Welcome to Slack. You are pokerbot of Raven
2015-09-02T14:28:42.799654+00:00 app[web.1]: You are in: general, poker-room
2015-09-02T14:28:42.799819+00:00 app[web.1]: Your open DM's: slackbot, henshaw, irene, nate, megan, lizzie, leah, i, kevin, steve, tamara
2015-09-02T14:28:44.582408+00:00 heroku[router]: at=info method=GET path="/" host=raven-poker-bot.herokuapp.com request_id=da87b9f6-b621-475a-a34b-6565aed5e5a6 fwd="50.232.2.81" dyno=web.1 connect=3ms service=7ms status=200 bytes=128
2015-09-02T14:28:44.926244+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=raven-poker-bot.herokuapp.com request_id=b55ebe81-205b-4ff2-bd68-f571d3fb37af fwd="50.232.2.81" dyno=web.1 connect=2ms service=4ms status=200 bytes=128
2015-09-02T14:35:43.664697+00:00 app[web.1]: Creating board image timed out
2015-09-02T14:36:15.581307+00:00 app[web.1]: Creating board image timed out
2015-09-02T14:36:16.620460+00:00 app[web.1]: Creating board image timed out
2015-09-02T14:36:17.754729+00:00 app[web.1]: npm ERR! Linux 3.13.0-61-generic
2015-09-02T14:36:17.688518+00:00 app[web.1]: /app/node_modules/rx/dist/rx.js:579
2015-09-02T14:36:17.688525+00:00 app[web.1]:     throw e;
2015-09-02T14:36:17.688527+00:00 app[web.1]:           ^
2015-09-02T14:36:17.688529+00:00 app[web.1]: TypeError: Cannot read property 'toString' of undefined
2015-09-02T14:36:17.688530+00:00 app[web.1]:     at /app/src/hand-evaluator.js:24:53
2015-09-02T14:36:17.688531+00:00 app[web.1]:     at Array.map (native)
2015-09-02T14:36:17.688533+00:00 app[web.1]:     at Function.evaluateHands (/app/src/hand-evaluator.js:24:37)
2015-09-02T14:36:17.688535+00:00 app[web.1]:     at PotManager.endHandWithShowdown (/app/src/pot-manager.js:184:34)
2015-09-02T14:36:17.688536+00:00 app[web.1]:     at AnonymousObserver._onNext (/app/src/texas-holdem.js:450:27)
2015-09-02T14:36:17.688538+00:00 app[web.1]:     at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (/app/node_modules/rx/dist/rx.js:1778:12)
2015-09-02T14:36:17.688540+00:00 app[web.1]:     at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/app/node_modules/rx/dist/rx.js:1710:35)
2015-09-02T14:36:17.688541+00:00 app[web.1]:     at AnonymousObserver.tryCatcher (/app/node_modules/rx/dist/rx.js:567:29)
2015-09-02T14:36:17.688543+00:00 app[web.1]:     at AutoDetachObserverPrototype.next (/app/node_modules/rx/dist/rx.js:5274:51)
2015-09-02T14:36:17.688544+00:00 app[web.1]:     at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/app/node_modules/rx/dist/rx.js:1710:35)
2015-09-02T14:36:17.748737+00:00 app[web.1]: 
2015-09-02T14:36:17.755397+00:00 app[web.1]: npm ERR! argv "/app/.heroku/node/bin/node" "/app/.heroku/node/bin/npm" "start"
2015-09-02T14:36:17.755714+00:00 app[web.1]: npm ERR! node v0.12.7
2015-09-02T14:36:17.756687+00:00 app[web.1]: npm ERR! npm  v2.11.3
2015-09-02T14:36:17.757045+00:00 app[web.1]: npm ERR! code ELIFECYCLE
2015-09-02T14:36:17.757295+00:00 app[web.1]: npm ERR! [email protected] start: `node src/main.js`
2015-09-02T14:36:17.757431+00:00 app[web.1]: npm ERR! Exit status 1
2015-09-02T14:36:17.757670+00:00 app[web.1]: npm ERR! 
2015-09-02T14:36:17.757916+00:00 app[web.1]: npm ERR! Failed at the [email protected] start script 'node src/main.js'.
2015-09-02T14:36:17.758307+00:00 app[web.1]: npm ERR! This is most likely a problem with the slack-poker-bot package,
2015-09-02T14:36:17.758515+00:00 app[web.1]: npm ERR! not with npm itself.
2015-09-02T14:36:17.758728+00:00 app[web.1]: npm ERR! Tell the author that this fails on your system:
2015-09-02T14:36:17.758930+00:00 app[web.1]: npm ERR!     node src/main.js
2015-09-02T14:36:17.759335+00:00 app[web.1]: npm ERR!     npm owner ls slack-poker-bot
2015-09-02T14:36:17.759130+00:00 app[web.1]: npm ERR! You can get their info via:
2015-09-02T14:36:17.759548+00:00 app[web.1]: npm ERR! There is likely additional logging output above.
2015-09-02T14:36:17.762431+00:00 app[web.1]: 
2015-09-02T14:36:17.762992+00:00 app[web.1]: npm ERR! Please include the following file with any support request:
2015-09-02T14:36:17.763193+00:00 app[web.1]: npm ERR!     /app/npm-debug.log
2015-09-02T14:36:18.633695+00:00 heroku[web.1]: State changed from crashed to starting
2015-09-02T14:36:18.633695+00:00 heroku[web.1]: State changed from up to crashed
2015-09-02T14:36:18.618224+00:00 heroku[web.1]: Process exited with status 1
2015-09-02T14:36:24.782187+00:00 heroku[web.1]: Starting process with command `npm start`
2015-09-02T14:36:33.369007+00:00 app[web.1]: 
2015-09-02T14:36:33.369027+00:00 app[web.1]: > [email protected] start /app
2015-09-02T14:36:33.369029+00:00 app[web.1]: > node src/main.js
2015-09-02T14:36:33.369030+00:00 app[web.1]: 

Random crash while idle

Sorry for lack of information, but sometimes, while being idle for some hours, the bot just closes; often without an error, sometimes with this error:

/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:579
    throw e;
          ^
TypeError: undefined is not a function
    at AnonymousObserver._onNext (/home/poker/slack-poker-bot/src/player-interaction.js:102:33)
    at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:1778:12)
    at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:1710:35)
    at AnonymousObserver.tryCatcher (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:567:29)
    at InnerObserver.onNext (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:4154:41)
    at InnerObserver.tryCatcher (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:567:29)
    at AutoDetachObserverPrototype.next (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:5274:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:1710:35)
    at AnonymousObserver._onNext (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:5079:13)
    at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (/home/poker/slack-poker-bot/node_modules/rx/dist/rx.js:1778:12)

Oh god, I love Rx, those stacktraces are always so useful.

times out!

Sets the duration (in seconds) before a player times out:30seconds ?
If I set 30Seconds ,but i response at the 10th seconds, will the time go on?or just do the next action? @CharlieHess

Improve handling for all-in actions

When a player bets all their chips, these actions are still labeled as "bets" or "raises." It would be helpful to note that these are all-ins, and provide an "all-in" action.

Shuffle algorithm is incorrect

Sorry I didn't just create a pull request, but I'm on my mobile.

I believe the shuffle algorithm here is incorrect: https://github.com/CharlieHess/slack-poker-bot/blob/master/src/deck.js

My understanding is that you only want to swap the current card with cards that have a greater index in the deck, basically creating a shuffled and unshuffled part of the deck. It's explained better here: http://www.i-programmer.info/programming/theory/2744-how-not-to-shuffle-the-kunth-fisher-yates-algorithm.html

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.