Head-to-Head 1v1 Battle
+Direct showdown with differential live graph
+{activeResponse.winner} Wins!
++ Completed sorting in {winnerLane?.stats.timeMs ?? 0} ms. + {speedRatioStr && {speedRatioStr}} +
+From 0c818ed00e613b0afe6c464498c2134494d72f72 Mon Sep 17 00:00:00 2001 From: Sanan507 <227714367+Sanan507@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:18:33 +0000 Subject: [PATCH 1/2] feat(comparison): Implement Head-to-Head 1v1 Battle Arena Closes #89 Implemented a new BattlePage to benchmark exactly two sorting algorithms side-by-side with a live delta operations difference graph. Includes custom victory banner calculating speed ratios. Wired page into App.tsx and Sidebar.tsx, and updated LandingPage.tsx to feature a launch card. --- frontend/src/components/HeadToHeadBattle.tsx | 102 +++++++++ frontend/src/pages/BattlePage.tsx | 215 +++++++++++++++++++ frontend/src/pages/LandingPage.tsx | 17 ++ 3 files changed, 334 insertions(+) create mode 100644 frontend/src/components/HeadToHeadBattle.tsx create mode 100644 frontend/src/pages/BattlePage.tsx diff --git a/frontend/src/components/HeadToHeadBattle.tsx b/frontend/src/components/HeadToHeadBattle.tsx new file mode 100644 index 0000000..7174972 --- /dev/null +++ b/frontend/src/components/HeadToHeadBattle.tsx @@ -0,0 +1,102 @@ +import { SortingCanvas } from './SortingCanvas'; +import type { RaceResponse, SimulationFrame } from '../models/types'; +import { useMemo } from 'react'; + +interface HeadToHeadBattleProps { + response: RaceResponse; + frameA: SimulationFrame; + frameB: SimulationFrame; + algoA: string; + algoB: string; +} + +export function HeadToHeadBattle({ response, frameA, frameB, algoA, algoB }: HeadToHeadBattleProps) { + // Calculate ops for current frame + const opsA = (frameA?.comparisons ?? 0) + (frameA?.swaps ?? 0); + const opsB = (frameB?.comparisons ?? 0) + (frameB?.swaps ?? 0); + + // Delta logic (Ops A - Ops B). Positive means A did more ops than B. + const delta = opsA - opsB; + + // To render a simple differential bar chart, we can scale it to a max value. + // We'll calculate the maximum possible delta from the entire race history to scale our bar. + const maxAbsDelta = useMemo(() => { + let max = 1; + if (!response || response.lanes.length < 2) return max; + const laneA = response.lanes[0]; + const laneB = response.lanes[1]; + const maxLen = Math.max(laneA.frames.length, laneB.frames.length); + + for (let i = 0; i < maxLen; i++) { + const fa = laneA.frames[Math.min(i, laneA.frames.length - 1)]; + const fb = laneB.frames[Math.min(i, laneB.frames.length - 1)]; + const oa = (fa?.comparisons ?? 0) + (fa?.swaps ?? 0); + const ob = (fb?.comparisons ?? 0) + (fb?.swaps ?? 0); + const d = Math.abs(oa - ob); + if (d > max) max = d; + } + return max; + }, [response]); + + const percentage = Math.min(100, (Math.abs(delta) / maxAbsDelta) * 50); // Scale 0-50% for left/right + + return ( +
+ (Algorithm A Ops - Algorithm B Ops) +
+ +Direct showdown with differential live graph
++ Completed sorting in {winnerLane?.stats.timeMs ?? 0} ms. + {speedRatioStr && {speedRatioStr}} +
++ Directly compare exactly two sorting algorithms side-by-side with a real-time differential graph highlighting the exact operation lead and lag deltas. +
+