package com.example import kotlin.math.* import androidx.compose.ui.geometry.Offset object BoardPhysics { // Carrom board bounds (scaled coordinate system, 0 to 400) const val BOARD_SIZE = 400f const val COLLISION_RADIUS = 15f // Puck and striker visual radius is 15f const val POCKET_RADIUS = 25f val POCKETS = listOf( Offset(30f, 30f), // Top-Left Offset(370f, 30f), // Top-Right Offset(30f, 370f), // Bottom-Left Offset(370f, 370f) // Bottom-Right ) // A class representing a ray step (from start to end offset, and type) data class RaySegment( val start: Offset, val end: Offset, val colorType: String // "striker", "target", "rebound" ) // Calculate reflection lines (returns list of segments) fun calculateReboundLines( startPoint: Offset, angleRad: Float, maxBounces: Int = 3 ): List { val path = mutableListOf() path.add(startPoint) var currentStart = startPoint var currentAngle = angleRad val padding = COLLISION_RADIUS for (bounce in 0 until maxBounces) { val vx = cos(currentAngle) val vy = sin(currentAngle) if (abs(vx) < 0.0001f && abs(vy) < 0.0001f) break // Solve for intersection with board boundaries [padding, BOARD_SIZE - padding] val tLeft = if (vx < 0) (padding - currentStart.x) / vx else Float.MAX_VALUE val tRight = if (vx > 0) ((BOARD_SIZE - padding) - currentStart.x) / vx else Float.MAX_VALUE val tTop = if (vy < 0) (padding - currentStart.y) / vy else Float.MAX_VALUE val tBottom = if (vy > 0) ((BOARD_SIZE - padding) - currentStart.y) / vy else Float.MAX_VALUE val t = minOf(tLeft, tRight, tTop, tBottom) if (t == Float.MAX_VALUE || t <= 0.01f) { break } // Calculation of the bounce point val bouncePoint = Offset( currentStart.x + vx * t, currentStart.y + vy * t ) path.add(bouncePoint) // Calculate new angle based on which wall was hit currentAngle = when { t == tLeft || t == tRight -> { // Vertical wall reflection -> reverse delta X atan2(vy, -vx) } else -> { // Horizontal wall reflection -> reverse delta Y atan2(-vy, vx) } } currentStart = bouncePoint } return path } // Helper to calculate circle intersection fun findRayCircleIntersection( rayStart: Offset, angleRad: Float, circleCenter: Offset, radius: Float ): Float? { val vx = cos(angleRad) val vy = sin(angleRad) val dx = rayStart.x - circleCenter.x val dy = rayStart.y - circleCenter.y // Coefficients of quadratic equation (ax^2 + bx + c = 0), a = vx^2 + vy^2 = 1 val b = 2 * (vx * dx + vy * dy) val c = dx * dx + dy * dy - radius * radius val discriminant = b * b - 4 * c if (discriminant < 0) return null val t1 = (-b - sqrt(discriminant)) / 2 val t2 = (-b + sqrt(discriminant)) / 2 val validT = mutableListOf() if (t1 > 0.01f) validT.add(t1) if (t2 > 0.01f) validT.add(t2) return if (validT.isNotEmpty()) validT.minOrNull() else null } // Surgical Accuracy: Calculates the exact contact point where the striker must impact the puck // to drive it straight into targetPocket fun getAimAssistImpactVector( strikerPos: Offset, puckPos: Offset, targetPocket: Offset ): Offset { // Line from targetPocket through puckPos, extended back by (2 * COLLISION_RADIUS) val dx = puckPos.x - targetPocket.x val dy = puckPos.y - targetPocket.y val dist = sqrt(dx * dx + dy * dy) val ux = dx / dist val uy = dy / dist // Impact point for striker centers (double radius of collision radius) return Offset( puckPos.x + ux * (2 * COLLISION_RADIUS), puckPos.y + uy * (2 * COLLISION_RADIUS) ) } // Check if striker shot hits target puck fun testShotSimulation( strikerStart: Offset, aimAngleRad: Float, puckStart: Offset ): Boolean { val t = findRayCircleIntersection( strikerStart, aimAngleRad, puckStart, 2 * COLLISION_RADIUS ) return t != null } }