using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using NAudio.Wave; namespace SortVisualizer; public class VisualizerForm : Form { int[] array = Array.Empty(); Random rand = new(); int delay = 10; bool sorting = false; ComboBox algoSelector; Button startButton, stopButton, shuffleButton; TrackBar speedSlider; CheckBox soundToggle; List algorithms; CancellationTokenSource? cts; // Audio BufferedWaveProvider? waveProvider; WaveOutEvent? waveOut; int highlightedIndex1 = -1; int highlightedIndex2 = -1; public VisualizerForm() { Text = "C# Sorting"; DoubleBuffered = true; MinimumSize = new Size(800, 500); Width = 1000; Height = 600; algorithms = new() { new BubbleSort(), new InsertionSort(), new BogoSort(), new MergeSort(), new StoogeSort(), new RadixSort(), new BeadSort() }; var topPanel = new FlowLayoutPanel { Dock = DockStyle.Top, AutoSize = true, WrapContents = true, FlowDirection = FlowDirection.LeftToRight, Padding = new Padding(5), AutoScroll = true }; algoSelector = new ComboBox { Width = 160, DropDownStyle = ComboBoxStyle.DropDownList }; algoSelector.DataSource = algorithms; algoSelector.DisplayMember = "Name"; int buttonHeight = algoSelector.Height; startButton = new Button { Width = 100, Height = buttonHeight, Text = "Start" }; stopButton = new Button { Width = 100, Height = buttonHeight, Text = "Stop" }; shuffleButton = new Button { Width = 100, Height = buttonHeight, Text = "Shuffle" }; soundToggle = new CheckBox { Width = 100, Height = buttonHeight, Text = "Sound On", Checked = true }; speedSlider = new TrackBar { Width = 250, Minimum = 1, Maximum = 100, Value = 10, TickFrequency = 10, SmallChange = 1, LargeChange = 10, Height = buttonHeight }; topPanel.Controls.AddRange(new Control[] { algoSelector, startButton, stopButton, shuffleButton, soundToggle, speedSlider }); Controls.Add(topPanel); speedSlider.ValueChanged += (s, e) => { delay = 101 - speedSlider.Value; }; startButton.Click += async (s, e) => await StartSort(); stopButton.Click += (s, e) => StopSort(); shuffleButton.Click += (s, e) => Shuffle(); this.Resize += (s, e) => Invalidate(); Shuffle(); } void Shuffle() { if (sorting) return; array = Enumerable.Range(1, 100).OrderBy(_ => rand.Next()).ToArray(); highlightedIndex1 = highlightedIndex2 = -1; Invalidate(); } async Task StartSort() { if (sorting) return; sorting = true; cts = new CancellationTokenSource(); var algo = algoSelector.SelectedItem as ISortAlgorithm; if (algo == null) { sorting = false; return; } try { if (soundToggle.Checked) { InitializeAudio(); if (waveProvider != null) { _ = Task.Run(() => AudioLoopAsync(waveProvider, cts.Token)); } } await algo.Sort( array, () => Invalidate(), delay, value => { highlightedIndex1 = value; }, cts.Token ); highlightedIndex1 = highlightedIndex2 = -1; Invalidate(); } finally { sorting = false; StopAudio(); } } void StopSort() { if (!sorting) return; cts?.Cancel(); sorting = false; highlightedIndex1 = highlightedIndex2 = -1; Invalidate(); StopAudio(); } void InitializeAudio() { waveProvider = new BufferedWaveProvider(new WaveFormat(44100, 16, 1)) { BufferDuration = TimeSpan.FromSeconds(5) }; waveOut = new WaveOutEvent(); waveOut.Init(waveProvider); waveOut.Play(); } void StopAudio() { waveOut?.Stop(); waveOut?.Dispose(); waveOut = null; waveProvider = null; } async Task AudioLoopAsync(BufferedWaveProvider provider, CancellationToken token) { short amplitude = 8000; int sampleRate = 44100; int chunkSamples = 512; var buffer = new byte[chunkSamples * 2]; double phase = 0; while (!token.IsCancellationRequested) { // Calculate frequency based on highlighted element or average int freq = 220; if (highlightedIndex1 >= 0 && highlightedIndex1 < array.Length) { freq = 220 + array[highlightedIndex1] * 15; // Rising pitch based on value } else if (array.Length > 0) { freq = 220 + (int)array.Average() * 15; } for (int n = 0; n < chunkSamples; n++) { double sample = Math.Sin(phase); phase += 2 * Math.PI * freq / sampleRate; // Keep phase in reasonable range if (phase > 2 * Math.PI * 1000) phase -= 2 * Math.PI * 1000; short s = (short)(sample * amplitude); buffer[n * 2] = (byte)(s & 0xFF); buffer[n * 2 + 1] = (byte)((s >> 8) & 0xFF); } int space = provider.BufferLength - provider.BufferedBytes; if (space >= buffer.Length) { provider.AddSamples(buffer, 0, buffer.Length); } else if (space > 0) { provider.AddSamples(buffer, buffer.Length - space, space); } await Task.Delay(1, token); } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); var g = e.Graphics; int offsetY = 50; int marginX = 20; int paddingTop = 5; int paddingBottom = 5; if (array.Length == 0) return; int barWidth = (ClientSize.Width - 2 * marginX) / array.Length; int top = offsetY + paddingTop; int bottom = ClientSize.Height - paddingBottom; int availableHeight = bottom - top; int maxVal = array.Max(); for (int i = 0; i < array.Length; i++) { int height = (int)Math.Round(array[i] * (availableHeight / (float)maxVal)); Brush brush = Brushes.LightGreen; if (sorting) { if (i == highlightedIndex1) brush = Brushes.Red; else if (i == highlightedIndex2) brush = Brushes.Orange; else brush = Brushes.DeepSkyBlue; } int y = bottom - height; if (y < top) y = top; g.FillRectangle(brush, marginX + i * barWidth, y, barWidth - 1, height); } } }