From 2aca34fb9c50d3baa028b061bcd1dc93483ca5ba Mon Sep 17 00:00:00 2001 From: Lobo Date: Wed, 4 Feb 2026 08:43:08 +0100 Subject: [PATCH 1/5] Update package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cbbb234..c110d85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2187,9 +2187,9 @@ } }, "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", "dev": true, "license": "MIT", "dependencies": { From cbc46cf85867cadf76637158d59fe26d79e09698 Mon Sep 17 00:00:00 2001 From: Lobo Date: Wed, 4 Feb 2026 09:22:05 +0100 Subject: [PATCH 2/5] initial implementation of sorting algorithms --- GEMINI.md | 79 +++++++++++++++ src/app/app.routes.ts | 1 + src/app/constants/RouterConstants.ts | 7 ++ .../algorithms/service/algorithms.service.ts | 6 ++ .../sorting/service/sorting.service.ts | 97 +++++++++++++++++++ .../algorithms/sorting/sorting.component.html | 44 +++++++++ .../algorithms/sorting/sorting.component.scss | 62 ++++++++++++ .../algorithms/sorting/sorting.component.ts | 92 ++++++++++++++++++ .../algorithms/sorting/sorting.models.ts | 4 + src/assets/i18n/de.json | 9 ++ src/assets/i18n/en.json | 9 ++ 11 files changed, 410 insertions(+) create mode 100644 GEMINI.md create mode 100644 src/app/pages/algorithms/sorting/service/sorting.service.ts create mode 100644 src/app/pages/algorithms/sorting/sorting.component.html create mode 100644 src/app/pages/algorithms/sorting/sorting.component.scss create mode 100644 src/app/pages/algorithms/sorting/sorting.component.ts create mode 100644 src/app/pages/algorithms/sorting/sorting.models.ts diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..5ef08da --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,79 @@ +# Gemini CLI Context for playground-frontend + +## Project Overview + +This is the frontend of the Playground project, built with Angular 21 and Angular Material. It includes features like a light/dark theme toggle and multi-language support via ngx-translate. The application is a static Single Page Application (SPA) served by NGINX. + +**Key Technologies:** +* **Frontend Framework:** Angular 21 +* **UI Components & Theming:** Angular Material +* **Internationalization:** ngx-translate +* **Server:** NGINX (for serving the SPA) +* **Containerization:** Docker +* **CI/CD:** GitHub Actions + +## Building and Running + +### Local Development + +1. **Install dependencies:** + ```bash + npm install + ``` +2. **Start development server:** + ```bash + ng serve --open + ``` + The app will run at `http://localhost:4200`. + +### Building for Production + +To build the project for production, which creates the optimized static files: +```bash +ng build +``` + +### Running Tests + +To run the unit tests: +```bash +ng test +``` + +### Linting + +To lint the codebase: +```bash +ng lint +``` + +### Docker + +To build and run the application using Docker locally: + +1. **Build Docker image:** + ```bash + docker build -t playground-frontend:local . + ``` +2. **Run Docker container:** + ```bash + docker run -p 8080:80 playground-frontend:local + ``` + Then open `http://localhost:8080` in your browser. + +## Development Conventions + +* **Language:** TypeScript +* **Framework:** Angular +* **Styling:** SCSS (based on `styles.scss` and component-specific `.scss` files). +* **Linting:** ESLint is configured (see `eslint.config.js` and `package.json` scripts). +* **Internationalization:** Uses `ngx-translate` with `en.json` and `de.json` asset files. + +## Project Structure (Key Areas) + +* `src/app/`: Contains the main application logic, components, services, and routing. +* `src/app/pages/`: Specific pages of the application (e.g., about, algorithms, imprint, projects). +* `src/assets/`: Static assets including images, internationalization files (`i18n`), and logos. +* `Dockerfile`: Defines the Docker image for the application. +* `nginx.conf`: NGINX configuration for serving the SPA. +* `.gitea/workflows/`: Contains CI/CD workflows (e.g., `build-Frontend-a.yml`). diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index c416983..55aa00e 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -8,6 +8,7 @@ export const routes: Routes = [ { path: RouterConstants.PROJECTS.PATH, component: RouterConstants.PROJECTS.COMPONENT}, { path: RouterConstants.ALGORITHMS.PATH, component: RouterConstants.ALGORITHMS.COMPONENT}, { path: RouterConstants.PATHFINDING.PATH, component: RouterConstants.PATHFINDING.COMPONENT}, + { path: RouterConstants.SORTING.PATH, component: RouterConstants.SORTING.COMPONENT}, { path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT}, ]; diff --git a/src/app/constants/RouterConstants.ts b/src/app/constants/RouterConstants.ts index 50e2d6d..ce20694 100644 --- a/src/app/constants/RouterConstants.ts +++ b/src/app/constants/RouterConstants.ts @@ -3,6 +3,7 @@ import {ProjectsComponent} from '../pages/projects/projects.component'; import {ImprintComponent} from '../pages/imprint/imprint.component'; import {AlgorithmsComponent} from '../pages/algorithms/algorithms.component'; import {PathfindingComponent} from '../pages/algorithms/pathfinding/pathfinding.component'; +import {SortingComponent} from '../pages/algorithms/sorting/sorting.component'; export class RouterConstants { @@ -30,6 +31,12 @@ export class RouterConstants { COMPONENT: PathfindingComponent }; + static readonly SORTING = { + PATH: 'algorithms/sorting', + LINK: '/algorithms/sorting', + COMPONENT: SortingComponent + }; + static readonly IMPRINT = { PATH: 'imprint', LINK: '/imprint', diff --git a/src/app/pages/algorithms/service/algorithms.service.ts b/src/app/pages/algorithms/service/algorithms.service.ts index a317759..6fd5b0c 100644 --- a/src/app/pages/algorithms/service/algorithms.service.ts +++ b/src/app/pages/algorithms/service/algorithms.service.ts @@ -14,6 +14,12 @@ export class AlgorithmsService { title: 'ALGORITHM.PATHFINDING.TITLE', description: 'ALGORITHM.PATHFINDING.DESCRIPTION', routerLink: RouterConstants.PATHFINDING.LINK + }, + { + id: 'sorting', + title: 'ALGORITHM.SORTING.TITLE', + description: 'ALGORITHM.SORTING.DESCRIPTION', + routerLink: RouterConstants.SORTING.LINK } ]; diff --git a/src/app/pages/algorithms/sorting/service/sorting.service.ts b/src/app/pages/algorithms/sorting/service/sorting.service.ts new file mode 100644 index 0000000..bbad7b1 --- /dev/null +++ b/src/app/pages/algorithms/sorting/service/sorting.service.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@angular/core'; +import { SortData } from '../sorting.models'; + +@Injectable({ + providedIn: 'root' +}) +export class SortingService { + + constructor() { } + + async bubbleSort(array: SortData[], animationSpeed: number): Promise { + console.log(animationSpeed); + const n = array.length; + for (let i = 0; i < n - 1; i++) { + console.log("1"); + for (let j = 0; j < n - i - 1; j++) { + console.log("2"); + array[j].state = 'comparing'; + array[j + 1].state = 'comparing'; + await this.delay(animationSpeed); + + if (array[j].value > array[j + 1].value) { + // Swap elements + const temp = array[j].value; + array[j].value = array[j + 1].value; + array[j + 1].value = temp; + } + + array[j].state = 'unsorted'; + array[j + 1].state = 'unsorted'; + } + array[n - 1 - i].state = 'sorted'; // Mark the largest element as sorted + } + array[0].state = 'sorted'; // Mark the last element as sorted (if n > 0) + } + + async selectionSort(array: SortData[], animationSpeed: number): Promise { + const n = array.length; + for (let i = 0; i < n - 1; i++) { + let minIdx = i; + array[i].state = 'comparing'; + for (let j = i + 1; j < n; j++) { + array[j].state = 'comparing'; + await this.delay(animationSpeed); + + if (array[j].value < array[minIdx].value) { + minIdx = j; + } + if (j !== minIdx) { // Only reset if it wasn't the minimum + array[j].state = 'unsorted'; + } + } + if (minIdx !== i) { + // Swap elements + const temp = array[i].value; + array[i].value = array[minIdx].value; + array[minIdx].value = temp; + } + array[i].state = 'sorted'; // Mark the current element as sorted + if (minIdx !== i) { + array[minIdx].state = 'unsorted'; + } + } + array[n - 1].state = 'sorted'; // Mark the last element as sorted + } + + async insertionSort(array: SortData[], animationSpeed: number): Promise { + const n = array.length; + for (let i = 1; i < n; i++) { + const key = array[i].value; + array[i].state = 'comparing'; + let j = i - 1; + + while (j >= 0 && array[j].value > key) { + array[j].state = 'comparing'; + await this.delay(animationSpeed); + + array[j + 1].value = array[j].value; + array[j + 1].state = 'unsorted'; // Reset after shifting + j = j - 1; + } + await this.delay(animationSpeed); // Delay after loop for final position + array[j + 1].value = key; + array[i].state = 'unsorted'; // Reset original 'key' position + array.forEach((item, idx) => { // Mark sorted up to i + if (idx <= i) { + item.state = 'sorted'; + } + }); + } + array.forEach(item => item.state = 'sorted'); // Mark all as sorted at the end + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html new file mode 100644 index 0000000..8133128 --- /dev/null +++ b/src/app/pages/algorithms/sorting/sorting.component.html @@ -0,0 +1,44 @@ +
+ + + {{ 'ALGORITHM.SORTING.TITLE' | translate }} + + +
+ + {{ 'ALGORITHM.SORTING.ALGORITHM' | translate }} + + @for (algo of availableAlgorithms; track algo.value) { + {{ algo.name }} + } + + + + + + +
+ +
+

{{ 'ALGORITHM.SORTING.EXECUTION_TIME' | translate }}: {{ executionTime }} ms

+
+
+ @for (item of sortArray; track $index) { +
+ } +
+
+
+
diff --git a/src/app/pages/algorithms/sorting/sorting.component.scss b/src/app/pages/algorithms/sorting/sorting.component.scss new file mode 100644 index 0000000..128fcaf --- /dev/null +++ b/src/app/pages/algorithms/sorting/sorting.component.scss @@ -0,0 +1,62 @@ +.sorting-container { + display: flex; + justify-content: center; + align-items: flex-start; + padding: 20px; + height: 100%; + box-sizing: border-box; + + .sorting-card { + width: 100%; + max-width: 1200px; + padding: 20px; + + .controls-panel { + display: flex; + gap: 10px; + margin-bottom: 20px; + align-items: center; + flex-wrap: wrap; + + mat-form-field { + width: 200px; + } + } + + .visualization-area { + display: flex; + align-items: flex-end; + height: 300px; /* Max height for bars */ + border-bottom: 1px solid #ccc; + margin-bottom: 20px; + gap: 1px; + background-color: #f0f0f0; + + .bar { + flex-grow: 1; + background-color: #424242; /* Default unsorted color */ + transition: height 0.1s ease-in-out, background-color 0.1s ease-in-out; + width: 10px; /* Default width, flex-grow will adjust */ + min-width: 1px; /* Ensure bars are always visible */ + + &.unsorted { + background-color: #424242; + } + + &.comparing { + background-color: #ffeb3b; /* Yellow for comparing */ + } + + &.sorted { + background-color: #4caf50; /* Green for sorted */ + } + } + } + + .info-panel { + margin-top: 10px; + font-size: 0.9em; + color: #555; + } + } +} diff --git a/src/app/pages/algorithms/sorting/sorting.component.ts b/src/app/pages/algorithms/sorting/sorting.component.ts new file mode 100644 index 0000000..43bd720 --- /dev/null +++ b/src/app/pages/algorithms/sorting/sorting.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {MatCardModule} from "@angular/material/card"; +import {MatFormFieldModule} from "@angular/material/form-field"; +import {MatSelectModule} from "@angular/material/select"; +import {MatButtonModule} from "@angular/material/button"; +import {MatIconModule} from "@angular/material/icon"; +import {TranslateModule} from "@ngx-translate/core"; +import { SortingService } from './service/sorting.service'; +import { SortData } from './sorting.models'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-sorting', + standalone: true, + imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule], + templateUrl: './sorting.component.html', + styleUrls: ['./sorting.component.scss'] +}) +export class SortingComponent implements OnInit { + + sortArray: SortData[] = []; + arraySize: number = 50; + maxArrayValue: number = 100; + animationSpeed: number = 10; // Milliseconds per step + + // Placeholder for available sorting algorithms + availableAlgorithms: { name: string; value: string }[] = [ + { name: 'Bubble Sort', value: 'bubbleSort' }, + { name: 'Selection Sort', value: 'selectionSort' }, + { name: 'Insertion Sort', value: 'insertionSort' }, + ]; + selectedAlgorithm: string = this.availableAlgorithms[0].value; + isSorting: boolean = false; + executionTime: number = 0; + + constructor(private sortingService: SortingService) { } + + ngOnInit(): void { + this.generateNewArray(); + } + + generateNewArray(): void { + this.isSorting = false; + this.executionTime = 0; + this.sortArray = []; + for (let i = 0; i < this.arraySize; i++) { + this.sortArray.push({ + value: Math.floor(Math.random() * this.maxArrayValue) + 1, + state: 'unsorted' + }); + } + } + + async startSorting(): Promise { + if (this.isSorting) { + return; + } + console.log('Starting sorting...'); + this.isSorting = true; + this.executionTime = 0; + + // Reset states for visualization + this.sortArray.forEach(item => item.state = 'unsorted'); + const startTime = performance.now(); + console.log("Select algorithm ", this.selectedAlgorithm); + + switch (this.selectedAlgorithm) { + case 'bubbleSort': + await this.sortingService.bubbleSort(this.sortArray, this.animationSpeed); + break; + case 'selectionSort': + await this.sortingService.selectionSort(this.sortArray, this.animationSpeed); + break; + case 'insertionSort': + await this.sortingService.insertionSort(this.sortArray, this.animationSpeed); + break; + default: + console.warn('Unknown sorting algorithm selected:', this.selectedAlgorithm); + break; + } + console.log("Done with sorting..."); + const endTime = performance.now(); + this.executionTime = Math.round(endTime - startTime); + this.isSorting = false; + this.sortArray.forEach(item => item.state = 'sorted'); // Mark all as sorted after completion + } + + resetSorting(): void { + this.generateNewArray(); + } +} diff --git a/src/app/pages/algorithms/sorting/sorting.models.ts b/src/app/pages/algorithms/sorting/sorting.models.ts new file mode 100644 index 0000000..2741353 --- /dev/null +++ b/src/app/pages/algorithms/sorting/sorting.models.ts @@ -0,0 +1,4 @@ +export interface SortData { + value: number; + state: 'sorted' | 'comparing' | 'unsorted'; +} diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index d30988a..1d0e778 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -326,6 +326,15 @@ "DESCRIPTION": "Vergleich von Dijkstra vs. A*.", "GRID_HEIGHT": "Höhe", "GRID_WIDTH": "Beite" + }, + "SORTING": { + "TITLE": "Sortieralgorithmen", + "DESCRIPTION": "Visualisierung verschiedener Sortieralgorithmen.", + "ALGORITHM": "Algorithmus", + "START": "Sortierung starten", + "RESET": "Zurücksetzen", + "GENERATE_NEW_ARRAY": "Neues Array generieren", + "EXECUTION_TIME": "Ausführungszeit" } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 71e8757..15c429a 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -326,6 +326,15 @@ "DESCRIPTION": "Comparing of Dijkstra vs. A*.", "GRID_HEIGHT": "Height", "GRID_WIDTH": "Width" + }, + "SORTING": { + "TITLE": "Sorting Algorithms", + "DESCRIPTION": "Visualizing various sorting algorithms.", + "ALGORITHM": "Algorithm", + "START": "Start Sorting", + "RESET": "Reset", + "GENERATE_NEW_ARRAY": "Generate New Array", + "EXECUTION_TIME": "Execution Time" } } } From 450ab0b8370468fa9ac2517d421d706510a9b021 Mon Sep 17 00:00:00 2001 From: Lobo Date: Wed, 4 Feb 2026 11:37:11 +0100 Subject: [PATCH 3/5] Implement snapshot-based sorting visualizer Refactor sorting to produce and consume SortSnapshot sequences for visualization. SortingService now creates immutable snapshots and implements bubble, quick and heap sorts (with helper methods) instead of performing UI delays; a swap/heapify/partition flow records state changes. SortingComponent was updated to animate snapshots (with start/stop timeout handling), added array size input and controls, stores an unsorted copy for resets, and uses ChangeDetectorRef for updates. Minor UI tweaks: faster bar transitions, info color, updated default array size and animation speed, and added i18n keys for ARRAY_SIZE (en/de). --- .../sorting/service/sorting.service.ts | 221 ++++++++++++------ .../algorithms/sorting/sorting.component.html | 22 +- .../algorithms/sorting/sorting.component.scss | 4 +- .../algorithms/sorting/sorting.component.ts | 116 ++++++--- .../algorithms/sorting/sorting.models.ts | 4 + src/assets/i18n/de.json | 3 +- src/assets/i18n/en.json | 3 +- 7 files changed, 261 insertions(+), 112 deletions(-) diff --git a/src/app/pages/algorithms/sorting/service/sorting.service.ts b/src/app/pages/algorithms/sorting/service/sorting.service.ts index bbad7b1..d0972d2 100644 --- a/src/app/pages/algorithms/sorting/service/sorting.service.ts +++ b/src/app/pages/algorithms/sorting/service/sorting.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { SortData } from '../sorting.models'; +import {SortData, SortSnapshot} from '../sorting.models'; @Injectable({ providedIn: 'root' @@ -8,90 +8,171 @@ export class SortingService { constructor() { } - async bubbleSort(array: SortData[], animationSpeed: number): Promise { - console.log(animationSpeed); - const n = array.length; + private createSnapshot(array: SortData[]): SortSnapshot { + return { + array: array.map(item => ({ ...item })) + }; + } + + bubbleSort(array: SortData[]): SortSnapshot[] { + const snapshots: SortSnapshot[] = []; + const arr = array.map(item => ({ ...item })); + const n = arr.length; + + snapshots.push(this.createSnapshot(arr)); + for (let i = 0; i < n - 1; i++) { - console.log("1"); for (let j = 0; j < n - i - 1; j++) { - console.log("2"); - array[j].state = 'comparing'; - array[j + 1].state = 'comparing'; - await this.delay(animationSpeed); + arr[j].state = 'comparing'; + arr[j + 1].state = 'comparing'; + snapshots.push(this.createSnapshot(arr)); // Snapshot Vergleich - if (array[j].value > array[j + 1].value) { - // Swap elements - const temp = array[j].value; - array[j].value = array[j + 1].value; - array[j + 1].value = temp; + if (arr[j].value > arr[j + 1].value) { + const temp = arr[j].value; + arr[j].value = arr[j + 1].value; + arr[j + 1].value = temp; + + snapshots.push(this.createSnapshot(arr)); } - array[j].state = 'unsorted'; - array[j + 1].state = 'unsorted'; + arr[j].state = 'unsorted'; + arr[j + 1].state = 'unsorted'; } - array[n - 1 - i].state = 'sorted'; // Mark the largest element as sorted + arr[n - 1 - i].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); } - array[0].state = 'sorted'; // Mark the last element as sorted (if n > 0) + + arr[0].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); + + return snapshots; } - async selectionSort(array: SortData[], animationSpeed: number): Promise { - const n = array.length; - for (let i = 0; i < n - 1; i++) { - let minIdx = i; - array[i].state = 'comparing'; - for (let j = i + 1; j < n; j++) { - array[j].state = 'comparing'; - await this.delay(animationSpeed); + // --- QUICK SORT --- + quickSort(array: SortData[]): SortSnapshot[] { + const snapshots: SortSnapshot[] = []; + const arr = array.map(item => ({ ...item })); + snapshots.push(this.createSnapshot(arr)); - if (array[j].value < array[minIdx].value) { - minIdx = j; - } - if (j !== minIdx) { // Only reset if it wasn't the minimum - array[j].state = 'unsorted'; - } - } - if (minIdx !== i) { - // Swap elements - const temp = array[i].value; - array[i].value = array[minIdx].value; - array[minIdx].value = temp; - } - array[i].state = 'sorted'; // Mark the current element as sorted - if (minIdx !== i) { - array[minIdx].state = 'unsorted'; - } - } - array[n - 1].state = 'sorted'; // Mark the last element as sorted + this.quickSortHelper(arr, 0, arr.length - 1, snapshots); + + arr.forEach(i => i.state = 'sorted'); + snapshots.push(this.createSnapshot(arr)); + + return snapshots; } - async insertionSort(array: SortData[], animationSpeed: number): Promise { - const n = array.length; - for (let i = 1; i < n; i++) { - const key = array[i].value; - array[i].state = 'comparing'; - let j = i - 1; + private quickSortHelper(arr: SortData[], low: number, high: number, snapshots: SortSnapshot[]) { + if (low < high) { + const pi = this.partition(arr, low, high, snapshots); - while (j >= 0 && array[j].value > key) { - array[j].state = 'comparing'; - await this.delay(animationSpeed); - - array[j + 1].value = array[j].value; - array[j + 1].state = 'unsorted'; // Reset after shifting - j = j - 1; - } - await this.delay(animationSpeed); // Delay after loop for final position - array[j + 1].value = key; - array[i].state = 'unsorted'; // Reset original 'key' position - array.forEach((item, idx) => { // Mark sorted up to i - if (idx <= i) { - item.state = 'sorted'; - } - }); + this.quickSortHelper(arr, low, pi - 1, snapshots); + this.quickSortHelper(arr, pi + 1, high, snapshots); + } else if (low >= 0 && high >= 0 && low === high) { + arr[low].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); } - array.forEach(item => item.state = 'sorted'); // Mark all as sorted at the end } - private delay(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); + private partition(arr: SortData[], low: number, high: number, snapshots: SortSnapshot[]): number { + const pivot = arr[high]; + arr[high].state = 'comparing'; // Pivot visualisieren + snapshots.push(this.createSnapshot(arr)); + + let i = (low - 1); + + for (let j = low; j <= high - 1; j++) { + arr[j].state = 'comparing'; + snapshots.push(this.createSnapshot(arr)); + + if (arr[j].value < pivot.value) { + i++; + this.swap(arr, i, j); + snapshots.push(this.createSnapshot(arr)); + } + arr[j].state = 'unsorted'; + } + this.swap(arr, i + 1, high); + + arr[high].state = 'unsorted'; + arr[i + 1].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); + + return i + 1; + } + + // --- HEAP SORT --- + heapSort(array: SortData[]): SortSnapshot[] { + const snapshots: SortSnapshot[] = []; + const arr = array.map(item => ({ ...item })); + const n = arr.length; + + snapshots.push(this.createSnapshot(arr)); + + for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { + this.heapify(arr, n, i, snapshots); + } + + for (let i = n - 1; i > 0; i--) { + arr[0].state = 'comparing'; + arr[i].state = 'comparing'; + snapshots.push(this.createSnapshot(arr)); + + this.swap(arr, 0, i); + + arr[0].state = 'unsorted'; + arr[i].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); + + this.heapify(arr, i, 0, snapshots); + } + arr[0].state = 'sorted'; + snapshots.push(this.createSnapshot(arr)); + + return snapshots; + } + + private heapify(arr: SortData[], n: number, i: number, snapshots: SortSnapshot[]) { + let largest = i; + const l = 2 * i + 1; + const r = 2 * i + 2; + + if (l < n) { + arr[l].state = 'comparing'; + arr[largest].state = 'comparing'; + snapshots.push(this.createSnapshot(arr)); + + if (arr[l].value > arr[largest].value) { + largest = l; + } + arr[l].state = 'unsorted'; + arr[largest].state = 'unsorted'; + } + + // Vergleich Rechts + if (r < n) { + arr[r].state = 'comparing'; + arr[largest].state = 'comparing'; + snapshots.push(this.createSnapshot(arr)); + + if (arr[r].value > arr[largest].value) { + largest = r; + } + arr[r].state = 'unsorted'; + arr[largest].state = 'unsorted'; + } + + if (largest !== i) { + this.swap(arr, i, largest); + snapshots.push(this.createSnapshot(arr)); + + this.heapify(arr, n, largest, snapshots); + } + } + + private swap(arr: SortData[], i: number, j: number) { + const temp = arr[i].value; + arr[i].value = arr[j].value; + arr[j].value = temp; } } diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html index 8133128..e9dc6d0 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.html +++ b/src/app/pages/algorithms/sorting/sorting.component.html @@ -7,20 +7,34 @@
{{ 'ALGORITHM.SORTING.ALGORITHM' | translate }} - + @for (algo of availableAlgorithms; track algo.value) { {{ algo.name }} } -
+
+ - -
diff --git a/src/app/pages/algorithms/sorting/sorting.component.scss b/src/app/pages/algorithms/sorting/sorting.component.scss index 128fcaf..b911eac 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.scss +++ b/src/app/pages/algorithms/sorting/sorting.component.scss @@ -35,7 +35,7 @@ .bar { flex-grow: 1; background-color: #424242; /* Default unsorted color */ - transition: height 0.1s ease-in-out, background-color 0.1s ease-in-out; + transition: height 0.05s ease-in-out, background-color 0.05s ease-in-out; width: 10px; /* Default width, flex-grow will adjust */ min-width: 1px; /* Ensure bars are always visible */ @@ -56,7 +56,7 @@ .info-panel { margin-top: 10px; font-size: 0.9em; - color: #555; + color: #FFFFFF; } } } diff --git a/src/app/pages/algorithms/sorting/sorting.component.ts b/src/app/pages/algorithms/sorting/sorting.component.ts index 43bd720..a247b31 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.ts +++ b/src/app/pages/algorithms/sorting/sorting.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import {ChangeDetectorRef, Component, OnInit} from '@angular/core'; import { CommonModule } from '@angular/common'; import {MatCardModule} from "@angular/material/card"; import {MatFormFieldModule} from "@angular/material/form-field"; @@ -7,86 +7,134 @@ import {MatButtonModule} from "@angular/material/button"; import {MatIconModule} from "@angular/material/icon"; import {TranslateModule} from "@ngx-translate/core"; import { SortingService } from './service/sorting.service'; -import { SortData } from './sorting.models'; +import {SortData, SortSnapshot} from './sorting.models'; import { FormsModule } from '@angular/forms'; - +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-sorting', standalone: true, - imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule], + imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule, MatInput], templateUrl: './sorting.component.html', styleUrls: ['./sorting.component.scss'] }) export class SortingComponent implements OnInit { + readonly MAX_ARRAY_SIZE: number = 200; + readonly MIN_ARRAY_SIZE: number = 20; + + private timeoutIds: any[] = []; sortArray: SortData[] = []; - arraySize: number = 50; + unsortedArrayCopy: SortData[] = []; + arraySize: number = 100; maxArrayValue: number = 100; - animationSpeed: number = 10; // Milliseconds per step + animationSpeed: number = 50; // Milliseconds per step // Placeholder for available sorting algorithms availableAlgorithms: { name: string; value: string }[] = [ { name: 'Bubble Sort', value: 'bubbleSort' }, - { name: 'Selection Sort', value: 'selectionSort' }, - { name: 'Insertion Sort', value: 'insertionSort' }, + { name: 'Quick Sort', value: 'quickSort' }, + { name: 'Heap Sort', value: 'heapSort' }, ]; selectedAlgorithm: string = this.availableAlgorithms[0].value; - isSorting: boolean = false; executionTime: number = 0; - constructor(private sortingService: SortingService) { } + constructor(private readonly sortingService: SortingService, private readonly cdr: ChangeDetectorRef) { } ngOnInit(): void { this.generateNewArray(); } + newArraySizeSet() + { + if (this.arraySize == this.sortArray.length) + { + return; + } + this.generateNewArray(); + } + generateNewArray(): void { - this.isSorting = false; + this.resetSorting(); this.executionTime = 0; + this.unsortedArrayCopy = []; this.sortArray = []; + for (let i = 0; i < this.arraySize; i++) { + const randomValue = Math.floor(Math.random() * this.maxArrayValue) + 1; this.sortArray.push({ - value: Math.floor(Math.random() * this.maxArrayValue) + 1, + value: randomValue, + state: 'unsorted' + }); + + this.unsortedArrayCopy.push({ + value: randomValue, state: 'unsorted' }); } } - async startSorting(): Promise { - if (this.isSorting) { - return; + private resetSortState() { + for (let i = 0; i < this.sortArray.length; i++) { + let element = this.sortArray[i]; + let unsortedElement = this.unsortedArrayCopy[i]; + element.value = unsortedElement.value; + element.state = 'unsorted'; } - console.log('Starting sorting...'); - this.isSorting = true; - this.executionTime = 0; + } - // Reset states for visualization - this.sortArray.forEach(item => item.state = 'unsorted'); + async startSorting(): Promise { + this.resetSorting(); const startTime = performance.now(); - console.log("Select algorithm ", this.selectedAlgorithm); + let snapshots: SortSnapshot[] = []; switch (this.selectedAlgorithm) { case 'bubbleSort': - await this.sortingService.bubbleSort(this.sortArray, this.animationSpeed); + snapshots = this.sortingService.bubbleSort(this.sortArray); break; - case 'selectionSort': - await this.sortingService.selectionSort(this.sortArray, this.animationSpeed); + case 'quickSort': + snapshots = this.sortingService.quickSort(this.sortArray); break; - case 'insertionSort': - await this.sortingService.insertionSort(this.sortArray, this.animationSpeed); - break; - default: - console.warn('Unknown sorting algorithm selected:', this.selectedAlgorithm); + case 'heapSort': + snapshots = this.sortingService.heapSort(this.sortArray); break; } - console.log("Done with sorting..."); + const endTime = performance.now(); - this.executionTime = Math.round(endTime - startTime); - this.isSorting = false; - this.sortArray.forEach(item => item.state = 'sorted'); // Mark all as sorted after completion + this.executionTime = parseFloat((endTime - startTime).toFixed(4)); + + console.log(snapshots.length); + this.animateSorting(snapshots); + } + + private animateSorting(snapshots: SortSnapshot[]): void { + snapshots.forEach((snapshot, index) => { + const id = setTimeout(() => { + for (let i = 0; i < this.sortArray.length; i++) { + if (snapshot.array[i]) { + this.sortArray[i].value = snapshot.array[i].value; + this.sortArray[i].state = snapshot.array[i].state; + } + } + + this.cdr.detectChanges(); + + if (index === snapshots.length - 1) { + this.sortArray.forEach(item => item.state = 'sorted'); + this.cdr.detectChanges(); + } + }, index * this.animationSpeed); + + this.timeoutIds.push(id); + }); + } + + private stopAnimations(): void { + this.timeoutIds.forEach(id => clearTimeout(id)); + this.timeoutIds = []; } resetSorting(): void { - this.generateNewArray(); + this.stopAnimations(); + this.resetSortState(); } } diff --git a/src/app/pages/algorithms/sorting/sorting.models.ts b/src/app/pages/algorithms/sorting/sorting.models.ts index 2741353..e4d8bce 100644 --- a/src/app/pages/algorithms/sorting/sorting.models.ts +++ b/src/app/pages/algorithms/sorting/sorting.models.ts @@ -2,3 +2,7 @@ export interface SortData { value: number; state: 'sorted' | 'comparing' | 'unsorted'; } + +export interface SortSnapshot { + array: SortData[]; +} diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 1d0e778..428a3e2 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -334,7 +334,8 @@ "START": "Sortierung starten", "RESET": "Zurücksetzen", "GENERATE_NEW_ARRAY": "Neues Array generieren", - "EXECUTION_TIME": "Ausführungszeit" + "EXECUTION_TIME": "Ausführungszeit", + "ARRAY_SIZE": "Anzahl der Balken" } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 15c429a..daaa808 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -334,7 +334,8 @@ "START": "Start Sorting", "RESET": "Reset", "GENERATE_NEW_ARRAY": "Generate New Array", - "EXECUTION_TIME": "Execution Time" + "EXECUTION_TIME": "Execution Time", + "ARRAY_SIZE": "Number of Bars" } } } From a10f62f2dd532e25edbcd75fcf1c30af7653099e Mon Sep 17 00:00:00 2001 From: Lobo Date: Wed, 4 Feb 2026 12:17:54 +0100 Subject: [PATCH 4/5] Refactor pathfinding/sorting UI and update i18n Replace plain container with Angular Material mat-card in pathfinding view and reorganize UI: add mat-card header/content, algorithm explanation block, control buttons, node-type toggle group, grid-size inputs, legend and results display (path length & execution time). Import and wire MatCard modules in the pathfinding component. Move .algo-info styling from the component SCSS into global styles.scss and remove it from the component stylesheet. Update sorting template to use new SORTING translation keys and add an UrlConstants reference in the sorting component TS for external links. Add/adjust i18n keys in en.json and de.json for sorting and grid labels to match the updated templates. --- .../pathfinding/pathfinding.component.html | 152 +++++++++--------- .../pathfinding/pathfinding.component.scss | 19 --- .../pathfinding/pathfinding.component.ts | 7 +- .../algorithms/sorting/sorting.component.html | 31 +++- .../algorithms/sorting/sorting.component.ts | 3 + src/assets/i18n/de.json | 28 ++-- src/assets/i18n/en.json | 25 +-- src/styles.scss | 20 +++ 8 files changed, 160 insertions(+), 125 deletions(-) diff --git a/src/app/pages/algorithms/pathfinding/pathfinding.component.html b/src/app/pages/algorithms/pathfinding/pathfinding.component.html index d457fc3..931dcf3 100644 --- a/src/app/pages/algorithms/pathfinding/pathfinding.component.html +++ b/src/app/pages/algorithms/pathfinding/pathfinding.component.html @@ -1,87 +1,89 @@ -
-

{{ 'PATHFINDING.TITLE' | translate }}

+ + + {{ 'PATHFINDING.TITLE' | translate }} + + +
+

{{ 'PATHFINDING.EXPLANATION.TITLE' | translate }}

-
-

{{ 'PATHFINDING.EXPLANATION.TITLE' | translate }}

+

+ Dijkstra {{ 'PATHFINDING.EXPLANATION.DIJKSTRA_EXPLANATION' | translate }} + Wikipedia +

-

- Dijkstra {{ 'PATHFINDING.EXPLANATION.DIJKSTRA_EXPLANATION' | translate }} - Wikipedia -

+

+ A* {{ 'PATHFINDING.EXPLANATION.ASTAR_EXPLANATION' | translate}} + Wikipedia +

-

- A* {{ 'PATHFINDING.EXPLANATION.ASTAR_EXPLANATION' | translate}} - Wikipedia -

- -

- {{ 'PATHFINDING.EXPLANATION.NOTE' | translate}} {{ 'PATHFINDING.EXPLANATION.DISCLAIMER' | translate}} -

-
- -
-
- - -
-
- - - +

+ {{ 'PATHFINDING.EXPLANATION.NOTE' | translate}} {{ 'PATHFINDING.EXPLANATION.DISCLAIMER' | translate}} +

-
- - {{ 'PATHFINDING.START_NODE' | translate }} - {{ 'PATHFINDING.END_NODE' | translate }} - {{ 'PATHFINDING.WALL' | translate }} - {{ 'PATHFINDING.CLEAR_NODE' | translate }} - -
+
+
+ + +
+
+ + + +
-
-
- - {{ 'ALGORITHM.PATHFINDING.GRID_HEIGHT' | translate }} - - +
+ + {{ 'PATHFINDING.START_NODE' | translate }} + {{ 'PATHFINDING.END_NODE' | translate }} + {{ 'PATHFINDING.WALL' | translate }} + {{ 'PATHFINDING.CLEAR_NODE' | translate }} + +
- - {{ 'ALGORITHM.PATHFINDING.GRID_WIDTH' | translate }} - - +
+
+ + {{ 'PATHFINDING.GRID_HEIGHT' | translate }} + + + + + {{ 'PATHFINDING.GRID_WIDTH' | translate }} + + +
+
+ +
+ {{ 'PATHFINDING.START_NODE' | translate }} + {{ 'PATHFINDING.END_NODE' | translate }} + {{ 'PATHFINDING.WALL' | translate }} + {{ 'PATHFINDING.VISITED' | translate }} + {{ 'PATHFINDING.PATH' | translate }}
-
- {{ 'PATHFINDING.START_NODE' | translate }} - {{ 'PATHFINDING.END_NODE' | translate }} - {{ 'PATHFINDING.WALL' | translate }} - {{ 'PATHFINDING.VISITED' | translate }} - {{ 'PATHFINDING.PATH' | translate }} +
+

{{ 'PATHFINDING.PATH_LENGTH' | translate }}: {{ pathLength }}

+

{{ 'PATHFINDING.EXECUTION_TIME' | translate }}: {{ executionTime | number:'1.2-2' }} ms

-
-
-

{{ 'PATHFINDING.PATH_LENGTH' | translate }}: {{ pathLength }}

-

{{ 'PATHFINDING.EXECUTION_TIME' | translate }}: {{ executionTime | number:'1.2-2' }} ms

-
- - -
+ + diff --git a/src/app/pages/algorithms/pathfinding/pathfinding.component.scss b/src/app/pages/algorithms/pathfinding/pathfinding.component.scss index 070efd4..3e30d18 100644 --- a/src/app/pages/algorithms/pathfinding/pathfinding.component.scss +++ b/src/app/pages/algorithms/pathfinding/pathfinding.component.scss @@ -2,25 +2,6 @@ padding: 2rem; } -.algo-info { - margin: 0 0 1rem 0; - padding: 0.75rem 1rem; - border: 1px solid #ddd; - border-radius: 8px; - - h3 { - margin: 0 0 0.5rem 0; - } - - p { - margin: 0.5rem 0; - } - - a { - margin-left: 0.25rem; - } -} - .controls-container { display: flex; flex-direction: column; diff --git a/src/app/pages/algorithms/pathfinding/pathfinding.component.ts b/src/app/pages/algorithms/pathfinding/pathfinding.component.ts index a50c270..c6ed385 100644 --- a/src/app/pages/algorithms/pathfinding/pathfinding.component.ts +++ b/src/app/pages/algorithms/pathfinding/pathfinding.component.ts @@ -12,6 +12,7 @@ import {TranslateModule, TranslateService} from '@ngx-translate/core'; import {DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS, MAX_GRID_PX, MAX_GRID_SIZE, MIN_GRID_SIZE, Node} from './pathfinding.models'; import {PathfindingService} from './service/pathfinding.service'; import {UrlConstants} from '../../../constants/UrlConstants'; +import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card'; enum NodeType { Start = 'start', @@ -32,7 +33,11 @@ interface GridPos { row: number; col: number } MatButtonToggleModule, MatFormFieldModule, MatInputModule, - TranslateModule + TranslateModule, + MatCard, + MatCardHeader, + MatCardTitle, + MatCardContent ], templateUrl: './pathfinding.component.html', styleUrls: ['./pathfinding.component.scss'] diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html index e9dc6d0..e6db2a1 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.html +++ b/src/app/pages/algorithms/sorting/sorting.component.html @@ -1,12 +1,29 @@
- {{ 'ALGORITHM.SORTING.TITLE' | translate }} + {{ 'SORTING.TITLE' | translate }} +
+

{{ 'SORTING.EXPLANATION.TITLE' | translate }}

+ +

+ Dijkstra {{ 'SORTING.EXPLANATION.DIJKSTRA_EXPLANATION' | translate }} + Wikipedia +

+ +

+ A* {{ 'SORTING.EXPLANATION.ASTAR_EXPLANATION' | translate}} + Wikipedia +

+ +

+ {{ 'SORTING.EXPLANATION.NOTE' | translate}} {{ 'SORTING.EXPLANATION.DISCLAIMER' | translate}} +

+
- {{ 'ALGORITHM.SORTING.ALGORITHM' | translate }} + {{ 'SORTING.ALGORITHM' | translate }} @for (algo of availableAlgorithms; track algo.value) { {{ algo.name }} @@ -15,7 +32,7 @@ - {{ 'ALGORITHM.SORTING.ARRAY_SIZE' | translate }} + {{ 'SORTING.ARRAY_SIZE' | translate }}
-

{{ 'ALGORITHM.SORTING.EXECUTION_TIME' | translate }}: {{ executionTime }} ms

+

{{ 'SORTING.EXECUTION_TIME' | translate }}: {{ executionTime }} ms

@for (item of sortArray; track $index) { diff --git a/src/app/pages/algorithms/sorting/sorting.component.ts b/src/app/pages/algorithms/sorting/sorting.component.ts index a247b31..8cbca5d 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.ts +++ b/src/app/pages/algorithms/sorting/sorting.component.ts @@ -10,6 +10,7 @@ import { SortingService } from './service/sorting.service'; import {SortData, SortSnapshot} from './sorting.models'; import { FormsModule } from '@angular/forms'; import {MatInput} from '@angular/material/input'; +import {UrlConstants} from '../../../constants/UrlConstants'; @Component({ selector: 'app-sorting', standalone: true, @@ -137,4 +138,6 @@ export class SortingComponent implements OnInit { this.stopAnimations(); this.resetSortState(); } + + protected readonly UrlConstants = UrlConstants; } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 428a3e2..09fc1a3 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -317,25 +317,29 @@ }, "ALERT": { "START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten." - } + }, + "GRID_HEIGHT": "Höhe", + "GRID_WIDTH": "Beite" + }, + "SORTING": { + "TITLE": "Sortieralgorithmen", + "ALGORITHM": "Algorithmen", + "START": "Sortierung starten", + "RESET": "Zurücksetzen", + "GENERATE_NEW_ARRAY": "Neues Array generieren", + "EXECUTION_TIME": "Ausführungszeit", + "ARRAY_SIZE": "Anzahl der Balken" }, "ALGORITHM": { "TITLE": "Algorithmen", "PATHFINDING": { "TITLE": "Wegfindung", - "DESCRIPTION": "Vergleich von Dijkstra vs. A*.", - "GRID_HEIGHT": "Höhe", - "GRID_WIDTH": "Beite" + "DESCRIPTION": "Vergleich von Dijkstra vs. A*." }, "SORTING": { - "TITLE": "Sortieralgorithmen", - "DESCRIPTION": "Visualisierung verschiedener Sortieralgorithmen.", - "ALGORITHM": "Algorithmus", - "START": "Sortierung starten", - "RESET": "Zurücksetzen", - "GENERATE_NEW_ARRAY": "Neues Array generieren", - "EXECUTION_TIME": "Ausführungszeit", - "ARRAY_SIZE": "Anzahl der Balken" + "TITLE": "Sortierung", + "DESCRIPTION": "Visualisierung verschiedener Sortieralgorithmen." + } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index daaa808..1a95e00 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -317,25 +317,28 @@ }, "ALERT": { "START_END_NODES": "Please select a start and end node before running the algorithm." - } + }, + "GRID_HEIGHT": "Height", + "GRID_WIDTH": "Width" + }, + "SORTING": { + "TITLE": "Sorting Algorithms", + "ALGORITHM": "Algorithm", + "START": "Start Sorting", + "RESET": "Reset", + "GENERATE_NEW_ARRAY": "Generate New Array", + "EXECUTION_TIME": "Execution Time", + "ARRAY_SIZE": "Number of Bars" }, "ALGORITHM": { "TITLE": "Algorithms", "PATHFINDING": { "TITLE": "Pathfinding", - "DESCRIPTION": "Comparing of Dijkstra vs. A*.", - "GRID_HEIGHT": "Height", - "GRID_WIDTH": "Width" + "DESCRIPTION": "Comparing of Dijkstra vs. A*." }, "SORTING": { - "TITLE": "Sorting Algorithms", + "TITLE": "Sorting", "DESCRIPTION": "Visualizing various sorting algorithms.", - "ALGORITHM": "Algorithm", - "START": "Start Sorting", - "RESET": "Reset", - "GENERATE_NEW_ARRAY": "Generate New Array", - "EXECUTION_TIME": "Execution Time", - "ARRAY_SIZE": "Number of Bars" } } } diff --git a/src/styles.scss b/src/styles.scss index dc63ed5..3a12a96 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -211,3 +211,23 @@ a { height: 18px; width: 18px; } + +// algos +.algo-info { + margin: 0 0 1rem 0; + padding: 0.75rem 1rem; + border: 1px solid #ddd; + border-radius: 8px; + + h3 { + margin: 0 0 0.5rem 0; + } + + p { + margin: 0.5rem 0; + } + + a { + margin-left: 0.25rem; + } +} From beb5bb7db14e95993767364863d3ed7dc373090b Mon Sep 17 00:00:00 2001 From: LoboTheDark Date: Wed, 4 Feb 2026 14:19:27 +0100 Subject: [PATCH 5/5] Add sorting explanations, wiki links, and i18n Introduce Bubble, Quick and Heap Sort documentation: add wiki URL constants, update sorting component to show Bubble/Quick/Heap Sort explanations with Wikipedia links, and include additional disclaimer text and list in the UI. Add corresponding i18n entries in English and German containing algorithm descriptions, note/title and several disclaimer lines. --- src/app/constants/UrlConstants.ts | 3 +++ .../algorithms/sorting/sorting.component.html | 21 +++++++++++++++---- src/assets/i18n/de.json | 14 ++++++++++++- src/assets/i18n/en.json | 16 ++++++++++++-- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/app/constants/UrlConstants.ts b/src/app/constants/UrlConstants.ts index a730a49..719b7f1 100644 --- a/src/app/constants/UrlConstants.ts +++ b/src/app/constants/UrlConstants.ts @@ -3,4 +3,7 @@ static readonly GIT_HUB = 'https://github.com/LoboTheDark'; static readonly DIJKSTRA_WIKI = 'https://de.wikipedia.org/wiki/Dijkstra-Algorithmus' static readonly ASTAR_WIKI = 'https://de.wikipedia.org/wiki/A*-Algorithmus' + static readonly BUBBLE_SORT_WIKI = 'https://de.wikipedia.org/wiki/Bubblesort' + static readonly QUICK_SORT_WIKI = 'https://de.wikipedia.org/wiki/Quicksort' + static readonly HEAP_SORT_WIKI = 'https://de.wikipedia.org/wiki/Heapsort' } diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html index e6db2a1..7880ace 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.html +++ b/src/app/pages/algorithms/sorting/sorting.component.html @@ -8,18 +8,31 @@

{{ 'SORTING.EXPLANATION.TITLE' | translate }}

- Dijkstra {{ 'SORTING.EXPLANATION.DIJKSTRA_EXPLANATION' | translate }} - Wikipedia + Bubble Sort {{ 'SORTING.EXPLANATION.BUBBLE_SORT_EXPLANATION' | translate }} + Wikipedia

- A* {{ 'SORTING.EXPLANATION.ASTAR_EXPLANATION' | translate}} - Wikipedia + Quick Sort {{ 'SORTING.EXPLANATION.QUICK_SORT_EXPLANATION' | translate}} + Wikipedia +

+ +

+ Heap Sort {{ 'SORTING.EXPLANATION.HEAP_SORT_EXPLANATION' | translate}} + Wikipedia

{{ 'SORTING.EXPLANATION.NOTE' | translate}} {{ 'SORTING.EXPLANATION.DISCLAIMER' | translate}}

+
    +
  • {{ 'SORTING.EXPLANATION.DISCLAIMER_1' | translate}}
  • +
  • {{ 'SORTING.EXPLANATION.DISCLAIMER_2' | translate}}
  • +
  • {{ 'SORTING.EXPLANATION.DISCLAIMER_3' | translate}}
  • +
+

+ {{ 'SORTING.EXPLANATION.DISCLAIMER_4' | translate}} +

diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 09fc1a3..a50598d 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -328,7 +328,19 @@ "RESET": "Zurücksetzen", "GENERATE_NEW_ARRAY": "Neues Array generieren", "EXECUTION_TIME": "Ausführungszeit", - "ARRAY_SIZE": "Anzahl der Balken" + "ARRAY_SIZE": "Anzahl der Balken", + "EXPLANATION": { + "TITLE": "Algorithmen", + "BUBBLE_SORT_EXPLANATION":"vergleicht wiederholt benachbarte Elemente und tauscht sie, wenn sie in der falschen Reihenfolge stehen. Das größte Element \"blubbert\" dabei wie eine Luftblase ans Ende der Liste. Vorteil: Extrem einfach zu verstehen und zu implementieren; erkennt bereits sortierte Listen sehr schnell. Nachteil: Sehr ineffizient bei großen Listen (Laufzeit O(n²)). In der Praxis kaum genutzt.", + "QUICK_SORT_EXPLANATION": "folgt dem \"Teile und Herrsche\"-Prinzip. Ein \"Pivot\"-Element wird gewählt, und das Array wird in zwei Hälften geteilt: Elemente kleiner als das Pivot und Elemente größer als das Pivot. Vorteil: Im Durchschnitt einer der schnellsten Sortieralgorithmen (O(n log n)); benötigt keinen zusätzlichen Speicher (In-Place). Nachteil: Im schlechtesten Fall (Worst Case) langsam (O(n²)), wenn das Pivot ungünstig gewählt wird. Ist nicht stabil (ändert Reihenfolge gleicher Elemente).", + "HEAP_SORT_EXPLANATION": "organisiert die Daten zunächst in einer speziellen Baumstruktur (Binary Heap). Das größte Element (die Wurzel) wird entnommen und ans Ende sortiert, dann wird der Baum repariert. Vorteil: Garantiert eine schnelle Laufzeit von O(n log n), selbst im schlechtesten Fall. Benötigt fast keinen zusätzlichen Speicher. Nachteil: In der Praxis oft etwas langsamer als Quick Sort, da die Sprünge im Speicher (Heap-Struktur) den CPU-Cache schlechter nutzen.", + "NOTE": "HINWEIS", + "DISCLAIMER": "Die Wahl des \"besten\" Sortieralgorithmus hängt stark von den Daten und den Rahmenbedingungen ab. In der Informatik betrachtet man oft drei Szenarien:", + "DISCLAIMER_1": "Best Case: Die Daten sind schon fast sortiert (hier glänzt z.B. Bubble Sort).", + "DISCLAIMER_2": "Average Case: Der statistische Normalfall.", + "DISCLAIMER_3": "Worst Case: Die Daten sind maximal ungünstig angeordnet (hier bricht Quick Sort ohne Optimierung ein, während Heap Sort stabil bleibt).", + "DISCLAIMER_4": "Zusätzlich gibt es fast immer einen Time-Space Trade-off (Zeit-Speicher-Kompromiss): Algorithmen, die extrem schnell sind (wie Merge Sort), benötigen oft viel zusätzlichen Arbeitsspeicher. Algorithmen, die direkt im vorhandenen Speicher arbeiten (wie Heap Sort), sparen Platz, sind aber manchmal komplexer oder minimal langsamer. Es gibt also keine \"One-Size-Fits-All\"-Lösung." + } }, "ALGORITHM": { "TITLE": "Algorithmen", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 1a95e00..7e754c2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -328,7 +328,19 @@ "RESET": "Reset", "GENERATE_NEW_ARRAY": "Generate New Array", "EXECUTION_TIME": "Execution Time", - "ARRAY_SIZE": "Number of Bars" + "ARRAY_SIZE": "Number of Bars", + "EXPLANATION": { + "TITLE": "Algorithms", + "BUBBLE_SORT_EXPLANATION": "repeatedly compares adjacent elements and swaps them if they are in the wrong order. The largest element \"bubbles\" to the end of the list like an air bubble. Advantage: Extremely simple to understand and implement; detects already sorted lists very quickly. Disadvantage: Very inefficient for large lists (runtime O(n²)). Rarely used in practice.", + "QUICK_SORT_EXPLANATION": "follows the \"divide and conquer\" principle. A \"pivot\" element is selected, and the array is divided into two halves: elements smaller than the pivot and elements larger than the pivot. Advantage: On average one of the fastest sorting algorithms (O(n log n)); requires no additional memory (in-place). Disadvantage: Slow in the worst case (O(n²)) if the pivot is chosen poorly. Is not stable (changes order of equal elements).", + "HEAP_SORT_EXPLANATION": "organizes the data initially into a special tree structure (Binary Heap). The largest element (the root) is extracted and sorted to the end, then the tree is repaired. Advantage: Guarantees a fast runtime of O(n log n), even in the worst case. Requires almost no additional memory. Disadvantage: Often slightly slower than Quick Sort in practice because the jumps in memory (heap structure) utilize the CPU cache less effectively.", + "NOTE": "NOTE", + "DISCLAIMER": "The choice of the \"best\" sorting algorithm depends heavily on the data and the constraints. In computer science, three scenarios are often considered:", + "DISCLAIMER_1": "Best Case: The data is already nearly sorted (Bubble Sort shines here, for example).", + "DISCLAIMER_2": "Average Case: The statistical norm.", + "DISCLAIMER_3": "Worst Case: The data is arranged in the most unfavorable way possible (Quick Sort performs poorly here without optimization, while Heap Sort remains stable).", + "DISCLAIMER_4": "Additionally, there is almost always a Time-Space Trade-off: Algorithms that are extremely fast (like Merge Sort) often require a lot of additional working memory. Algorithms that work directly in existing memory (like Heap Sort) save space but are sometimes more complex or slightly slower. Thus, there is no \"one-size-fits-all\" solution." + } }, "ALGORITHM": { "TITLE": "Algorithms", @@ -338,7 +350,7 @@ }, "SORTING": { "TITLE": "Sorting", - "DESCRIPTION": "Visualizing various sorting algorithms.", + "DESCRIPTION": "Visualizing various sorting algorithms." } } }