Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ Hand: 1
└─────────┴──────┴──────┴─────────┴─────────────┴───────────┴───────┘
```

## use as a module

```js
const simulateHands = require('./hands.js')
const result = simulateHands(1)
console.log(result.sessionSummary)
```

## table rules

`playHand` accepts a `rules` object that controls minimum bets and odds limits.
Expand Down
284 changes: 148 additions & 136 deletions hands.js
Original file line number Diff line number Diff line change
@@ -1,158 +1,170 @@
#!/usr/bin/env node
'use strict'

const { playHand } = require('./index.js')
const { minPassLineMaxOddsPlaceSixEight } = require('./betting.js')

const numHands = parseInt(process.argv.slice(2)[0], 10)
const showDetail = process.argv.slice(2)[1]

console.log(`Simulating ${numHands} Craps Hand(s)`)
console.log('Using betting strategy: minPassLineMaxOddsPlaceSixEight')

const summaryTemplate = {
balance: 0,
rollCount: 0,
pointsSet: 0,
pointsWon: 0,
comeOutWins: 0,
comeOutLosses: 0,
netComeOutWins: 0,
neutrals: 0,
placeSixWins: 0,
placeSixLosses: 0,
placeEightWins: 0,
placeEightLosses: 0,
dist: {
2: { ct: 0, prob: 1 / 36 },
3: { ct: 0, prob: 2 / 36 },
4: { ct: 0, prob: 3 / 36 },
5: { ct: 0, prob: 4 / 36 },
6: { ct: 0, prob: 5 / 36 },
7: { ct: 0, prob: 6 / 36 },
8: { ct: 0, prob: 5 / 36 },
9: { ct: 0, prob: 4 / 36 },
10: { ct: 0, prob: 3 / 36 },
11: { ct: 0, prob: 2 / 36 },
12: { ct: 0, prob: 1 / 36 }
}
}

const sessionSummary = Object.assign({}, summaryTemplate)

const hands = []
const rules = {
minBet: 10,
maxOddsMultiple: {
4: 3,
5: 4,
6: 5,
8: 5,
9: 4,
10: 3
function simulateHands (numHands, showDetail) {
const summaryTemplate = {
balance: 0,
rollCount: 0,
pointsSet: 0,
pointsWon: 0,
comeOutWins: 0,
comeOutLosses: 0,
netComeOutWins: 0,
neutrals: 0,
placeSixWins: 0,
placeSixLosses: 0,
placeEightWins: 0,
placeEightLosses: 0,
dist: {
2: { ct: 0, prob: 1 / 36 },
3: { ct: 0, prob: 2 / 36 },
4: { ct: 0, prob: 3 / 36 },
5: { ct: 0, prob: 4 / 36 },
6: { ct: 0, prob: 5 / 36 },
7: { ct: 0, prob: 6 / 36 },
8: { ct: 0, prob: 5 / 36 },
9: { ct: 0, prob: 4 / 36 },
10: { ct: 0, prob: 3 / 36 },
11: { ct: 0, prob: 2 / 36 },
12: { ct: 0, prob: 1 / 36 }
}
}
}

console.log(`[table rules] minimum bet: $${rules.minBet}`)

for (let i = 0; i < numHands; i++) {
if (process.env.DEBUG) {
console.log(`\n================ HAND ${i + 1} ================`)
}
const hand = playHand({ rules, bettingStrategy: minPassLineMaxOddsPlaceSixEight })
hand.summary = Object.assign({}, summaryTemplate)

sessionSummary.balance += hand.balance
hand.summary.balance = hand.balance

hand.history.reduce((memo, roll) => {
memo.rollCount++
hand.summary.rollCount++
memo.dist[roll.diceSum].ct++

switch (roll.result) {
case 'neutral':
memo.neutrals++
hand.summary.neutrals++
break
case 'point set':
memo.pointsSet++
hand.summary.pointsSet++
break
case 'point win':
memo.pointsWon++
hand.summary.pointsWon++
break
case 'comeout win':
memo.comeOutWins++
hand.summary.comeOutWins++
memo.netComeOutWins++
hand.summary.netComeOutWins++
break
case 'comeout loss':
memo.comeOutLosses++
hand.summary.comeOutLosses++
memo.netComeOutWins--
hand.summary.netComeOutWins--
break
const sessionSummary = Object.assign({}, summaryTemplate)
const hands = []
const rules = {
minBet: 10,
maxOddsMultiple: {
4: 3,
5: 4,
6: 5,
8: 5,
9: 4,
10: 3
}
}

if (Array.isArray(roll.payouts)) {
roll.payouts.forEach(p => {
if (p.type === 'place 6 win') {
memo.placeSixWins++
hand.summary.placeSixWins++
} else if (p.type === 'place 8 win') {
memo.placeEightWins++
hand.summary.placeEightWins++
}
})
for (let i = 0; i < numHands; i++) {
if (process.env.DEBUG) {
console.log(`\n================ HAND ${i + 1} ================`)
}
const hand = playHand({ rules, bettingStrategy: minPassLineMaxOddsPlaceSixEight })
hand.summary = Object.assign({}, summaryTemplate)

sessionSummary.balance += hand.balance
hand.summary.balance = hand.balance

hand.history.reduce((memo, roll) => {
memo.rollCount++
hand.summary.rollCount++
memo.dist[roll.diceSum].ct++

switch (roll.result) {
case 'neutral':
memo.neutrals++
hand.summary.neutrals++
break
case 'point set':
memo.pointsSet++
hand.summary.pointsSet++
break
case 'point win':
memo.pointsWon++
hand.summary.pointsWon++
break
case 'comeout win':
memo.comeOutWins++
hand.summary.comeOutWins++
memo.netComeOutWins++
hand.summary.netComeOutWins++
break
case 'comeout loss':
memo.comeOutLosses++
hand.summary.comeOutLosses++
memo.netComeOutWins--
hand.summary.netComeOutWins--
break
}

if (roll.result === 'seven out') {
if (roll.betsBefore?.place?.six) {
memo.placeSixLosses++
hand.summary.placeSixLosses++
if (Array.isArray(roll.payouts)) {
roll.payouts.forEach(p => {
if (p.type === 'place 6 win') {
memo.placeSixWins++
hand.summary.placeSixWins++
} else if (p.type === 'place 8 win') {
memo.placeEightWins++
hand.summary.placeEightWins++
}
})
}
if (roll.betsBefore?.place?.eight) {
memo.placeEightLosses++
hand.summary.placeEightLosses++

if (roll.result === 'seven out') {
if (roll.betsBefore?.place?.six) {
memo.placeSixLosses++
hand.summary.placeSixLosses++
}
if (roll.betsBefore?.place?.eight) {
memo.placeEightLosses++
hand.summary.placeEightLosses++
}
}
}

return memo
}, sessionSummary)
return memo
}, sessionSummary)

hands.push(hand)
}

sessionSummary.handCount = hands.length

for (const k of Object.keys(sessionSummary.dist)) {
sessionSummary.dist[k].ref = Number((sessionSummary.dist[k].prob * sessionSummary.rollCount).toFixed(1))
sessionSummary.dist[k].diff = Number((sessionSummary.dist[k].ct - sessionSummary.dist[k].ref).toFixed(1))
sessionSummary.dist[k].diff_pct = Number((((sessionSummary.dist[k].ct - sessionSummary.dist[k].ref) / sessionSummary.dist[k].ref) * 100).toFixed(1))
if (showDetail) {
sessionSummary.dist[k].ref_work = `${(sessionSummary.dist[k].prob * sessionSummary.rollCount).toFixed(1)} (${sessionSummary.rollCount} * ${sessionSummary.dist[k].prob.toFixed(2)})`
}
delete sessionSummary.dist[k].prob
}

hands.push(hand)
return { sessionSummary, hands, rules }
}

sessionSummary.handCount = hands.length
function printResults ({ sessionSummary, hands, showDetail, rules }) {
console.log(`[table rules] minimum bet: $${rules.minBet}`)

console.log('\nDice Roll Distribution')
console.table(sessionSummary.dist)
delete sessionSummary.dist

console.log('\nSession Summary')
console.table(sessionSummary)

console.log('\nHands Summary')
console.table(hands.map(hand => {
delete hand.summary.dist
return hand.summary
}))

for (const k of Object.keys(sessionSummary.dist)) {
sessionSummary.dist[k].ref = Number((sessionSummary.dist[k].prob * sessionSummary.rollCount).toFixed(1))
sessionSummary.dist[k].diff = Number((sessionSummary.dist[k].ct - sessionSummary.dist[k].ref).toFixed(1))
sessionSummary.dist[k].diff_pct = Number((((sessionSummary.dist[k].ct - sessionSummary.dist[k].ref) / sessionSummary.dist[k].ref) * 100).toFixed(1))
if (showDetail) {
sessionSummary.dist[k].ref_work = `${(sessionSummary.dist[k].prob * sessionSummary.rollCount).toFixed(1)} (${sessionSummary.rollCount} * ${sessionSummary.dist[k].prob.toFixed(2)})`
console.log('\nHands')
hands.forEach((hand, handNum) => {
console.log(`\nHand: ${handNum + 1}, Balance: $${hand.balance}`)
console.table(hand.history)
})
}
delete sessionSummary.dist[k].prob
}
console.log('\nDice Roll Distribution')
console.table(sessionSummary.dist)
delete sessionSummary.dist

console.log('\nSession Summary')
console.table(sessionSummary)

console.log('\nHands Summary')
console.table(hands.map(hand => {
delete hand.summary.dist
return hand.summary
}))

if (showDetail) {
console.log('\nHands')
hands.forEach((hand, handNum) => {
console.log(`\nHand: ${handNum + 1}, Balance: $${hand.balance}`)
console.table(hand.history)
})

if (require.main === module) {
const numHands = parseInt(process.argv[2], 10)
const showDetail = process.argv[3]
console.log(`Simulating ${numHands} Craps Hand(s)`)
console.log('Using betting strategy: minPassLineMaxOddsPlaceSixEight')
const result = simulateHands(numHands, showDetail)
printResults({ ...result, showDetail })
} else {
module.exports = simulateHands
}