diff --git a/betting.js b/betting.js index c6ca88c..a3df21a 100644 --- a/betting.js +++ b/betting.js @@ -145,6 +145,47 @@ function minPassLineMaxOddsPlaceSixEight (opts) { return bets } +function comeLineMaxOdds (opts) { + const { rules, bets: existingBets = {}, hand, maxComeBets = 1 } = opts + const bets = Object.assign({ new: 0 }, existingBets) + + if (hand.isComeOut) { + if (process.env.DEBUG) console.log('[decision] skip come bets on comeout') + return bets + } + + bets.come = bets.come || {} + 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) => { + return memo + pointBets.length + }, 0) + + while (activeComeBets < 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}`) + } + + Object.keys(bets.come.points).forEach(point => { + bets.come.points[point].forEach(bet => { + if (!bet.line || bet.odds) return + const oddsAmount = rules.maxOddsMultiple[point] * bet.line.amount + bet.odds = { amount: oddsAmount } + bets.new += oddsAmount + if (process.env.DEBUG) { + console.log(`[action] make come odds bet behind ${point} for $${oddsAmount}`) + } + }) + }) + + return bets +} + module.exports = { minPassLineOnly, lineMaxOdds, @@ -152,5 +193,6 @@ module.exports = { placeSixEight, placeSixEightUnlessPoint, minPassLinePlaceSixEight, - minPassLineMaxOddsPlaceSixEight + minPassLineMaxOddsPlaceSixEight, + comeLineMaxOdds } diff --git a/betting.test.js b/betting.test.js index af4a597..468eada 100644 --- a/betting.test.js +++ b/betting.test.js @@ -158,6 +158,25 @@ tap.test('lineMaxOdds: add odds to existing line bet', (t) => { t.end() }) +tap.test('comeLineMaxOdds: 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 } + } + + const hand = { isComeOut: false, point: 6 } + const bets = { come: { points: { 5: [{ line: { amount: 5 } }] } } } + + const updated = lib.comeLineMaxOdds({ 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) + t.equal(updated.come.points[5][0].odds.amount, rules.maxOddsMultiple['5'] * rules.minBet, 'adds odds behind come point') + t.equal(updated.new, rules.minBet + updated.come.points[5][0].odds.amount, 'tracks new wagers') + + t.end() +}) + tap.test('minPassLineMaxOdds: make new bet upon establishing point', (t) => { const rules = { minBet: 5, diff --git a/settle.js b/settle.js index 9fafc7d..33847d1 100644 --- a/settle.js +++ b/settle.js @@ -126,6 +126,100 @@ function placeEight (opts) { return placeBet({ ...opts, placeNumber: 8 }) } +function comeLine ({ bets, hand }) { + if (!bets?.come) { + if (process.env.DEBUG) console.log('[decision] no come bets') + return { bets } + } + + const payouts = [] + bets.come.points = bets.come.points || {} + + if (bets.come.points) { + Object.keys(bets.come.points).forEach(point => { + const remainingBets = [] + bets.come.points[point].forEach(bet => { + if (hand.result === 'seven out') { + if (process.env.DEBUG) console.log(`[decision] come line ${point} loss -$${bet.line.amount}`) + return + } + + if (hand.diceSum === Number(point)) { + const linePayout = { + type: 'come line win', + principal: bet.line.amount, + profit: bet.line.amount + } + payouts.push(linePayout) + + if (bet.odds) { + const oddsPayouts = { + 4: 2, + 5: 3 / 2, + 6: 6 / 5, + 8: 6 / 5, + 9: 3 / 2, + 10: 2 + } + + payouts.push({ + type: 'come odds win', + principal: bet.odds.amount, + profit: bet.odds.amount * oddsPayouts[point] + }) + } + return + } + + remainingBets.push(bet) + }) + + if (remainingBets.length) { + bets.come.points[point] = remainingBets + } else { + delete bets.come.points[point] + } + }) + + if (Object.keys(bets.come.points).length === 0) { + delete bets.come.points + } + } + + const pending = bets.come.pending || [] + + if (pending.length) { + const immediateWins = [7, 11] + const immediateLosses = [2, 3, 12] + + pending.forEach(bet => { + if (immediateWins.includes(hand.diceSum)) { + const payout = { + type: 'come line win', + principal: bet.amount, + profit: bet.amount + } + payouts.push(payout) + } else if (immediateLosses.includes(hand.diceSum)) { + if (process.env.DEBUG) console.log(`[decision] come line loss -$${bet.amount}`) + } else { + bets.come.points = bets.come.points || {} + bets.come.points[hand.diceSum] = bets.come.points[hand.diceSum] || [] + bets.come.points[hand.diceSum].push({ line: { amount: bet.amount } }) + if (process.env.DEBUG) console.log(`[decision] come line moves to ${hand.diceSum}`) + } + }) + + delete bets.come.pending + } + + if (bets.come && Object.keys(bets.come).length === 0) { + delete bets.come + } + + return { bets, payouts } +} + function all ({ bets, hand, rules }) { const payouts = [] @@ -139,6 +233,11 @@ function all ({ bets, hand, rules }) { bets = passOddsResult.bets payouts.push(passOddsResult.payout) + const comeLineResult = comeLine({ bets, hand }) + + bets = comeLineResult.bets + payouts.push(...(comeLineResult.payouts || [])) + const placeSixResult = placeSix({ bets, hand }) bets = placeSixResult.bets @@ -174,5 +273,6 @@ module.exports = { placeBet, placeSix, placeEight, + comeLine, all } diff --git a/settle.test.js b/settle.test.js index e29d6aa..facaad5 100644 --- a/settle.test.js +++ b/settle.test.js @@ -357,6 +357,50 @@ tap.test('passOdds: odds bet, seven out', function (t) { t.end() }) +tap.test('comeLine: pending come bet wins immediately', (t) => { + const bets = { come: { pending: [{ amount: 5 }] } } + const hand = { result: 'neutral', diceSum: 11 } + + const result = settle.comeLine({ bets, hand }) + + t.equal(result.payouts[0].type, 'come line win') + t.equal(result.payouts[0].principal, 5) + t.equal(result.payouts[0].profit, 5) + t.notOk(result.bets.come) + + t.end() +}) + +tap.test('comeLine: pending come bet moves to point', (t) => { + const bets = { come: { pending: [{ amount: 5 }] } } + const hand = { result: 'neutral', diceSum: 4, point: 6 } + + const result = settle.comeLine({ bets, hand }) + + t.notOk(result.payouts?.length) + t.equal(result.bets.come.points[4][0].line.amount, 5) + t.notOk(result.bets.come.pending) + + t.end() +}) + +tap.test('comeLine: come point win pays odds', (t) => { + const bets = { come: { points: { 5: [{ line: { amount: 5 }, odds: { amount: 20 } }] } } } + const hand = { result: 'neutral', diceSum: 5, point: 6 } + + const result = settle.comeLine({ bets, hand }) + + const lineWin = result.payouts.find(p => p.type === 'come line win') + const oddsWin = result.payouts.find(p => p.type === 'come odds win') + + t.equal(lineWin.profit, 5) + t.equal(oddsWin.principal, 20) + t.equal(oddsWin.profit, 30) + t.notOk(result.bets.come) + + t.end() +}) + tap.test('all: pass line win', (t) => { const bets = { pass: {