From ecaf33841a7c99c9e389e5e2d0c1ddff175b8435 Mon Sep 17 00:00:00 2001 From: xWickz Date: Thu, 19 Feb 2026 09:57:28 -0400 Subject: [PATCH] feat: Add multiple language support with code samples for algorithms --- src/components/AlgoViz.tsx | 2 + src/components/CodePanel.tsx | 137 +++++++++++++++- src/i18n/translations.ts | 6 + src/lib/algorithms/backtracking.ts | 66 ++++++++ src/lib/algorithms/concepts.ts | 52 +++++++ src/lib/algorithms/data-structures.ts | 215 ++++++++++++++++++++++++++ src/lib/algorithms/graphs.ts | 66 ++++++++ src/lib/algorithms/sorting.ts | 35 +++++ src/lib/types.ts | 9 ++ 9 files changed, 584 insertions(+), 4 deletions(-) diff --git a/src/components/AlgoViz.tsx b/src/components/AlgoViz.tsx index 4f73293..155e14d 100644 --- a/src/components/AlgoViz.tsx +++ b/src/components/AlgoViz.tsx @@ -431,6 +431,7 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr {selectedAlgorithm ? ( import('@monaco-editor/react')) @@ -29,8 +29,25 @@ const DIFFICULTY_CONFIG: Record = { + javascript: { label: 'JavaScript', short: 'JS', monaco: 'javascript' }, + typescript: { label: 'TypeScript', short: 'TS', monaco: 'typescript' }, + python: { label: 'Python', short: 'Py', monaco: 'python' }, + cpp: { label: 'C++', short: 'C++', monaco: 'cpp' }, + java: { label: 'Java', short: 'Java', monaco: 'java' }, +} + +interface EnrichedSample { + language: CodeLanguage + label: string + shortLabel: string + monacoLanguage: string + code: string +} + interface CodePanelProps { code: string + codeSamples?: CodeSample[] description: string difficulty?: Difficulty currentLine?: number @@ -60,6 +77,7 @@ function defineTheme(monaco: Monaco) { export default function CodePanel({ code, + codeSamples, description, difficulty, currentLine, @@ -75,6 +93,56 @@ export default function CodePanel({ const editorRef = useRef(null) const monacoRef = useRef(null) const decorationsRef = useRef([]) + const samples = useMemo(() => { + const seen = new Set() + const enriched: EnrichedSample[] = [] + + const pushSample = (sample: { language: CodeLanguage; code: string; label?: string }) => { + if (seen.has(sample.language)) return + const preset = LANGUAGE_PRESETS[sample.language] + if (!preset) return + seen.add(sample.language) + enriched.push({ + language: sample.language, + label: sample.label ?? preset.label, + shortLabel: preset.short, + monacoLanguage: preset.monaco, + code: sample.code, + }) + } + + pushSample({ language: 'javascript', code, label: LANGUAGE_PRESETS.javascript.label }) + ;(codeSamples ?? []).forEach((sample) => pushSample(sample)) + + return enriched + }, [code, codeSamples]) + const [selectedLanguage, setSelectedLanguage] = useState('javascript') + + useEffect(() => { + if (samples.length === 0) return + if (!samples.some((s) => s.language === selectedLanguage)) { + setSelectedLanguage(samples[0].language) + } + }, [samples, selectedLanguage]) + + const activeSample = useMemo(() => { + if (samples.length === 0) { + const preset = LANGUAGE_PRESETS.javascript + return { + language: 'javascript' as CodeLanguage, + label: preset.label, + shortLabel: preset.short, + monacoLanguage: preset.monaco, + code, + } + } + return samples.find((s) => s.language === selectedLanguage) ?? samples[0] + }, [samples, selectedLanguage, code]) + + const editorLanguage = activeSample.monacoLanguage + const editorCode = activeSample.code + const hasMultipleLanguages = samples.length > 1 + const showLanguageWarning = hasMultipleLanguages && activeSample.language !== 'javascript' useEffect(() => { setIsMounted(true) @@ -223,6 +291,66 @@ export default function CodePanel({ id="tabpanel-code" aria-labelledby="tab-code" > + {hasMultipleLanguages && ( +
+
+ + {t.codeLanguageLabel} + + + {activeSample.label} + +
+
+ {samples.map((sample) => { + const isActive = sample.language === selectedLanguage + return ( + + ) + })} +
+ {showLanguageWarning && ( +
+ + {t.codeLanguageWarning} +
+ )} +
+ )}
- {/* Console output panel */} + {/* Console output panel asd */} {consoleOutput && consoleOutput.length > 0 && (
= { selectAlgorithmCode: 'Select an algorithm to view its code', expandCodePanel: 'Expand code panel', collapseCodePanel: 'Collapse code panel', + codeLanguageLabel: 'Language', + codeLanguageWarning: 'Step-by-step highlighting is only synced with JavaScript right now.', variables: 'Variables', speed: 'Speed', @@ -996,6 +1000,8 @@ The puzzle was invented by mathematician Édouard Lucas in 1883. Legend says mon selectAlgorithmCode: 'Selecciona un algoritmo para ver su código', expandCodePanel: 'Expandir panel de código', collapseCodePanel: 'Contraer panel de código', + codeLanguageLabel: 'Lenguaje', + codeLanguageWarning: 'El paso a paso solo se sincroniza con JavaScript por ahora.', variables: 'Variables', speed: 'Velocidad', diff --git a/src/lib/algorithms/backtracking.ts b/src/lib/algorithms/backtracking.ts index fe3d2b8..c183773 100644 --- a/src/lib/algorithms/backtracking.ts +++ b/src/lib/algorithms/backtracking.ts @@ -41,6 +41,72 @@ const nQueens: Algorithm = { solve(0); return board; }`, + codeSamples: [ + { + language: 'cpp', + code: `#include +#include + +bool solve(int row, int n, std::vector& board, + std::vector& cols, + std::vector& diag1, + std::vector& diag2) { + if (row == n) return true; + for (int col = 0; col < n; ++col) { + if (cols[col] || diag1[row + col] || diag2[row - col + n - 1]) continue; + board[row][col] = 'Q'; + cols[col] = diag1[row + col] = diag2[row - col + n - 1] = true; + if (solve(row + 1, n, board, cols, diag1, diag2)) return true; + cols[col] = diag1[row + col] = diag2[row - col + n - 1] = false; + board[row][col] = '.'; + } + return false; +} + +std::vector solveNQueens(int n) { + std::vector board(n, std::string(n, '.')); + std::vector cols(n, false); + std::vector diag1(2 * n - 1, false); + std::vector diag2(2 * n - 1, false); + solve(0, n, board, cols, diag1, diag2); + return board; +}`, + }, + { + language: 'java', + code: `import java.util.Arrays; + +class NQueensSolver { + static char[][] solveNQueens(int n) { + char[][] board = new char[n][n]; + for (char[] row : board) { + Arrays.fill(row, '.'); + } + boolean[] cols = new boolean[n]; + boolean[] diag1 = new boolean[2 * n - 1]; + boolean[] diag2 = new boolean[2 * n - 1]; + backtrack(0, n, board, cols, diag1, diag2); + return board; + } + + private static boolean backtrack(int row, int n, char[][] board, + boolean[] cols, + boolean[] diag1, + boolean[] diag2) { + if (row == n) return true; + for (int col = 0; col < n; col++) { + if (cols[col] || diag1[row + col] || diag2[row - col + n - 1]) continue; + board[row][col] = 'Q'; + cols[col] = diag1[row + col] = diag2[row - col + n - 1] = true; + if (backtrack(row + 1, n, board, cols, diag1, diag2)) return true; + board[row][col] = '.'; + cols[col] = diag1[row + col] = diag2[row - col + n - 1] = false; + } + return false; + } +}`, + }, + ], description: `N-Queens Problem The N-Queens problem asks: how can N chess queens be placed on an N×N chessboard so that no two queens threaten each other? diff --git a/src/lib/algorithms/concepts.ts b/src/lib/algorithms/concepts.ts index 4457998..ca40480 100644 --- a/src/lib/algorithms/concepts.ts +++ b/src/lib/algorithms/concepts.ts @@ -933,6 +933,58 @@ export const slidingWindow: Algorithm = { } return s.slice(bestStart, bestStart + best); }`, + codeSamples: [ + { + language: 'cpp', + code: `#include +#include + +std::string longestUniqueSubstring(const std::string& s) { + std::unordered_set window; + std::size_t start = 0, bestStart = 0, bestLen = 0; + + for (std::size_t end = 0; end < s.size(); ++end) { + while (window.count(s[end])) { + window.erase(s[start]); + ++start; + } + window.insert(s[end]); + if (end - start + 1 > bestLen) { + bestLen = end - start + 1; + bestStart = start; + } + } + + return s.substr(bestStart, bestLen); +}`, + }, + { + language: 'java', + code: `import java.util.HashSet; +import java.util.Set; + +class SlidingWindow { + static String longestUniqueSubstring(String s) { + Set window = new HashSet<>(); + int start = 0, bestStart = 0, bestLen = 0; + + for (int end = 0; end < s.length(); end++) { + while (window.contains(s.charAt(end))) { + window.remove(s.charAt(start)); + start++; + } + window.add(s.charAt(end)); + if (end - start + 1 > bestLen) { + bestLen = end - start + 1; + bestStart = start; + } + } + + return s.substring(bestStart, bestStart + bestLen); + } +}`, + }, + ], description: `Sliding Window Sliding Window maintains a dynamic range (window) over a sequence, expanding and contracting to solve substring/subarray problems efficiently. diff --git a/src/lib/algorithms/data-structures.ts b/src/lib/algorithms/data-structures.ts index 95eea38..0e66080 100644 --- a/src/lib/algorithms/data-structures.ts +++ b/src/lib/algorithms/data-structures.ts @@ -81,6 +81,128 @@ class LinkedList { } } }`, + codeSamples: [ + { + language: 'cpp', + code: `struct Node { + int value; + Node* next; + explicit Node(int v) : value(v), next(nullptr) {} +}; + +class LinkedList { + public: + void append(int value) { + Node* node = new Node(value); + if (!head) { + head = tail = node; + } else { + tail->next = node; + tail = node; + } + } + + void prepend(int value) { + Node* node = new Node(value); + node->next = head; + head = node; + if (!tail) tail = node; + } + + Node* search(int value) const { + Node* current = head; + while (current) { + if (current->value == value) return current; + current = current->next; + } + return nullptr; + } + + void remove(int value) { + if (!head) return; + if (head->value == value) { + Node* next = head->next; + delete head; + head = next; + if (!head) tail = nullptr; + return; + } + Node* current = head; + while (current->next) { + if (current->next->value == value) { + Node* victim = current->next; + current->next = victim->next; + if (victim == tail) tail = current; + delete victim; + return; + } + current = current->next; + } + } + + private: + Node* head = nullptr; + Node* tail = nullptr; +};`, + }, + { + language: 'java', + code: `class LinkedList { + static class Node { + int value; + Node next; + Node(int value) { this.value = value; } + } + + private Node head; + private Node tail; + + void append(int value) { + Node node = new Node(value); + if (head == null) { + head = tail = node; + } else { + tail.next = node; + tail = node; + } + } + + void prepend(int value) { + Node node = new Node(value); + node.next = head; + head = node; + if (tail == null) tail = node; + } + + Node search(int value) { + Node current = head; + while (current != null) { + if (current.value == value) return current; + current = current.next; + } + return null; + } + + void deleteValue(int value) { + if (head == null) return; + if (head.value == value) { + head = head.next; + if (head == null) tail = null; + return; + } + Node current = head; + while (current.next != null) { + if (current.next.value == value) { + current.next = current.next.next; + if (current.next == null) tail = current; + return; + } + current = current.next; + } + } +}`, + }, + ], description: `Linked List A Linked List is a linear data structure where each element (node) contains a value and a pointer (reference) to the next node. @@ -601,6 +723,99 @@ class BST { return null; } }`, + codeSamples: [ + { + language: 'cpp', + code: `struct Node { + int value; + Node* left; + Node* right; + explicit Node(int v) : value(v), left(nullptr), right(nullptr) {} +}; + +class BST { + public: + void insert(int value) { + if (!root) { + root = new Node(value); + return; + } + Node* current = root; + while (true) { + if (value < current->value) { + if (!current->left) { + current->left = new Node(value); + return; + } + current = current->left; + } else { + if (!current->right) { + current->right = new Node(value); + return; + } + current = current->right; + } + } + } + + Node* search(int value) const { + Node* current = root; + while (current) { + if (value == current->value) return current; + current = value < current->value ? current->left : current->right; + } + return nullptr; + } + + private: + Node* root = nullptr; +};`, + }, + { + language: 'java', + code: `class BinarySearchTree { + static class Node { + int value; + Node left, right; + Node(int value) { this.value = value; } + } + + private Node root; + + void insert(int value) { + if (root == null) { + root = new Node(value); + return; + } + Node current = root; + while (true) { + if (value < current.value) { + if (current.left == null) { + current.left = new Node(value); + return; + } + current = current.left; + } else { + if (current.right == null) { + current.right = new Node(value); + return; + } + current = current.right; + } + } + } + + Node search(int value) { + Node current = root; + while (current != null) { + if (value == current.value) return current; + current = value < current.value ? current.left : current.right; + } + return null; + } +}`, + }, + ], description: `Binary Search Tree (BST) A BST is a tree where each node has at most two children, and for every node: diff --git a/src/lib/algorithms/graphs.ts b/src/lib/algorithms/graphs.ts index d5f3c9d..b0581de 100644 --- a/src/lib/algorithms/graphs.ts +++ b/src/lib/algorithms/graphs.ts @@ -349,6 +349,72 @@ const dijkstra: Algorithm = { return dist; }`, + codeSamples: [ + { + language: 'cpp', + code: `#include + #include + #include + #include + +std::vector dijkstra(const std::vector>>& graph, int start) { + const int n = graph.size(); + const int INF = std::numeric_limits::max(); + std::vector dist(n, INF); + dist[start] = 0; + using Node = std::pair; // {distance, node} + std::priority_queue, std::greater> pq; + pq.push({0, start}); + + while (!pq.empty()) { + auto [d, u] = pq.top(); + pq.pop(); + if (d != dist[u]) continue; + + for (const auto& [v, w] : graph[u]) { + if (dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + pq.push({dist[v], v}); + } + } + } + + return dist; +}`, + }, + { + language: 'java', + code: `import java.util.*; + +public static int[] dijkstra(List> graph, int start) { + int n = graph.size(); + int[] dist = new int[n]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[start] = 0; + + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(edge -> edge[0])); + pq.offer(new int[] {0, start}); + + while (!pq.isEmpty()) { + int[] current = pq.poll(); + int d = current[0]; + int u = current[1]; + if (d != dist[u]) continue; + + for (int[] edge : graph.get(u)) { + int v = edge[0]; + int w = edge[1]; + if (dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + pq.offer(new int[] {dist[v], v}); + } + } + } + + return dist; +}`, + }, + ], description: `Dijkstra's Algorithm Dijkstra's Algorithm finds the shortest path from a source node to all other nodes in a weighted graph with non-negative edge weights. diff --git a/src/lib/algorithms/sorting.ts b/src/lib/algorithms/sorting.ts index 4d1ffcd..b9f9404 100644 --- a/src/lib/algorithms/sorting.ts +++ b/src/lib/algorithms/sorting.ts @@ -24,6 +24,41 @@ const bubbleSort: Algorithm = { return array; }`, + codeSamples: [ + { + language: 'cpp', + code: `#include +#include + +std::vector bubbleSort(std::vector nums) { + const int n = nums.size(); + for (int i = 0; i < n - 1; ++i) { + for (int j = 0; j < n - i - 1; ++j) { + if (nums[j] > nums[j + 1]) { + std::swap(nums[j], nums[j + 1]); + } + } + } + return nums; +}`, + }, + { + language: 'java', + code: `public static int[] bubbleSort(int[] arr) { + int n = arr.length; + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < n - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } + return arr; +}`, + }, + ], description: `Bubble Sort Bubble Sort is a simple comparison-based sorting algorithm. It repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. diff --git a/src/lib/types.ts b/src/lib/types.ts index b647820..465c109 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,5 +1,13 @@ export type VisualizationType = 'array' | 'graph' | 'matrix' | 'concept' +export type CodeLanguage = 'javascript' | 'typescript' | 'python' | 'cpp' | 'java' + +export interface CodeSample { + language: CodeLanguage + label?: string + code: string +} + export type HighlightType = | 'comparing' | 'swapped' @@ -213,6 +221,7 @@ export interface Algorithm { difficulty: Difficulty description: string code: string + codeSamples?: CodeSample[] visualization: VisualizationType generateSteps: (locale?: string) => Step[] }