diff --git a/hands.js b/hands.js index 85c5f9e..a19adea 100644 --- a/hands.js +++ b/hands.js @@ -14,6 +14,10 @@ function simulateHands ({ numHands, bettingStrategy, showDetail }) { comeOutLosses: 0, netComeOutWins: 0, neutrals: 0, + comeLineWins: 0, + comeLineLosses: 0, + comeOddsWins: 0, + comeOddsLosses: 0, placeSixWins: 0, placeSixLosses: 0, placeEightWins: 0, @@ -36,7 +40,7 @@ function simulateHands ({ numHands, bettingStrategy, showDetail }) { const sessionSummary = Object.assign({}, summaryTemplate) const hands = [] const rules = { - minBet: 10, + minBet: 15, maxOddsMultiple: { 4: 3, 5: 4, @@ -97,6 +101,12 @@ function simulateHands ({ numHands, bettingStrategy, showDetail }) { } else if (p.type === 'place 8 win') { memo.placeEightWins++ hand.summary.placeEightWins++ + } else if (p.type === 'come line win') { + memo.comeLineWins++ + hand.summary.comeLineWins++ + } else if (p.type === 'come odds win') { + memo.comeOddsWins++ + hand.summary.comeOddsWins++ } }) } @@ -110,6 +120,18 @@ function simulateHands ({ numHands, bettingStrategy, showDetail }) { memo.placeEightLosses++ hand.summary.placeEightLosses++ } + if (roll.betsBefore?.come?.points) { + Object.keys(roll.betsBefore.come.points).forEach(point => { + roll.betsBefore.come.points[point].forEach(bet => { + memo.comeLineLosses++ + hand.summary.comeLineLosses++ + if (bet.odds) { + memo.comeOddsLosses++ + hand.summary.comeOddsLosses++ + } + }) + }) + } } return memo @@ -153,7 +175,218 @@ function printResults ({ sessionSummary, hands, showDetail, rules }) { console.log('\nHands') hands.forEach((hand, handNum) => { console.log(`\nHand: ${handNum + 1}, Balance: $${hand.balance}`) - console.table(hand.history) + const formattedHistory = hand.history.map((roll, rollIndex) => { + const formatted = {} + + // Get game state BEFORE this roll (from previous roll or initial state) + let beforeIsComeOut, beforePoint + if (rollIndex === 0) { + // First roll - use initial hand state + beforeIsComeOut = true + beforePoint = undefined + } else { + // Use previous roll's after state + const prevRoll = hand.history[rollIndex - 1] + beforeIsComeOut = prevRoll.isComeOut + beforePoint = prevRoll.point + } + + // Calculate total money in play + let moneyInPlay = 0 + if (roll.betsBefore) { + // Pass line and odds + if (roll.betsBefore.pass?.line) { + moneyInPlay += roll.betsBefore.pass.line.amount + } + if (roll.betsBefore.pass?.odds) { + moneyInPlay += roll.betsBefore.pass.odds.amount + } + // Place bets + if (roll.betsBefore.place?.six) { + moneyInPlay += roll.betsBefore.place.six.amount + } + if (roll.betsBefore.place?.eight) { + moneyInPlay += roll.betsBefore.place.eight.amount + } + // Come bets + if (roll.betsBefore.come?.points) { + Object.values(roll.betsBefore.come.points).forEach(pointBets => { + pointBets.forEach(bet => { + if (bet.line) moneyInPlay += bet.line.amount + if (bet.odds) moneyInPlay += bet.odds.amount + }) + }) + } + if (roll.betsBefore.come?.pending?.length) { + moneyInPlay += roll.betsBefore.come.pending[0].amount + } + } + + // 1. Format pass line and odds details + formatted.passLine = '' + if (roll.betsBefore?.pass?.line) { + const line = `$${roll.betsBefore.pass.line.amount}` + const odds = roll.betsBefore.pass.odds ? `+$${roll.betsBefore.pass.odds.amount}` : '' + formatted.passLine = line + odds + } + + // 2. Format place 6,8 details + formatted.placeBets = '' + const placeBets = [] + if (roll.betsBefore?.place?.six) { + placeBets.push(`6:$${roll.betsBefore.place.six.amount}`) + } + if (roll.betsBefore?.place?.eight) { + placeBets.push(`8:$${roll.betsBefore.place.eight.amount}`) + } + if (placeBets.length > 0) { + formatted.placeBets = placeBets.join(' ') + } + + // 3. Format come bets (both established and pending) + formatted.comeBets = '' + const comeBetParts = [] + if (roll.betsBefore?.come?.points) { + const comePoints = Object.keys(roll.betsBefore.come.points).map(point => { + const bets = roll.betsBefore.come.points[point] + return bets.map(bet => { + const line = `${point}:$${bet.line.amount}` + const odds = bet.odds ? `+$${bet.odds.amount}` : '' + return line + odds + }).join(',') + }).join(' ') + if (comePoints) comeBetParts.push(comePoints) + } + if (roll.betsBefore?.come?.pending?.length) { + comeBetParts.push(`P:$${roll.betsBefore.come.pending[0].amount}`) + } + if (comeBetParts.length > 0) { + formatted.comeBets = comeBetParts.join(' ') + } + + // 5. Add money in play total + formatted.moneyInPlay = `$${moneyInPlay}` + + // 6. Add game state BEFORE the roll + formatted.beforeIsComeOut = beforeIsComeOut + formatted.beforePoint = beforePoint ?? '' + + // 7. Add roll details (combined dice display) + formatted.roll = `${roll.diceSum} (${roll.die1},${roll.die2})` + + // 8. Add result + formatted.result = roll.result + + // 9. Add game state AFTER the roll + formatted.afterIsComeOut = roll.isComeOut + formatted.afterPoint = roll.point ?? '' + + // 8. Calculate losses based on betsBefore and result + formatted.losses = '' + const losses = [] + let totalLosses = 0 + + if (roll.result === 'seven out') { + // Pass line + odds lost + if (roll.betsBefore?.pass?.line) { + const loss = roll.betsBefore.pass.line.amount + losses.push(`pass:$${loss}`) + totalLosses += loss + } + if (roll.betsBefore?.pass?.odds) { + const loss = roll.betsBefore.pass.odds.amount + losses.push(`pass-odds:$${loss}`) + totalLosses += loss + } + // All come bets lost + if (roll.betsBefore?.come?.points) { + Object.keys(roll.betsBefore.come.points).forEach(point => { + roll.betsBefore.come.points[point].forEach(bet => { + const lineLoss = bet.line.amount + losses.push(`come-${point}:$${lineLoss}`) + totalLosses += lineLoss + if (bet.odds) { + const oddsLoss = bet.odds.amount + losses.push(`come-${point}-odds:$${oddsLoss}`) + totalLosses += oddsLoss + } + }) + }) + } + // Place bets lost + if (roll.betsBefore?.place?.six) { + const loss = roll.betsBefore.place.six.amount + losses.push(`place-6:$${loss}`) + totalLosses += loss + } + if (roll.betsBefore?.place?.eight) { + const loss = roll.betsBefore.place.eight.amount + losses.push(`place-8:$${loss}`) + totalLosses += loss + } + } else if (roll.result === 'comeout loss') { + // Only pass line lost + if (roll.betsBefore?.pass?.line) { + const loss = roll.betsBefore.pass.line.amount + losses.push(`pass:$${loss}`) + totalLosses += loss + } + } else if (roll.betsBefore?.come?.pending?.length) { + // Check for immediate come bet losses (2, 3, 12) + if ([2, 3, 12].includes(roll.diceSum)) { + const loss = roll.betsBefore.come.pending[0].amount + losses.push(`come:$${loss}`) + totalLosses += loss + } + } + + if (losses.length > 0) { + formatted.losses = losses.join(', ') + } + + // 9. Track come bet movements (pending -> established on point) + formatted.movements = '' + if (roll.betsBefore?.come?.pending?.length) { + const pendingAmount = roll.betsBefore.come.pending[0].amount + // If dice sum is not an immediate win/loss, it establishes on that point + if (![7, 11, 2, 3, 12].includes(roll.diceSum)) { + formatted.movements = `come→${roll.diceSum}:$${pendingAmount}` + } + } + + // 10. Format payouts to show wins + formatted.wins = '' + let totalWins = 0 + if (Array.isArray(roll.payouts)) { + formatted.wins = roll.payouts.map(p => { + const winAmount = p.principal + p.profit + totalWins += winAmount + return `${p.type}:$${winAmount}` + }).join(', ') + } + + // 11. Calculate net change + formatted.netChange = `$${totalWins - totalLosses}` + + // Return with columns in desired order + return { + passLine: formatted.passLine, + comeBets: formatted.comeBets, + placeBets: formatted.placeBets, + moneyInPlay: formatted.moneyInPlay, + beforeIsComeOut: formatted.beforeIsComeOut, + beforePoint: formatted.beforePoint, + roll: formatted.roll, + result: formatted.result, + afterIsComeOut: formatted.afterIsComeOut, + afterPoint: formatted.afterPoint, + movements: formatted.movements, + wins: formatted.wins, + losses: formatted.losses, + netChange: formatted.netChange + } + }) + console.table(formattedHistory) }) } }