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 @@ -154,6 +154,14 @@ stdDev: 28.28
95% CI: [5.80, 84.20]
```

## example: passCome68 strategy

```
$ node hands.js 1 passCome68
Simulating 1 Craps Hand(s)
Using betting strategy: passCome68
```

The standard deviation shows how widely the trials vary around the mean.
Roughly 68% of results will fall within one standard deviation, 95% within
two, and 99.7% within three. The 95% confidence interval is a range that is
Expand Down
52 changes: 45 additions & 7 deletions betting.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ function placeSixEightUnlessPoint (opts) {
return bets
}

function placeSixEightUnlessPassOrCome (opts) {
const { hand, bets: existingBets } = opts

const bets = placeSixEight(opts)
const comePoints = Object.keys(existingBets?.come?.points || {})
const coveredPoints = new Set([hand.point, ...comePoints.map(Number)])

if (coveredPoints.has(6) && bets.place?.six) {
bets.new -= bets.place.six.amount
delete bets.place.six
if (process.env.DEBUG) console.log('[decision] removed place 6 bet matching pass or come point')
}

if (coveredPoints.has(8) && bets.place?.eight) {
bets.new -= bets.place.eight.amount
delete bets.place.eight
if (process.env.DEBUG) console.log('[decision] removed place 8 bet matching pass or come point')
}

if (bets.place && Object.keys(bets.place).length === 0) delete bets.place

return bets
}

function minPassLinePlaceSixEight (opts) {
let bets = minPassLineOnly(opts)
bets = placeSixEightUnlessPoint({ ...opts, bets })
Expand All @@ -145,7 +169,20 @@ function minPassLineMaxOddsPlaceSixEight (opts) {
return bets
}

function comeLineMaxOdds (opts) {
function minPassLineMaxOddsMinComeLineMaxOdds (opts) {
let bets = minPassLineMaxOdds(opts)
bets = minComeLineMaxOdds({ ...opts, bets })
return bets
}

function passCome68 (opts) {
let bets = minPassLineMaxOdds(opts)
bets = minComeLineMaxOdds({ ...opts, bets })
bets = placeSixEightUnlessPassOrCome({ ...opts, bets })
return bets
}

function minComeLineMaxOdds (opts) {
const { rules, bets: existingBets = {}, hand, maxComeBets = 1 } = opts
const bets = Object.assign({ new: 0 }, existingBets)

Expand All @@ -158,16 +195,14 @@ function comeLineMaxOdds (opts) {
bets.come.pending = bets.come.pending || []
bets.come.points = bets.come.points || {}

let activeComeBets = bets.come.pending.length

activeComeBets += Object.values(bets.come.points).reduce((memo, pointBets) => {
const pendingCount = bets.come.pending.length
const pointCount = Object.values(bets.come.points).reduce((memo, pointBets) => {
return memo + pointBets.length
}, 0)

while (activeComeBets < maxComeBets) {
if (pendingCount === 0 && pointCount < maxComeBets) {
bets.come.pending.push({ amount: rules.minBet })
bets.new += rules.minBet
activeComeBets++
if (process.env.DEBUG) console.log(`[action] make come line bet $${rules.minBet}`)
}

Expand All @@ -192,7 +227,10 @@ module.exports = {
minPassLineMaxOdds,
placeSixEight,
placeSixEightUnlessPoint,
placeSixEightUnlessPassOrCome,
minPassLinePlaceSixEight,
minPassLineMaxOddsPlaceSixEight,
comeLineMaxOdds
minPassLineMaxOddsMinComeLineMaxOdds,
minComeLineMaxOdds,
passCome68
}
117 changes: 115 additions & 2 deletions betting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ tap.test('lineMaxOdds: add odds to existing line bet', (t) => {
t.end()
})

tap.test('comeLineMaxOdds: create pending come bet and add odds', (t) => {
tap.test('minComeLineMaxOdds: create pending come bet and add odds', (t) => {
const rules = {
minBet: 5,
maxOddsMultiple: { 4: 3, 5: 4, 6: 5, 8: 5, 9: 4, 10: 3 }
Expand All @@ -167,7 +167,7 @@ tap.test('comeLineMaxOdds: create pending come bet and add odds', (t) => {
const hand = { isComeOut: false, point: 6 }
const bets = { come: { points: { 5: [{ line: { amount: 5 } }] } } }

const updated = lib.comeLineMaxOdds({ rules, bets, hand, maxComeBets: 2 })
const updated = lib.minComeLineMaxOdds({ rules, bets, hand, maxComeBets: 2 })

t.equal(updated.come.pending.length, 1, 'adds a new pending come bet')
t.equal(updated.come.pending[0].amount, rules.minBet)
Expand All @@ -177,6 +177,23 @@ tap.test('comeLineMaxOdds: create pending come bet and add odds', (t) => {
t.end()
})

tap.test('minComeLineMaxOdds: only one pending come bet at a time', (t) => {
const rules = {
minBet: 5,
maxOddsMultiple: { 4: 3, 5: 4, 6: 5, 8: 5, 9: 4, 10: 3 }
}

const hand = { isComeOut: false, point: 6 }
const bets = { come: { pending: [{ amount: 5 }], points: {} } }

const updated = lib.minComeLineMaxOdds({ rules, bets, hand, maxComeBets: 3 })

t.equal(updated.come.pending.length, 1, 'does not stack pending come bets')
t.notOk(updated.new, 'no additional come bet added')

t.end()
})

tap.test('minPassLineMaxOdds: make new bet upon establishing point', (t) => {
const rules = {
minBet: 5,
Expand Down Expand Up @@ -518,6 +535,102 @@ tap.test('minPassLinePlaceSixEight: existing bets remain unchanged', (t) => {
t.end()
})

tap.test('minPassLineMaxOddsMinComeLineMaxOdds: adds come bet after point set', (t) => {
const rules = {
minBet: 5,
maxOddsMultiple: {
4: 3,
5: 4,
6: 5,
8: 5,
9: 4,
10: 3
}
}

const comeOut = { isComeOut: true }
const first = lib.minPassLineMaxOddsMinComeLineMaxOdds({ rules, hand: comeOut })

t.equal(first.pass.line.amount, rules.minBet)
t.notOk(first.come, 'no come bet on comeout')
t.equal(first.new, rules.minBet)

delete first.new

const pointSix = { isComeOut: false, result: 'point set', point: 6 }
const second = lib.minPassLineMaxOddsMinComeLineMaxOdds({ rules, bets: first, hand: pointSix })

t.equal(second.pass.odds.amount, rules.maxOddsMultiple['6'] * rules.minBet)
t.equal(second.come.pending.length, 1, 'adds one pending come bet after point set')
t.equal(second.come.pending[0].amount, rules.minBet)
t.equal(second.new, rules.maxOddsMultiple['6'] * rules.minBet + rules.minBet)

t.end()
})

tap.test('passCome68: adds pass odds, come bet, and place bets not on pass point', (t) => {
const rules = {
minBet: 5,
maxOddsMultiple: {
4: 3,
5: 4,
6: 5,
8: 5,
9: 4,
10: 3
}
}

const comeOut = { isComeOut: true }
const first = lib.passCome68({ rules, hand: comeOut })

t.equal(first.pass.line.amount, rules.minBet)
t.notOk(first.come, 'no come bet on comeout')
t.notOk(first.place, 'no place bets on comeout')
t.equal(first.new, rules.minBet)

delete first.new

const pointSix = { isComeOut: false, result: 'point set', point: 6 }
const second = lib.passCome68({ rules, bets: first, hand: pointSix })

t.equal(second.pass.odds.amount, rules.maxOddsMultiple['6'] * rules.minBet)
t.equal(second.come.pending.length, 1, 'adds one pending come bet after point set')
t.notOk(second.place?.six, 'skip place 6 when 6 is the pass point')
t.equal(second.place.eight.amount, 6)
t.equal(second.new, second.pass.odds.amount + rules.minBet + 6)

t.end()
})

tap.test('passCome68: skips place bets covered by come points', (t) => {
const rules = {
minBet: 5,
maxOddsMultiple: {
4: 3,
5: 4,
6: 5,
8: 5,
9: 4,
10: 3
}
}

const hand = { isComeOut: false, result: 'neutral', point: 5 }
const bets = {
pass: { line: { amount: 5, isContract: true }, odds: { amount: 20 } },
come: { points: { 6: [{ line: { amount: 5 } }] } }
}

const updated = lib.passCome68({ rules, bets, hand })

t.notOk(updated.place?.six, 'skip place 6 when come point is 6')
t.equal(updated.place.eight.amount, 6)
t.equal(updated.new, rules.maxOddsMultiple['6'] * rules.minBet + 6)

t.end()
})

// Priority 2: Test all points (4, 5, 6, 8, 9, 10) with odds calculations
tap.test('minPassLineMaxOdds: all points have correct odds multiples', (t) => {
const rules = {
Expand Down