Skip to content

Commit

Permalink
Implement basic stress tests (#56)
Browse files Browse the repository at this point in the history
* test Served blackjack should be around 4%
* test served blackjack across multiple decks
* stats for Dealer bust after deal and player stand

Following the information here
http://www.blackjackage.com/bust-out-rate.php

* add stats references to README and some info about pseudo-random
* add expect(numberOfPlayerWinningGames).toBeLessThan(numberOfDealerWinningGames)

closes #55

Signed-off-by: Marco Casula <[email protected]>
  • Loading branch information
kedoska authored Jul 5, 2017
1 parent d9e69a8 commit d5f3943
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 66 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,37 @@ You can also write specific test cases using this syntax. For more details have

If you specify the `finalWin` the test will compare the final winning.

## Stats

Probabilities are not my favourite things... but apparently they are important for someone.
Got some information here:

* [In blackjack, what is the probability of a blackjack?](https://wizardofodds.com/ask-the-wizard/blackjack/probability/)
* [Dealer's Bust-Out Rate](http://www.blackjackage.com/bust-out-rate.php)

## Random

Please consider that for stress test this is the used random fn. It overrides the getRandom provided by [52-deck](https://github.com/kedoska/52-deck)

```javascript
const deck = require('52-deck')
deck.getRandom = (min, max) => {
let number
const range = max - min + 1
do
{
const buffer = crypto.randomBytes(4)
number = buffer.readUInt8(0)
}
while (number >= Number.MAX_VALUE - (Number.MAX_VALUE % range))
number %= range
return number + min
}
```

The original function (provided by 52-deck) is `const getRandom = (v: number) => Math.floor(Math.random() * v)`.
I just wanted to be fancy importing and implementing something approved by a well known game laboratory _(No more details here)_.

## Side Bets

Side bets are part of the "multi-game strategy". They are returned to the client as "available bets" and they can be sets in the `deal()` _payload_.
Expand Down
88 changes: 88 additions & 0 deletions test/stress.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import crypto from 'crypto'
import { calculate, isBlackjack, getHigherValidValue } from '../src/engine'
const deck = require('52-deck')
deck.getRandom = (min, max) => {
let number
const range = max - min + 1
do
{
const buffer = crypto.randomBytes(4)
number = buffer.readUInt8(0)
}
while (number >= Number.MAX_VALUE - (Number.MAX_VALUE % range))
number %= range
return number + min
}

describe('Probabilities', () => {
describe('Served blackjack', () => {
[1,2,4,6,8].forEach(numberOfDecks => {
it(`should be around 4% with ${numberOfDecks} deck/s`, () => {
const MAX = 10000
let p = 0
let d = 0
for(let i = 0; i < MAX; i ++) {
const cards = deck.shuffle(deck.newDecks(numberOfDecks))
const playerCards = cards.splice(cards.length - 2, 2)
const dealerFirstCards = cards.splice(cards.length - 1, 1)
const dealerHoleCard = cards.splice(cards.length - 1, 1)[ 0 ]
const dealerCards = dealerFirstCards.concat([dealerHoleCard])
const playerHasBlackjack = isBlackjack(playerCards)
const dealerHasBlackjack = isBlackjack(dealerCards)
p = playerHasBlackjack ? p + 1 : p
d = dealerHasBlackjack ? d + 1 : d
}
p = (p / MAX) * 100
d = (d / MAX) * 100
expect(p).toBeGreaterThanOrEqual(4)
expect(p).toBeLessThanOrEqual(6)

expect(d).toBeGreaterThanOrEqual(4)
expect(d).toBeLessThanOrEqual(6)
})
})
})
describe('Dealer bust after deal and player stand', () => {
[1,2,4,6,8].forEach(numberOfDecks => {
it(`should be between 22 and 32% with ${numberOfDecks} deck/s`, () => {
const MAX = 1000
let numberOfBustedGames = 0
let numberOfDealerWinningGames = 0
let numberOfPlayerWinningGames = 0
let numberOfPushGames = 0
for (let i = 0; i < MAX; i++) {
const cards = deck.shuffle(deck.newDecks(numberOfDecks))
const playerCards = cards.splice(cards.length - 2, 2)
const dealerFirstCards = cards.splice(cards.length - 1, 1)
const dealerHoleCard = cards.splice(cards.length - 1, 1)[0]
let dealerCards = dealerFirstCards.concat([dealerHoleCard])
const pV = getHigherValidValue(calculate(playerCards))
let dV = getHigherValidValue(calculate(dealerCards))
while (dV < 17){
const nextCard = cards.splice(cards.length - 1, 1)[0]
dealerCards = dealerCards.concat([nextCard])
dV = getHigherValidValue(calculate(dealerCards))
}
let dealerHasBusted = dV > 21

if (dealerHasBusted) {
numberOfBustedGames++
}
if (!dealerHasBusted && dV > pV) {
numberOfDealerWinningGames++
}
if (pV > dV || dealerHasBusted) {
numberOfPlayerWinningGames++
}
if (!dealerHasBusted && dV === pV) {
numberOfPushGames++
}
}
numberOfBustedGames = (numberOfBustedGames / MAX) * 100
expect(numberOfBustedGames).toBeGreaterThanOrEqual(22)
expect(numberOfBustedGames).toBeLessThanOrEqual(32)
expect(numberOfPlayerWinningGames).toBeLessThan(numberOfDealerWinningGames)
})
})
})
})
74 changes: 8 additions & 66 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -329,17 +329,7 @@ babel-helper-flip-expressions@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.1.2.tgz#77f6652f9de9c42401d827bd46ebd2109e3ef18a"

babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz#25742d67175c8903dbe4b6cb9d9e1fcb8dcf23a6"
dependencies:
babel-helper-get-function-arity "^6.22.0"
babel-runtime "^6.22.0"
babel-template "^6.23.0"
babel-traverse "^6.23.0"
babel-types "^6.23.0"

babel-helper-function-name@^6.24.1:
babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0, babel-helper-function-name@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
dependencies:
Expand All @@ -349,14 +339,7 @@ babel-helper-function-name@^6.24.1:
babel-traverse "^6.24.1"
babel-types "^6.24.1"

babel-helper-get-function-arity@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce"
dependencies:
babel-runtime "^6.22.0"
babel-types "^6.22.0"

babel-helper-get-function-arity@^6.24.1:
babel-helper-get-function-arity@^6.22.0, babel-helper-get-function-arity@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
dependencies:
Expand Down Expand Up @@ -674,7 +657,7 @@ babel-plugin-transform-es2015-modules-amd@^6.22.0:
babel-runtime "^6.22.0"
babel-template "^6.22.0"

babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.23.0:
babel-plugin-transform-es2015-modules-commonjs@^6.22.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.23.0.tgz#cba7aa6379fb7ec99250e6d46de2973aaffa7b92"
dependencies:
Expand All @@ -683,7 +666,7 @@ babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-e
babel-template "^6.23.0"
babel-types "^6.23.0"

babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe"
dependencies:
Expand Down Expand Up @@ -972,7 +955,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"

babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0:
babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0, babel-template@^6.24.1, babel-template@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
dependencies:
Expand All @@ -982,17 +965,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0:
babylon "^6.17.2"
lodash "^4.2.0"

babel-template@^6.22.0, babel-template@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638"
dependencies:
babel-runtime "^6.22.0"
babel-traverse "^6.23.0"
babel-types "^6.23.0"
babylon "^6.11.0"
lodash "^4.2.0"

babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0:
babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
dependencies:
Expand All @@ -1006,21 +979,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0:
invariant "^2.2.0"
lodash "^4.2.0"

babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
version "6.23.1"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48"
dependencies:
babel-code-frame "^6.22.0"
babel-messages "^6.23.0"
babel-runtime "^6.22.0"
babel-types "^6.23.0"
babylon "^6.15.0"
debug "^2.2.0"
globals "^9.0.0"
invariant "^2.2.0"
lodash "^4.2.0"

babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.25.0:
babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
dependencies:
Expand All @@ -1029,15 +988,6 @@ babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.25.0:
lodash "^4.2.0"
to-fast-properties "^1.0.1"

babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf"
dependencies:
babel-runtime "^6.22.0"
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^1.0.1"

babili-webpack-plugin@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/babili-webpack-plugin/-/babili-webpack-plugin-0.1.2.tgz#164ac03d5932f6a52143e7ffc06f2711c651b6f2"
Expand All @@ -1046,10 +996,6 @@ babili-webpack-plugin@^0.1.1:
babel-preset-babili "^0.1.4"
webpack-sources "^1.0.1"

babylon@^6.11.0, babylon@^6.15.0:
version "6.16.1"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"

babylon@^6.17.0, babylon@^6.17.2, babylon@^6.17.4:
version "6.17.4"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
Expand Down Expand Up @@ -2972,11 +2918,7 @@ istanbul-api@^1.1.1:
mkdirp "^0.5.1"
once "^1.4.0"

istanbul-lib-coverage@^1.0.0-alpha.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz#f263efb519c051c5f1f3343034fc40e7b43ff212"

istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1:
istanbul-lib-coverage@^1.0.0-alpha.0, istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"

Expand Down

0 comments on commit d5f3943

Please sign in to comment.