All files / web/src/utils beadDiff.ts

100% Statements 115/115
100% Branches 12/12
100% Functions 3/3
100% Lines 115/115

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 1161x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 7x 7x 7x 7x 7x 3x 3x 7x 1x 1x 8x 8x 8x 8x 8x 8x 8x 1x 1x 8x 8x 8x 8x 8x 23x 23x 7x 7x 23x 23x 23x 8x 8x 8x  
// Re-export core bead diff functionality from abacus-react
// App-specific extensions for multi-step tutorials and validation
 
export {
  type BeadDiffResult,
  type BeadDiffOutput,
  calculateBeadDiff,
  calculateBeadDiffFromValues,
  areStatesEqual,
  type AbacusState,
  type BeadState,
} from '@soroban/abacus-react'
 
import type { BeadDiffOutput, BeadDiffResult, AbacusState } from '@soroban/abacus-react'
import { calculateBeadDiffFromValues } from '@soroban/abacus-react'
 
// Local type for use in filter callbacks when BeadDiffResult can't resolve from package
type BeadChange = {
  placeValue: number
  beadType: 'heaven' | 'earth'
  position?: number
  direction: 'activate' | 'deactivate'
  order: number
}
 
/** A single step in a multi-step bead diff sequence */
type StepDiff = {
  stepIndex: number
  instruction: string
  diff: BeadDiffOutput
  fromValue: number
  toValue: number
}
 
/**
 * Calculate step-by-step bead diffs for multi-step operations
 * This is used for tutorial multi-step instructions where we want to show
 * the progression through intermediate states
 *
 * APP-SPECIFIC FUNCTION - not in core abacus-react
 */
export function calculateMultiStepBeadDiffs(
  startValue: number,
  steps: Array<{ expectedValue: number; instruction: string }>
): StepDiff[] {
  const stepDiffs: StepDiff[] = []
  let currentValue = startValue
 
  steps.forEach((step, index) => {
    const diff = calculateBeadDiffFromValues(currentValue, step.expectedValue)
 
    stepDiffs.push({
      stepIndex: index,
      instruction: step.instruction,
      diff,
      fromValue: currentValue,
      toValue: step.expectedValue,
    })
 
    currentValue = step.expectedValue
  })
 
  return stepDiffs
}
 
/**
 * Validate that a bead diff is feasible (no impossible bead states)
 *
 * APP-SPECIFIC FUNCTION - not in core abacus-react
 */
export function validateBeadDiff(diff: BeadDiffOutput): {
  isValid: boolean
  errors: string[]
} {
  const errors: string[] = []
 
  // Check for impossible earth bead counts
  const earthChanges = diff.changes.filter((c: BeadChange) => c.beadType === 'earth')
  const earthByPlace = groupByPlace(earthChanges)
 
  Object.entries(earthByPlace).forEach(([place, changes]) => {
    const activations = changes.filter((c) => c.direction === 'activate').length
    const deactivations = changes.filter((c) => c.direction === 'deactivate').length
    const netChange = activations - deactivations
 
    if (netChange > 4) {
      errors.push(`Place ${place}: Cannot have more than 4 earth beads`)
    }
    if (netChange < 0) {
      errors.push(`Place ${place}: Cannot have negative earth beads`)
    }
  })
 
  return {
    isValid: errors.length === 0,
    errors,
  }
}
 
// Helper function for validation
function groupByPlace(changes: BeadDiffResult[]): {
  [place: string]: BeadDiffResult[]
} {
  return changes.reduce(
    (groups, change) => {
      const place = change.placeValue.toString()
      if (!groups[place]) {
        groups[place] = []
      }
      groups[place].push(change)
      return groups
    },
    {} as { [place: string]: BeadDiffResult[] }
  )
}